diff --git a/CODING_STYLE b/CODING_STYLE new file mode 100644 index 0000000..d46cfa5 --- /dev/null +++ b/CODING_STYLE @@ -0,0 +1,107 @@ +QEMU Coding Style +================= + +Please use the script checkpatch.pl in the scripts directory to check +patches before submitting. + +1. Whitespace + +Of course, the most important aspect in any coding style is whitespace. +Crusty old coders who have trouble spotting the glasses on their noses +can tell the difference between a tab and eight spaces from a distance +of approximately fifteen parsecs. Many a flamewar have been fought and +lost on this issue. + +QEMU indents are four spaces. Tabs are never used, except in Makefiles +where they have been irreversibly coded into the syntax. +Spaces of course are superior to tabs because: + + - You have just one way to specify whitespace, not two. Ambiguity breeds + mistakes. + - The confusion surrounding 'use tabs to indent, spaces to justify' is gone. + - Tab indents push your code to the right, making your screen seriously + unbalanced. + - Tabs will be rendered incorrectly on editors who are misconfigured not + to use tab stops of eight positions. + - Tabs are rendered badly in patches, causing off-by-one errors in almost + every line. + - It is the QEMU coding style. + +Do not leave whitespace dangling off the ends of lines. + +2. Line width + +Lines are 80 characters; not longer. + +Rationale: + - Some people like to tile their 24" screens with a 6x4 matrix of 80x24 + xterms and use vi in all of them. The best way to punish them is to + let them keep doing it. + - Code and especially patches is much more readable if limited to a sane + line length. Eighty is traditional. + - It is the QEMU coding style. + +3. Naming + +Variables are lower_case_with_underscores; easy to type and read. Structured +type names are in CamelCase; harder to type but standing out. Enum type +names and function type names should also be in CamelCase. Scalar type +names are lower_case_with_underscores_ending_with_a_t, like the POSIX +uint64_t and family. Note that this last convention contradicts POSIX +and is therefore likely to be changed. + +When wrapping standard library functions, use the prefix qemu_ to alert +readers that they are seeing a wrapped version; otherwise avoid this prefix. + +4. Block structure + +Every indented statement is braced; even if the block contains just one +statement. The opening brace is on the line that contains the control +flow statement that introduces the new block; the closing brace is on the +same line as the else keyword, or on a line by itself if there is no else +keyword. Example: + + if (a == 5) { + printf("a was 5.\n"); + } else if (a == 6) { + printf("a was 6.\n"); + } else { + printf("a was something else entirely.\n"); + } + +Note that 'else if' is considered a single statement; otherwise a long if/ +else if/else if/.../else sequence would need an indent for every else +statement. + +An exception is the opening brace for a function; for reasons of tradition +and clarity it comes on a line by itself: + + void a_function(void) + { + do_something(); + } + +Rationale: a consistent (except for functions...) bracing style reduces +ambiguity and avoids needless churn when lines are added or removed. +Furthermore, it is the QEMU coding style. + +5. Declarations + +Mixed declarations (interleaving statements and declarations within blocks) +are not allowed; declarations should be at the beginning of blocks. In other +words, the code should not generate warnings if using GCC's +-Wdeclaration-after-statement option. + +6. Conditional statements + +When comparing a variable for (in)equality with a constant, list the +constant on the right, as in: + +if (a == 1) { + /* Reads like: "If a equals 1" */ + do_something(); +} + +Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read. +Besides, good compilers already warn users when '==' is mis-typed as '=', +even when the constant is on the right. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..00ccfbb --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..48afc2e --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..1249b8a --- /dev/null +++ b/Changelog @@ -0,0 +1,580 @@ +This file documents changes for QEMU releases 0.12 and earlier. +For changelog information for later releases, see +http://wiki.qemu-project.org/ChangeLog or look at the git history for +more detailed information. + + +version 0.12.0: + + - Update to SeaBIOS 0.5.0 + - e1000: fix device link status in Linux (Anthony Liguori) + - monitor: fix QMP for balloon command (Luiz Capitulino) + - QMP: Return an empty dict by default (Luiz Capitulino) + - QMP: Only handle converted commands (Luiz Capitulino) + - pci: support PCI based option rom loading (Gerd Hoffman/Anthony Liguori) + - Fix backcompat for hotplug of SCSI controllers (Daniel P. Berrange) + - fdc: fix migration from 0.11 (Juan Quintela) + - vmware-vga: fix segv on cursor resize. (Dave Airlie) + - vmware-vga: various fixes (Dave Airlie/Anthony Liguori) + - qdev: improve property error reporting. (Gerd Hoffmann) + - fix vga names in default_list (Gerd Hoffmann) + - usb-host: check mon before using it. (Gerd Hoffmann) + - usb-net: use qdev for -usbdevice (Gerd Hoffmann) + - monitor: Catch printing to non-existent monitor (Luiz Capitulino) + - Avoid permanently disabled QEMU monitor when UNIX migration fails (Daniel P. Berrange) + - Fix loading of ELF multiboot kernels (Kevin Wolf) + - qemu-io: Fix memory leak (Kevin Wolf) + - Fix thinko in linuxboot.S (Paolo Bonzini) + - target-i386: Fix evaluation of DR7 register (Jan Kiszka) + - vnc: hextile: do not generate ForegroundSpecified and SubrectsColoured tiles (Anthony Liguori) + - S390: Bail out without KVM (Alexander Graf) + - S390: Don't tell guest we're updating config space (Alexander Graf) + - target-s390: Fail on unknown instructions (Alexander Graf) + - osdep: Fix runtime failure on older Linux kernels (Andre Przywara) + - Fix a make -j race (Juergen Lock) + - target-alpha: Fix generic ctz64. (Richard Henderson) + - s390: Fix buggy assignment (Stefan Weil) + - target-mips: fix user-mode emulation startup (Nathan Froyd) + - target-i386: Update CPUID feature set for TCG (Andre Przywara) + - s390: fix build on 32 bit host (Michael S. Tsirkin) + +version 0.12.0-rc2: + + - v2: properly save kvm system time msr registers (Glauber Costa) + - convert more monitor commands to qmp (Luiz Capitulino) + - vnc: fix capslock tracking logic. (Gerd Hoffmann) + - QemuOpts: allow larger option values. (Gerd Hoffmann) + - scsi: fix drive hotplug. (Gerd Hoffmann) + - pci: don't hw_error() when no slot is available. (Gerd Hoffmann) + - pci: don't abort() when trying to hotplug with acpi off. (Gerd Hoffmann) + - allow default devices to be implemented in config file (Gerd Hoffman) + - vc: colorize chardev title line with blue background. (Gerd Hoffmann) + - chardev: make chardevs specified in config file work. (Gerd Hoffmann) + - qdev: also match bus name for global properties (Gerd Hoffmann) + - qdev: add command line option to set global defaults for properties. (Gerd Hoffmann) + - kvm: x86: Save/restore exception_index (Jan Kiszka) + - qdev: Replace device names containing whitespace (Markus Armbruster) + - fix rtc-td-hack on host without high-res timers (Gleb Natapov) + - virtio: verify features on load (Michael S. Tsirkin) + - vmware_vga: add rom file so that it boots. (Dave Airlie) + - Do not abort on qemu_malloc(0) in production builds (Anthony Liguori) + - Fix ARM userspace strex implementation. (Paul Brook) + - qemu: delete rule target on error (Michael S. Tsirkin) + - QMP: add human-readable description to error response (Markus Armbruster) + - convert more monitor commands to QError (Markus Armbruster) + - monitor: Fix double-prompt after "change vnc passwd BLA" (Markus Armbruster) + - monitor: do_cont(): Don't ask for passwords (Luiz Capitulino) + - monitor: Introduce 'block_passwd' command (Luiz Capitulino) + - pci: interrupt disable bit support (Michael S. Tsirkin) + - pci: interrupt status bit implementation (Michael S. Tsirkin) + - pci: prepare irq code for interrupt state (Michael S. Tsirkin) + - msix: function mask support (Michael S. Tsirkin) + - msix: macro rename for function mask support (Michael S. Tsirkin) + - cpuid: Fix multicore setup on Intel (Andre Przywara) + - kvm: x86: Fix initial kvm_has_msr_star (Jan Kiszka) + - Update OpenBIOS images to r640 (Aurelien Jarno) + +version 0.10.2: + + - fix savevm/loadvm (Anthony Liguori) + - live migration: fix dirty tracking windows (Glauber Costa) + - live migration: improve error propagation (Glauber Costa) + - qcow2: fix image creation for > ~2TB images (Chris Wright) + - hotplug: fix error handling for if= parameter (Eduardo Habkost) + - qcow2: fix data corruption (Nolan Leake) + - virtio: fix guest oops with 2.6.25 kernels (Rusty Russell) + - SH4: add support for -kernel (Takashi Yoshii, Aurelien Jarno) + - hotplug: fix closing of char devices (Jan Kiszka) + - hotplug: remove incorrect check for device name (Eduardo Habkost) + - enable -k on win32 (Herve Poussineau) + - configure: use LANG=C for grep (Andreas Faerber) + - fix VGA regression (malc) + +version 0.10.1: + + - virtio-net: check right return size on sg list (Alex Williamson) + - Make qemu_announce_self handle holes (live migration after hotplug) + (Marcelo Tosatti) + - Revert r6804-r6808 (qcow2 allocation info). This series of changes added + a high cost to startup for large qcow2 images (Anthony Liguori) + - qemu-img: fix help message (Aurelien Jarno) + - Fix build for non-default installs of SDL (Anthony Liguori) + - Fix race condition in env->interrupt_request. When using TCG and a dynticks + host timer, this condition could cause TCG to get stuck in an infinite + loop (Aurelien Jarno) + - Fix reading encrypted hard disk passwords during early startup (Jan Kiszka) + - Fix encrypted disk reporting in 'info block' (Jan Kiszka) + - Fix console size with tiny displays (MusicPal) (Jan Kiszka) + - Improve error handling in bdrv_open2 (Jan Kiszka) + - Avoid leaking data in mux'ed character devices (Jan Kiszka) + - Fix initial character device reset (no banner in monitor) (Jan Kiszka) + - Fix cpuid KVM crash on i386 host (Lubomir Rintel) + - Fix SLES10sp2 installation by adding ISTAT1 register to LSI SCSI emulation + (Ryan Harper) + +version 0.10.0: + + - TCG support (No longer requires GCC 3.x) + - Kernel Virtual Machine acceleration support + - BSD userspace emulation + - Bluetooth emulation and host passthrough support + - GDB XML register description support + - Intel e1000 emulation + - HPET emulation + - VirtIO paravirtual device support + - Marvell 88w8618 / MusicPal emulation + - Nokia N-series tablet emulation / OMAP2 processor emulation + - PCI hotplug support + - Live migration and new save/restore formats + - Curses display support + - qemu-nbd utility to mount supported block formats + - Altivec support in PPC emulation and new firmware (OpenBIOS) + - Multiple VNC clients are now supported + - TLS encryption is now supported in VNC + - MIPS Magnum R4000 machine (Hervé Poussineau) + - Braille support (Samuel Thibault) + - Freecom MusicPal system emulation (Jan Kiszka) + - OMAP242x and Nokia N800, N810 machines (Andrzej Zaborowski) + - EsounD audio driver (Frederick Reeve) + - Gravis Ultrasound GF1 sound card (Tibor "TS" Schütz) + - Many, many, bug fixes and new features + +version 0.9.1: + + - TFTP booting from host directory (Anthony Liguori, Erwan Velu) + - Tap device emulation for Solaris (Sittichai Palanisong) + - Monitor multiplexing to several I/O channels (Jason Wessel) + - ds1225y nvram support (Herve Poussineau) + - CPU model selection support (J. Mayer, Paul Brook, Herve Poussineau) + - Several Sparc fixes (Aurelien Jarno, Blue Swirl, Robert Reif) + - MIPS 64-bit FPU support (Thiemo Seufer) + - Xscale PDA emulation (Andrzej Zaborowski) + - ColdFire system emulation (Paul Brook) + - Improved SH4 support (Magnus Damm) + - MIPS64 support (Aurelien Jarno, Thiemo Seufer) + - Preliminary Alpha guest support (J. Mayer) + - Read-only support for Parallels disk images (Alex Beregszaszi) + - SVM (x86 virtualization) support (Alexander Graf) + - CRIS emulation (Edgar E. Iglesias) + - SPARC32PLUS execution support (Blue Swirl) + - MIPS mipssim pseudo machine (Thiemo Seufer) + - Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh) + - OMAP310 MPU emulation plus Palm T|E machine (Andrzej Zaborowski) + - ARM v6, v7, NEON SIMD and SMP emulation (Paul Brook/CodeSourcery) + - Gumstix boards: connex and verdex emulation (Thorsten Zitterell) + - Intel mainstone II board emulation (Armin Kuster) + - VMware SVGA II graphics card support (Andrzej Zaborowski) + +version 0.9.0: + + - Support for relative paths in backing files for disk images + - Async file I/O API + - New qcow2 disk image format + - Support of multiple VM snapshots + - Linux: specific host CDROM and floppy support + - SMM support + - Moved PCI init, MP table init and ACPI table init to Bochs BIOS + - Support for MIPS32 Release 2 instruction set (Thiemo Seufer) + - MIPS Malta system emulation (Aurelien Jarno, Stefan Weil) + - Darwin userspace emulation (Pierre d'Herbemont) + - m68k user support (Paul Brook) + - several x86 and x86_64 emulation fixes + - Mouse relative offset VNC extension (Anthony Liguori) + - PXE boot support (Anthony Liguori) + - '-daemonize' option (Anthony Liguori) + +version 0.8.2: + + - ACPI support + - PC VGA BIOS fixes + - switch to OpenBios for SPARC targets (Blue Swirl) + - VNC server fixes + - MIPS FPU support (Marius Groeger) + - Solaris/SPARC host support (Juergen Keil) + - PPC breakpoints and single stepping (Jason Wessel) + - USB updates (Paul Brook) + - UDP/TCP/telnet character devices (Jason Wessel) + - Windows sparse file support (Frediano Ziglio) + - RTL8139 NIC TCP segmentation offloading (Igor Kovalenko) + - PCNET NIC support (Antony T Curtis) + - Support for variable frequency host CPUs + - Workaround for win32 SMP hosts + - Support for AMD Flash memories (Jocelyn Mayer) + - Audio capture to WAV files support (malc) + +version 0.8.1: + + - USB tablet support (Brad Campbell, Anthony Liguori) + - win32 host serial support (Kazu) + - PC speaker support (Joachim Henke) + - IDE LBA48 support (Jens Axboe) + - SSE3 support + - Solaris port (Juergen Keil) + - Preliminary SH4 target (Samuel Tardieu) + - VNC server (Anthony Liguori) + - slirp fixes (Ed Swierk et al.) + - USB fixes + - ARM Versatile Platform Baseboard emulation (Paul Brook) + +version 0.8.0: + + - ARM system emulation: Arm Integrator/CP board with an arm1026ej-s + cpu (Paul Brook) + - SMP support + - Mac OS X cocoa improvements (Mike Kronenberg) + - Mac OS X CoreAudio driver (Mike Kronenberg) + - DirectSound driver (malc) + - ALSA audio driver (malc) + - new audio options: '-soundhw' and '-audio-help' (malc) + - ES1370 PCI audio device (malc) + - Initial USB support + - Linux host serial port access + - Linux host low level parallel port access + - New network emulation code supporting VLANs. + - MIPS and MIPSel User Linux emulation + - MIPS fixes to boot Linux (Daniel Jacobowitz) + - NX bit support + - Initial SPARC SMP support (Blue Swirl) + - Major overhaul of the virtual FAT driver for read/write support + (Johannes Schindelin) + +version 0.7.2: + + - x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit) + - merge self modifying code handling in dirty ram page mecanism. + - MIPS fixes (Ralf Baechle) + - better user net performances + +version 0.7.1: + + - read-only Virtual FAT support (Johannes Schindelin) + - Windows 2000 install disk full hack (original idea from Vladimir + N. Oleynik) + - VMDK disk image creation (Filip Navara) + - SPARC64 progress (Blue Swirl) + - initial MIPS support (Jocelyn mayer) + - MIPS improvements (Ralf Baechle) + - 64 bit fixes in user networking (initial patch by Gwenole Beauchesne) + - IOAPIC support (Filip Navara) + +version 0.7.0: + + - better BIOS translation and HDD geometry auto-detection + - user mode networking bug fix + - undocumented FPU ops support + - Cirrus VGA: support for 1280x1024x[8,15,16] modes + - 'pidfile' option + - .dmg disk image format support (Johannes Schindelin) + - keymaps support (initial patch by Johannes Schindelin) + - big endian ARM support (Lennert Buytenhek) + - added generic 64 bit target support + - x86_64 target support + - initial APIC support + - MMX/SSE/SSE2/PNI support + - PC parallel port support (Mark Jonckheere) + - initial SPARC64 support (Blue Swirl) + - SPARC target boots Linux (Blue Swirl) + - armv5te user mode support (Paul Brook) + - ARM VFP support (Paul Brook) + - ARM "Angel" semihosting syscalls (Paul Brook) + - user mode gdb stub support (Paul Brook) + - Samba 3 support + - initial Cocoa support (Pierre d'Herbemont) + - generic FPU emulation code + - Virtual PC read-only disk image support (Alex Beregszaszi) + +version 0.6.1: + + - Mac OS X port (Pierre d'Herbemont) + - Virtual console support + - Better monitor line edition + - New block device layer + - New 'qcow' growable disk image support with AES encryption and + transparent decompression + - VMware 3 and 4 read-only disk image support (untested) + - Support for up to 4 serial ports + - TFTP server support (Magnus Damm) + - Port redirection support in user mode networking + - Support for not executable data sections + - Compressed loop disk image support (Johannes Schindelin) + - Level triggered IRQ fix (aka NE2000 PCI performance fix) (Steve + Wormley) + - Fixed Fedora Core 2 problems (now you can run qemu without any + LD_ASSUME_KERNEL tricks on FC2) + - DHCP fix for Windows (accept DHCPREQUEST alone) + - SPARC system emulation (Blue Swirl) + - Automatic Samba configuration for host file access from Windows. + - '-loadvm' and '-full-screen' options + - ne2000 savevm support (Johannes Schindelin) + - Ctrl-Alt is now the default grab key. Ctrl-Alt-[0-9] switches to + the virtual consoles. + - BIOS floppy fix for NT4 (Mike Nordell, Derek Fawcus, Volker Ruppert) + - Floppy fixes for NT4 and NT5 (Mike Nordell) + - NT4 IDE fixes (Ben Pfaf, Mike Nordell) + - SDL Audio support and SB16 fixes (malc) + - ENTER instruction bug fix (initial patch by Stefan Kisdaroczi) + - VGA font change fix + - VGA read-only CRTC register fix + +version 0.6.0: + + - minimalist FPU exception support (NetBSD FPU probe fix) + - cr0.ET fix (Win95 boot) + - *BSD port (Markus Niemisto) + - I/O access fix (signaled by Mark Jonckheere) + - IDE drives serial number fix (Mike Nordell) + - int13 CDROM BIOS fix (aka Solaris x86 install CD fix) + - int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix) + - BSR/BSF "undefined behaviour" fix + - vmdk2raw: convert VMware disk images to raw images + - PCI support + - NE2K PCI support + - dummy VGA PCI support + - VGA font selection fix (Daniel Serpell) + - PIC reset fix (Hidemi KAWAI) + - PIC spurious irq support (aka Solaris install bug) + - added '-localtime' option + - Cirrus CL-GD54xx VGA support (initial patch by Makoto Suzuki (suzu)) + - APM and system shutdown support + - Fixed system reset + - Support for other PC BIOSes + - Initial PowerMac hardware emulation + - PowerMac/PREP OpenFirmware compatible BIOS (Jocelyn Mayer) + - initial IDE BMDMA support (needed for Darwin x86) + - Set the default memory size for PC emulation to 128 MB + +version 0.5.5: + + - SDL full screen support (initial patch by malc) + - VGA support on PowerPC PREP + - VBE fixes (Matthew Mastracci) + - PIT fixes (aka Win98 hardware probe and "VGA slowness" bug) + - IDE master only fixes (aka Win98 CD-ROM probe bug) + - ARM load/store half word fix (Ulrich Hecht) + - FDC fixes for Win98 + +version 0.5.4: + + - qemu-fast fixes + - BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell) + - keyboard/mouse fix (Mike Nordell) + - IDE fixes (Linux did not recognized slave drivers) + - VM86 EIP masking fix (aka NT5 install fix) (Mike Nordell) + - QEMU can now boot a PowerPC Linux kernel (Jocelyn Mayer) + - User mode network stack + - imul imm8 fix + 0x82 opcode support (Hidemi KAWAI) + - precise self modifying code (aka BeOS install bug) + +version 0.5.3: + + - added Bochs VESA VBE support + - VGA memory map mode 3 access fix (OS/2 install fix) + - IDE fixes (Jens Axboe) + - CPU interrupt fixes + - fixed various TLB invalidation cases (NT install) + - fixed cr0.WP semantics (XP install) + - direct chaining support for SPARC and PowerPC (faster) + - ARM NWFPE support (initial patch by Ulrich Hecht) + - added specific x86 to x86 translator (close to native performance + in qemu-i386 and qemu-fast) + - shm syscalls support (Paul McKerras) + - added accurate CR0.MP/ME/TS emulation + - fixed DMA memory write access (Win95 boot floppy fix) + - graphical x86 linux loader + - command line monitor + - generic removable device support + - support of CD-ROM change + - multiple network interface support + - initial x86-64 host support (Gwenole Beauchesne) + - lret to outer privilege fix (OS/2 install fix) + - task switch fixes (SkyOS boot) + - VM save/restore commands + - new timer API + - more precise RTC emulation (periodic timers + time updates) + - Win32 port (initial patch by Kazu) + +version 0.5.2: + + - improved soft MMU speed (assembly functions and specializing) + - improved multitasking speed by avoiding flushing TBs when + switching tasks + - improved qemu-fast speed + - improved self modifying code handling (big performance gain in + softmmu mode). + - fixed IO checking + - fixed CD-ROM detection (win98 install CD) + - fixed addseg real mode bug (GRUB boot fix) + - added ROM memory support (win98 boot) + - fixed 'call Ev' in case of paging exception + - updated the script 'qemu-binfmt-conf.sh' to use QEMU automagically + when launching executables for the supported target CPUs. + - PowerPC system emulation update (Jocelyn Mayer) + - PC floppy emulation and DMA fixes (Jocelyn Mayer) + - polled mode for PIC (Jocelyn Mayer) + - fixed PTE dirty bit handling + - fixed xadd same reg bug + - fixed cmpxchg exception safeness + - access to virtual memory in gdb stub + - task gate and NT flag fixes + - eflags optimisation fix for string operations + +version 0.5.1: + + - float access fixes when using soft mmu + - PC emulation support on PowerPC + - A20 support + - IDE CD-ROM emulation + - ARM fixes (Ulrich Hecht) + - SB16 emulation (malc) + - IRET and INT fixes in VM86 mode with IOPL=3 + - Port I/Os use TSS io map + - Full task switching/task gate support + - added verr, verw, arpl, fcmovxx + - PowerPC target support (Jocelyn Mayer) + - Major SPARC target fixes (dynamically linked programs begin to work) + +version 0.5.0: + + - full hardware level VGA emulation + - graphical display with SDL + - added PS/2 mouse and keyboard emulation + - popw (%esp) fix + - mov to/from segment data width fix + - added real mode support + - added Bochs BIOS and LGPL'ed VGA BIOS loader in qemu + - m68k host port (Richard Zidlicky) + - partial soft MMU support for memory mapped I/Os + - multi-target build + - fixed: no error code in hardware interrupts + - fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn + - correct single stepping through string operations + - preliminary SPARC target support (Thomas M. Ogrisegg) + - tun-fd option (Rusty Russell) + - automatic IDE geometry detection + - renamed 'vl' to qemu[-fast] and user qemu to qemu-{cpu}. + - added man page + - added full soft mmu mode to launch unpatched OSes. + +version 0.4.3: + + - x86 exception fix in case of nop instruction. + - gcc 3.2.2 bug workaround (RedHat 9 fix) + - sparc and Alpha host fixes + - many ARM target fixes: 'ls' and 'bash' can be launched. + +version 0.4.2: + + - many exception handling fixes (can compile a Linux kernel inside vl) + - IDE emulation support + - initial GDB stub support + - deferred update support for disk images (Rusty Russell) + - accept User Mode Linux Copy On Write disk images + - SMP kernels can at least be booted + +version 0.4.1: + + - more accurate timer support in vl. + - more reliable NE2000 probe in vl. + - added 2.5.66 kernel in vl-test. + - added VLTMPDIR environment variable in vl. + +version 0.4: + + - initial support for ring 0 x86 processor emulation + - fixed signal handling for correct dosemu DPMI emulation + - fast x86 MMU emulation with mmap() + - fixed popl (%esp) case + - Linux kernel can be executed by QEMU with the 'vl' command. + +version 0.3: + + - initial support for ARM emulation + - added fnsave, frstor, fnstenv, fldenv FPU instructions + - added FPU register save in signal emulation + - initial ARM port + - Sparc and Alpha ports work on the regression test + - generic ioctl number conversion + - fixed ioctl type conversion + +version 0.2: + + - PowerPC disassembly and ELF symbols output (Rusty Russell) + - flock support (Rusty Russell) + - ugetrlimit support (Rusty Russell) + - fstat64 fix (Rusty Russell) + - initial Alpha port (Falk Hueffner) + - initial IA64 port (Matt Wilson) + - initial Sparc and Sparc64 port (David S. Miller) + - added HLT instruction + - LRET instruction fix. + - added GPF generation for I/Os. + - added INT3 and TF flag support. + - SHL instruction C flag fix. + - mmap emulation for host page size > 4KB + - self-modifying code support + - better VM86 support (dosemu works on non trivial programs) + - precise exception support (EIP is computed correctly in most cases) + - more precise LDT/GDT/IDT emulation + - faster segment load in vm86 mode + - direct chaining of basic blocks (faster emulation) + +version 0.1.6: + + - automatic library search system. QEMU can now work with unpatched + ELF dynamic loader and libc (Rusty Russell). + - ISO C warning fixes (Alistair Strachan) + - first self-virtualizable version (works only as long as the + translation cache is not flushed) + - RH9 fixes + +version 0.1.5: + + - ppc64 support + personality() patch (Rusty Russell) + - first Alpha CPU patches (Falk Hueffner) + - removed bfd.h dependency + - fixed shrd, shld, idivl and divl on PowerPC. + - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC). + +version 0.1.4: + + - more accurate VM86 emulation (can launch small DOS 16 bit + executables in wine). + - fixed push/pop fs/gs + - added iret instruction. + - added times() syscall and SIOCATMARK ioctl. + +version 0.1.3: + + - S390 support (Ulrich Weigand) + - glibc 2.3.x compile fix (Ulrich Weigand) + - socketcall endian fix (Ulrich Weigand) + - struct sockaddr endian fix (Ulrich Weigand) + - sendmsg/recvmsg endian fix (Ulrich Weigand) + - execve endian fix (Ulrich Weigand) + - fdset endian fix (Ulrich Weigand) + - partial setsockopt syscall support (Ulrich Weigand) + - more accurate pushf/popf emulation + - first partial vm86() syscall support (can be used with runcom example). + - added bound, cmpxchg8b, cpuid instructions + - added 16 bit addressing support/override for string operations + - poll() fix + +version 0.1.2: + + - compile fixes + - xlat instruction + - xchg instruction memory lock + - added simple vm86 example (not working with QEMU yet). The 54 byte + DOS executable 'pi_10.com' program was released by Bertram + Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html). + +version 0.1.1: + + - glibc 2.2 compilation fixes + - added -s and -L options + - binary distribution of x86 glibc and wine + - big endian fixes in ELF loader and getdents. + +version 0.1: + + - initial public release. diff --git a/HACKING b/HACKING new file mode 100644 index 0000000..12fbc8a --- /dev/null +++ b/HACKING @@ -0,0 +1,159 @@ +1. Preprocessor + +For variadic macros, stick with this C99-like syntax: + +#define DPRINTF(fmt, ...) \ + do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0) + +2. C types + +It should be common sense to use the right type, but we have collected +a few useful guidelines here. + +2.1. Scalars + +If you're using "int" or "long", odds are good that there's a better type. +If a variable is counting something, it should be declared with an +unsigned type. + +If it's host memory-size related, size_t should be a good choice (use +ssize_t only if required). Guest RAM memory offsets must use ram_addr_t, +but only for RAM, it may not cover whole guest address space. + +If it's file-size related, use off_t. +If it's file-offset related (i.e., signed), use off_t. +If it's just counting small numbers use "unsigned int"; +(on all but oddball embedded systems, you can assume that that +type is at least four bytes wide). + +In the event that you require a specific width, use a standard type +like int32_t, uint32_t, uint64_t, etc. The specific types are +mandatory for VMState fields. + +Don't use Linux kernel internal types like u32, __u32 or __le32. + +Use hwaddr for guest physical addresses except pcibus_t +for PCI addresses. In addition, ram_addr_t is a QEMU internal address +space that maps guest RAM physical addresses into an intermediate +address space that can map to host virtual address spaces. Generally +speaking, the size of guest memory can always fit into ram_addr_t but +it would not be correct to store an actual guest physical address in a +ram_addr_t. + +For CPU virtual addresses there are several possible types. +vaddr is the best type to use to hold a CPU virtual address in +target-independent code. It is guaranteed to be large enough to hold a +virtual address for any target, and it does not change size from target +to target. It is always unsigned. +target_ulong is a type the size of a virtual address on the CPU; this means +it may be 32 or 64 bits depending on which target is being built. It should +therefore be used only in target-specific code, and in some +performance-critical built-per-target core code such as the TLB code. +There is also a signed version, target_long. +abi_ulong is for the *-user targets, and represents a type the size of +'void *' in that target's ABI. (This may not be the same as the size of a +full CPU virtual address in the case of target ABIs which use 32 bit pointers +on 64 bit CPUs, like sparc32plus.) Definitions of structures that must match +the target's ABI must use this type for anything that on the target is defined +to be an 'unsigned long' or a pointer type. +There is also a signed version, abi_long. + +Of course, take all of the above with a grain of salt. If you're about +to use some system interface that requires a type like size_t, pid_t or +off_t, use matching types for any corresponding variables. + +Also, if you try to use e.g., "unsigned int" as a type, and that +conflicts with the signedness of a related variable, sometimes +it's best just to use the *wrong* type, if "pulling the thread" +and fixing all related variables would be too invasive. + +Finally, while using descriptive types is important, be careful not to +go overboard. If whatever you're doing causes warnings, or requires +casts, then reconsider or ask for help. + +2.2. Pointers + +Ensure that all of your pointers are "const-correct". +Unless a pointer is used to modify the pointed-to storage, +give it the "const" attribute. That way, the reader knows +up-front that this is a read-only pointer. Perhaps more +importantly, if we're diligent about this, when you see a non-const +pointer, you're guaranteed that it is used to modify the storage +it points to, or it is aliased to another pointer that is. + +2.3. Typedefs +Typedefs are used to eliminate the redundant 'struct' keyword. + +2.4. Reserved namespaces in C and POSIX +Underscore capital, double underscore, and underscore 't' suffixes should be +avoided. + +3. Low level memory management + +Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign +APIs is not allowed in the QEMU codebase. Instead of these routines, +use the GLib memory allocation routines g_malloc/g_malloc0/g_new/ +g_new0/g_realloc/g_free or QEMU's qemu_memalign/qemu_blockalign/qemu_vfree +APIs. + +Please note that g_malloc will exit on allocation failure, so there +is no need to test for failure (as you would have to with malloc). +Calling g_malloc with a zero size is valid and will return NULL. + +Memory allocated by qemu_memalign or qemu_blockalign must be freed with +qemu_vfree, since breaking this will cause problems on Win32. + +4. String manipulation + +Do not use the strncpy function. As mentioned in the man page, it does *not* +guarantee a NULL-terminated buffer, which makes it extremely dangerous to use. +It also zeros trailing destination bytes out to the specified length. Instead, +use this similar function when possible, but note its different signature: +void pstrcpy(char *dest, int dest_buf_size, const char *src) + +Don't use strcat because it can't check for buffer overflows, but: +char *pstrcat(char *buf, int buf_size, const char *s) + +The same limitation exists with sprintf and vsprintf, so use snprintf and +vsnprintf. + +QEMU provides other useful string functions: +int strstart(const char *str, const char *val, const char **ptr) +int stristart(const char *str, const char *val, const char **ptr) +int qemu_strnlen(const char *s, int max_len) + +There are also replacement character processing macros for isxyz and toxyz, +so instead of e.g. isalnum you should use qemu_isalnum. + +Because of the memory management rules, you must use g_strdup/g_strndup +instead of plain strdup/strndup. + +5. Printf-style functions + +Whenever you add a new printf-style function, i.e., one with a format +string argument and following "..." in its prototype, be sure to use +gcc's printf attribute directive in the prototype. + +This makes it so gcc's -Wformat and -Wformat-security options can do +their jobs and cross-check format strings with the number and types +of arguments. + +6. C standard, implementation defined and undefined behaviors + +C code in QEMU should be written to the C99 language specification. A copy +of the final version of the C99 standard with corrigenda TC1, TC2, and TC3 +included, formatted as a draft, can be downloaded from: + http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf + +The C language specification defines regions of undefined behavior and +implementation defined behavior (to give compiler authors enough leeway to +produce better code). In general, code in QEMU should follow the language +specification and avoid both undefined and implementation defined +constructs. ("It works fine on the gcc I tested it with" is not a valid +argument...) However there are a few areas where we allow ourselves to +assume certain behaviors because in practice all the platforms we care about +behave in the same way and writing strictly conformant code would be +painful. These are: + * you may assume that integers are 2s complement representation + * you may assume that right shift of a signed integer duplicates + the sign bit (ie it is an arithmetic shift, not a logical shift) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0e0b4b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The following points clarify the QEMU license: + +1) QEMU as a whole is released under the GNU General Public License, +version 2. + +2) Parts of QEMU have specific licenses which are compatible with the +GNU General Public License, version 2. Hence each source file contains +its own licensing information. Source files with no licensing information +are released under the GNU General Public License, version 2 or (at your +option) any later version. + +As of July 2013, contributions under version 2 of the GNU General Public +License (and no later version) are only accepted for the following files +or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*. + +3) The Tiny Code Generator (TCG) is released under the BSD license + (see license headers in files). + +4) QEMU is a trademark of Fabrice Bellard. + +Fabrice Bellard and the QEMU team diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..0463696 --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,1311 @@ +QEMU Maintainers +================ + +The intention of this file is not to establish who owns what portions of the +code base, but to provide a set of names that developers can consult when they +have a question about a particular subset and also to provide a set of names +to be CC'd when submitting a patch to obtain appropriate review. + +In general, if you have a question about inclusion of a patch, you should +consult qemu-devel and not any specific individual privately. + +Descriptions of section entries: + + M: Mail patches to: FullName + L: Mailing list that is relevant to this area + W: Web-page with status/info + Q: Patchwork web based patch tracking system site + T: SCM tree type and location. Type is one of: git, hg, quilt, stgit. + S: Status, one of the following: + Supported: Someone is actually paid to look after this. + Maintained: Someone actually looks after it. + Odd Fixes: It has a maintainer but they don't have time to do + much other than throw the odd patch in. See below. + Orphan: No current maintainer [but maybe you could take the + role as you write your new code]. + Obsolete: Old code. Something tagged obsolete generally means + it has been replaced by a better system and you + should be using that. + F: Files and directories with wildcard patterns. + A trailing slash includes all files and subdirectory files. + F: drivers/net/ all files in and below drivers/net + F: drivers/net/* all files in drivers/net, but not below + F: */net/* all files in "any top level directory"/net + One pattern per line. Multiple F: lines acceptable. + X: Files and directories that are NOT maintained, same rules as F: + Files exclusions are tested before file matches. + Can be useful for excluding a specific subdirectory, for instance: + F: net/ + X: net/ipv6/ + matches all files in and below net excluding net/ipv6/ + K: Keyword perl extended regex pattern to match content in a + patch or file. For instance: + K: of_get_profile + matches patches or files that contain "of_get_profile" + K: \b(printk|pr_(info|err))\b + matches patches or files that contain one or more of the words + printk, pr_info or pr_err + One regex pattern per line. Multiple K: lines acceptable. + + +General Project Administration +------------------------------ +M: Peter Maydell + +Responsible Disclosure, Reporting Security Issues +------------------------------ +W: http://wiki.qemu.org/SecurityProcess +M: Michael S. Tsirkin +L: secalert@redhat.com + +Guest CPU cores (TCG): +---------------------- +Overall +L: qemu-devel@nongnu.org +S: Odd fixes +F: cpu-exec.c +F: cputlb.c +F: softmmu_template.h +F: translate-all.c +F: include/exec/cpu_ldst.h +F: include/exec/cpu_ldst_template.h +F: include/exec/helper*.h + +Alpha +M: Richard Henderson +S: Maintained +F: target-alpha/ +F: hw/alpha/ +F: tests/tcg/alpha/ + +ARM +M: Peter Maydell +S: Maintained +F: target-arm/ +F: hw/arm/ +F: hw/cpu/a*mpcore.c + +CRIS +M: Edgar E. Iglesias +S: Maintained +F: target-cris/ +F: hw/cris/ +F: tests/tcg/cris/ + +LM32 +M: Michael Walle +S: Maintained +F: target-lm32/ +F: disas/lm32.c +F: hw/lm32/ +F: hw/*/lm32_* +F: hw/*/milkymist-* +F: include/hw/char/lm32_juart.h +F: include/hw/lm32/ +F: tests/tcg/lm32/ + +M68K +S: Orphan +F: target-m68k/ +F: hw/m68k/ + +MicroBlaze +M: Edgar E. Iglesias +S: Maintained +F: target-microblaze/ +F: hw/microblaze/ + +MIPS +M: Aurelien Jarno +M: Leon Alrae +S: Maintained +F: target-mips/ +F: hw/mips/ +F: tests/tcg/mips/ + +Moxie +M: Anthony Green +S: Maintained +F: target-moxie/ + +OpenRISC +M: Jia Liu +S: Maintained +F: target-openrisc/ +F: hw/openrisc/ +F: tests/tcg/openrisc/ + +PowerPC +M: Alexander Graf +L: qemu-ppc@nongnu.org +S: Maintained +F: target-ppc/ +F: hw/ppc/ + +S390 +M: Richard Henderson +M: Alexander Graf +S: Maintained +F: target-s390x/ +F: hw/s390x/ + +SH4 +M: Aurelien Jarno +S: Odd Fixes +F: target-sh4/ +F: hw/sh4/ + +SPARC +M: Blue Swirl +M: Mark Cave-Ayland +S: Maintained +F: target-sparc/ +F: hw/sparc/ +F: hw/sparc64/ + +UniCore32 +M: Guan Xuetao +S: Maintained +F: target-unicore32/ +F: hw/unicore32/ + +X86 +M: Paolo Bonzini +M: Richard Henderson +M: Eduardo Habkost +S: Maintained +F: target-i386/ +F: hw/i386/ + +Xtensa +M: Max Filippov +W: http://wiki.osll.spb.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa +S: Maintained +F: target-xtensa/ +F: hw/xtensa/ +F: tests/tcg/xtensa/ + +TriCore +M: Bastian Koppelmann +S: Maintained +F: target-tricore/ +F: hw/tricore/ + +Guest CPU Cores (KVM): +---------------------- + +Overall +M: Paolo Bonzini +L: kvm@vger.kernel.org +S: Supported +F: kvm-* +F: */kvm.* + +ARM +M: Peter Maydell +S: Maintained +F: target-arm/kvm.c + +MIPS +M: James Hogan +S: Maintained +F: target-mips/kvm.c + +PPC +M: Alexander Graf +S: Maintained +F: target-ppc/kvm.c + +S390 +M: Christian Borntraeger +M: Cornelia Huck +M: Alexander Graf +S: Maintained +F: target-s390x/kvm.c +F: hw/intc/s390_flic.c +F: hw/intc/s390_flic_kvm.c +F: include/hw/s390x/s390_flic.h + +X86 +M: Paolo Bonzini +M: Marcelo Tosatti +L: kvm@vger.kernel.org +S: Supported +F: target-i386/kvm.c + +Guest CPU Cores (Xen): +---------------------- + +X86 +M: Stefano Stabellini +L: xen-devel@lists.xensource.com +S: Supported +F: xen-* +F: */xen* + +Hosts: +------ + +LINUX +L: qemu-devel@nongnu.org +S: Maintained +F: linux-* +F: linux-headers/ + +POSIX +L: qemu-devel@nongnu.org +S: Maintained +F: *posix* + +W32, W64 +L: qemu-devel@nongnu.org +M: Stefan Weil +S: Maintained +F: *win32* + +ARM Machines +------------ +Allwinner-a10 +M: Li Guang +S: Maintained +F: hw/*/allwinner-a10* +F: include/hw/*/allwinner-a10* +F: hw/arm/cubieboard.c + +Exynos +M: Evgeny Voevodin +M: Maksim Kozlov +M: Igor Mitsyanko +M: Dmitry Solodkiy +S: Maintained +F: hw/*/exynos* + +Calxeda Highbank +M: Rob Herring +S: Maintained +F: hw/arm/highbank.c +F: hw/net/xgmac.c + +Canon DIGIC +M: Antony Pavlov +S: Maintained +F: include/hw/arm/digic.h +F: hw/*/digic* + +Gumstix +L: qemu-devel@nongnu.org +S: Orphan +F: hw/arm/gumstix.c + +i.MX31 +M: Peter Chubb +S: Odd fixes +F: hw/*/imx* +F: hw/arm/kzm.c + +Integrator CP +M: Peter Maydell +S: Maintained +F: hw/arm/integratorcp.c + +Mainstone +L: qemu-devel@nongnu.org +S: Orphan +F: hw/arm/mainstone.c + +Musicpal +M: Jan Kiszka +S: Maintained +F: hw/arm/musicpal.c + +nSeries +M: Andrzej Zaborowski +S: Maintained +F: hw/arm/nseries.c + +Palm +M: Andrzej Zaborowski +S: Maintained +F: hw/arm/palm.c + +Real View +M: Peter Maydell +S: Maintained +F: hw/arm/realview* + +Spitz +M: Andrzej Zaborowski +S: Maintained +F: hw/arm/spitz.c + +Stellaris +M: Peter Maydell +S: Maintained +F: hw/*/stellaris* + +Versatile PB +M: Peter Maydell +S: Maintained +F: hw/*/versatile* + +Xilinx Zynq +M: Peter Crosthwaite +S: Maintained +F: hw/arm/xilinx_zynq.c +F: hw/misc/zynq_slcr.c +F: hw/*/cadence_* +F: hw/ssi/xilinx_spips.c + +CRIS Machines +------------- +Axis Dev88 +M: Edgar E. Iglesias +S: Maintained +F: hw/cris/axis_dev88.c +F: hw/*/etraxfs_*.c + +LM32 Machines +------------- +EVR32 and uclinux BSP +M: Michael Walle +S: Maintained +F: hw/lm32/lm32_boards.c + +milkymist +M: Michael Walle +S: Maintained +F: hw/lm32/milkymist.c + +M68K Machines +------------- +an5206 +S: Orphan +F: hw/m68k/an5206.c + +dummy_m68k +S: Orphan +F: hw/m68k/dummy_m68k.c + +mcf5208 +S: Orphan +F: hw/m68k/mcf5208.c + +MicroBlaze Machines +------------------- +petalogix_s3adsp1800 +M: Edgar E. Iglesias +S: Maintained +F: hw/microblaze/petalogix_s3adsp1800_mmu.c + +petalogix_ml605 +M: Peter Crosthwaite +S: Maintained +F: hw/microblaze/petalogix_ml605_mmu.c + +MIPS Machines +------------- +Jazz +M: Hervé Poussineau +S: Maintained +F: hw/mips/mips_jazz.c + +Malta +M: Aurelien Jarno +S: Maintained +F: hw/mips/mips_malta.c + +Mipssim +L: qemu-devel@nongnu.org +S: Orphan +F: hw/mips/mips_mipssim.c + +R4000 +M: Aurelien Jarno +S: Maintained +F: hw/mips/mips_r4k.c + +OpenRISC Machines +----------------- +or1k-sim +M: Jia Liu +S: Maintained +F: hw/openrisc/openrisc_sim.c + +PowerPC Machines +---------------- +405 +M: Alexander Graf +L: qemu-ppc@nongnu.org +S: Odd Fixes +F: hw/ppc/ppc405_boards.c + +Bamboo +M: Alexander Graf +L: qemu-ppc@nongnu.org +S: Odd Fixes +F: hw/ppc/ppc440_bamboo.c + +e500 +M: Alexander Graf +M: Scott Wood +L: qemu-ppc@nongnu.org +S: Supported +F: hw/ppc/e500.[hc] +F: hw/ppc/e500plat.c + +mpc8544ds +M: Alexander Graf +M: Scott Wood +L: qemu-ppc@nongnu.org +S: Supported +F: hw/ppc/mpc8544ds.c +F: hw/ppc/mpc8544_guts.c + +New World +M: Alexander Graf +L: qemu-ppc@nongnu.org +S: Maintained +F: hw/ppc/mac_newworld.c +F: hw/pci-host/uninorth.c +F: hw/pci-bridge/dec.[hc] +F: hw/misc/macio/ + +Old World +M: Alexander Graf +L: qemu-ppc@nongnu.org +S: Maintained +F: hw/ppc/mac_oldworld.c +F: hw/pci-host/grackle.c +F: hw/misc/macio/ + +PReP +M: Andreas Färber +L: qemu-ppc@nongnu.org +S: Odd Fixes +F: hw/ppc/prep.c +F: hw/pci-host/prep.[hc] +F: hw/isa/pc87312.[hc] + +sPAPR +M: Alexander Graf +L: qemu-ppc@nongnu.org +S: Supported +F: hw/*/spapr* +F: include/hw/*/spapr* +F: hw/*/xics* +F: include/hw/*/xics* +F: pc-bios/spapr-rtas/* + +virtex_ml507 +M: Edgar E. Iglesias +L: qemu-ppc@nongnu.org +S: Odd Fixes +F: hw/ppc/virtex_ml507.c + +SH4 Machines +------------ +R2D +M: Magnus Damm +S: Maintained +F: hw/sh4/r2d.c + +Shix +M: Magnus Damm +S: Orphan +F: hw/sh4/shix.c + +SPARC Machines +-------------- +Sun4m +M: Blue Swirl +M: Mark Cave-Ayland +S: Maintained +F: hw/sparc/sun4m.c + +Sun4u +M: Blue Swirl +M: Mark Cave-Ayland +S: Maintained +F: hw/sparc64/sun4u.c + +Leon3 +M: Fabien Chouteau +S: Maintained +F: hw/sparc/leon3.c +F: hw/*/grlib* + +S390 Machines +------------- +S390 Virtio +M: Alexander Graf +S: Maintained +F: hw/s390x/s390-*.c +X: hw/s390x/*pci*.[hc] + +S390 Virtio-ccw +M: Cornelia Huck +M: Christian Borntraeger +M: Alexander Graf +S: Supported +F: hw/s390x/s390-virtio-ccw.c +F: hw/s390x/css.[hc] +F: hw/s390x/sclp*.[hc] +F: hw/s390x/ipl*.[hc] +F: hw/s390x/*pci*.[hc] +F: include/hw/s390x/ +F: pc-bios/s390-ccw/ +T: git git://github.com/cohuck/qemu virtio-ccw-upstr + +UniCore32 Machines +------------- +PKUnity-3 SoC initramfs-with-busybox +M: Guan Xuetao +S: Maintained +F: hw/*/puv3* +F: hw/unicore32/ + +X86 Machines +------------ +PC +M: Michael S. Tsirkin +S: Supported +F: include/hw/i386/ +F: hw/i386/ +F: hw/pci-host/piix.c +F: hw/pci-host/q35.c +F: hw/pci-host/pam.c +F: include/hw/pci-host/q35.h +F: include/hw/pci-host/pam.h +F: hw/isa/piix4.c +F: hw/isa/lpc_ich9.c +F: hw/i2c/smbus_ich9.c +F: hw/acpi/piix4.c +F: hw/acpi/ich9.c +F: include/hw/acpi/ich9.h +F: include/hw/acpi/piix.h + + +Xtensa Machines +--------------- +sim +M: Max Filippov +S: Maintained +F: hw/xtensa/sim.c + +XTFPGA (LX60, LX200, ML605, KC705) +M: Max Filippov +S: Maintained +F: hw/xtensa/xtfpga.c +F: hw/net/opencores_eth.c + +Devices +------- +EDU +M: Jiri Slaby +S: Maintained +F: hw/misc/edu.c + +IDE +M: John Snow +L: qemu-block@nongnu.org +S: Supported +F: include/hw/ide.h +F: hw/ide/ +F: hw/block/block.c +F: hw/block/cdrom.c +F: hw/block/hd-geometry.c +F: tests/ide-test.c +F: tests/ahci-test.c +T: git git://github.com/jnsnow/qemu.git ide + +Floppy +M: John Snow +L: qemu-block@nongnu.org +S: Supported +F: hw/block/fdc.c +F: include/hw/block/fdc.h +T: git git://github.com/jnsnow/qemu.git ide + +OMAP +M: Peter Maydell +S: Maintained +F: hw/*/omap* + +PCI +M: Michael S. Tsirkin +S: Supported +F: include/hw/pci/* +F: hw/pci/* +F: hw/acpi/* + +ppc4xx +M: Alexander Graf +L: qemu-ppc@nongnu.org +S: Odd Fixes +F: hw/ppc/ppc4*.c + +ppce500 +M: Alexander Graf +M: Scott Wood +L: qemu-ppc@nongnu.org +S: Supported +F: hw/ppc/e500* + +SCSI +M: Paolo Bonzini +S: Supported +F: include/hw/scsi* +F: hw/scsi/* +T: git git://github.com/bonzini/qemu.git scsi-next + +LSI53C895A +S: Orphan +F: hw/scsi/lsi53c895a.c + +SSI +M: Peter Crosthwaite +S: Maintained +F: hw/ssi/* +F: hw/block/m25p80.c + +USB +M: Gerd Hoffmann +S: Maintained +F: hw/usb/* +F: tests/usb-*-test.c + +USB (serial adapter) +M: Gerd Hoffmann +M: Samuel Thibault +S: Maintained +F: hw/usb/dev-serial.c + +VFIO +M: Alex Williamson +S: Supported +F: hw/vfio/* + +vhost +M: Michael S. Tsirkin +S: Supported +F: hw/*/*vhost* + +virtio +M: Michael S. Tsirkin +S: Supported +F: hw/*/virtio* + +virtio-9p +M: Aneesh Kumar K.V +S: Supported +F: hw/9pfs/ +F: fsdev/ +F: tests/virtio-9p-test.c +T: git git://github.com/kvaneesh/QEMU.git + +virtio-blk +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: hw/block/virtio-blk.c +F: hw/block/dataplane/* +F: hw/virtio/dataplane/* +T: git git://github.com/stefanha/qemu.git block + +virtio-ccw +M: Cornelia Huck +M: Christian Borntraeger +S: Supported +F: hw/s390x/virtio-ccw.[hc] +T: git git://github.com/cohuck/qemu virtio-ccw-upstr + +virtio-serial +M: Amit Shah +S: Supported +F: hw/char/virtio-serial-bus.c +F: hw/char/virtio-console.c +F: include/hw/virtio/virtio-serial.h + +virtio-rng +M: Amit Shah +S: Supported +F: hw/virtio/virtio-rng.c +F: include/hw/virtio/virtio-rng.h +F: backends/rng*.c + +nvme +M: Keith Busch +L: qemu-block@nongnu.org +S: Supported +F: hw/block/nvme* +F: tests/nvme-test.c + +megasas +M: Hannes Reinecke +L: qemu-block@nongnu.org +S: Supported +F: hw/scsi/megasas.c +F: hw/scsi/mfi.h + +Xilinx EDK +M: Peter Crosthwaite +M: Edgar E. Iglesias +S: Maintained +F: hw/*/xilinx_* +F: include/hw/xilinx.h + +Vmware +M: Dmitry Fleytman +S: Maintained +F: hw/net/vmxnet* +F: hw/scsi/vmw_pvscsi* + +Rocker +M: Scott Feldman +M: Jiri Pirko +S: Maintained +F: hw/net/rocker/ + +Subsystems +---------- +Audio +M: Vassili Karpov (malc) +M: Gerd Hoffmann +S: Maintained +F: audio/ +F: hw/audio/ +F: tests/ac97-test.c +F: tests/es1370-test.c +F: tests/intel-hda-test.c + +Block layer core +M: Kevin Wolf +L: qemu-block@nongnu.org +S: Supported +F: block* +F: block/ +F: hw/block/ +F: include/block/ +F: qemu-img* +F: qemu-io* +F: tests/qemu-iotests/ +T: git git://repo.or.cz/qemu/kevin.git block + +Block I/O path +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: async.c +F: aio-*.c +F: block/io.c +F: migration/block* +T: git git://github.com/stefanha/qemu.git block + +Block Jobs +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: blockjob.c +F: include/block/blockjob.h +F: block/backup.c +F: block/commit.c +F: block/stream.h +F: block/mirror.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +Block QAPI, monitor, command line +M: Markus Armbruster +S: Supported +F: blockdev.c +F: block/qapi.c +F: qapi/block*.json +T: git git://repo.or.cz/qemu/armbru.git block-next + +Character Devices +M: Paolo Bonzini +S: Maintained +F: qemu-char.c +F: backends/msmouse.c +F: backends/testdev.c + +Character Devices (Braille) +M: Samuel Thibault +S: Maintained +F: backends/baum.c + +Coverity model +M: Markus Armbruster +S: Supported +F: scripts/coverity-model.c + +CPU +M: Andreas Färber +S: Supported +F: qom/cpu.c +F: include/qom/cpu.h +F: target-i386/cpu.c + +ICC Bus +M: Igor Mammedov +S: Supported +F: include/hw/cpu/icc_bus.h +F: hw/cpu/icc_bus.c + +Device Tree +M: Peter Crosthwaite +M: Alexander Graf +S: Maintained +F: device_tree.[ch] + +GDB stub +L: qemu-devel@nongnu.org +S: Odd Fixes +F: gdbstub* +F: gdb-xml/ + +Memory API +M: Paolo Bonzini +S: Supported +F: include/exec/ioport.h +F: ioport.c +F: include/exec/memory.h +F: memory.c +F: include/exec/memory-internal.h +F: exec.c + +SPICE +M: Gerd Hoffmann +S: Supported +F: include/ui/qemu-spice.h +F: ui/spice-*.c +F: audio/spiceaudio.c +F: hw/display/qxl* + +Graphics +M: Gerd Hoffmann +S: Odd Fixes +F: ui/ + +Cocoa graphics +M: Andreas Färber +M: Peter Maydell +S: Odd Fixes +F: ui/cocoa.m + +Main loop +M: Paolo Bonzini +S: Maintained +F: cpus.c +F: main-loop.c +F: qemu-timer.c +F: vl.c + +Human Monitor (HMP) +M: Luiz Capitulino +S: Maintained +F: monitor.c +F: hmp.c +F: hmp-commands.hx +T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp + +Network device layer +M: Stefan Hajnoczi +M: Jason Wang +S: Maintained +F: net/ +T: git git://github.com/stefanha/qemu.git net + +Netmap network backend +M: Luigi Rizzo +M: Giuseppe Lettieri +M: Vincenzo Maffione +W: http://info.iet.unipi.it/~luigi/netmap/ +S: Maintained +F: net/netmap.c + +Network Block Device (NBD) +M: Paolo Bonzini +S: Odd Fixes +F: block/nbd.c +F: nbd.* +F: qemu-nbd.c +T: git git://github.com/bonzini/qemu.git nbd-next + +NUMA +M: Eduardo Habkost +S: Maintained +F: numa.c +F: include/sysemu/numa.h +K: numa|NUMA +K: srat|SRAT +T: git git://github.com/ehabkost/qemu.git numa + +QAPI +M: Markus Armbruster +M: Michael Roth +S: Supported +F: qapi/ +F: tests/qapi-schema/ +T: git git://repo.or.cz/qemu/armbru.git qapi-next + +QAPI Schema +M: Eric Blake +M: Markus Armbruster +S: Supported +F: qapi-schema.json +T: git git://repo.or.cz/qemu/armbru.git qapi-next + +QObject +M: Luiz Capitulino +S: Maintained +F: qobject/ +T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp + +QEMU Guest Agent +M: Michael Roth +S: Maintained +F: qga/ +T: git git://github.com/mdroth/qemu.git qga + +QOM +M: Andreas Färber +S: Supported +T: git git://github.com/afaerber/qemu-cpu.git qom-next +F: include/qom/ +X: include/qom/cpu.h +F: qom/ +X: qom/cpu.c +F: tests/qom-test.c + +QMP +M: Markus Armbruster +S: Supported +F: qmp.c +F: monitor.c +F: qmp-commands.hx +F: docs/qmp/ +F: scripts/qmp/ +T: git git://repo.or.cz/qemu/armbru.git qapi-next + +SLIRP +M: Jan Kiszka +S: Maintained +F: slirp/ +T: git git://git.kiszka.org/qemu.git queues/slirp + +Tracing +M: Stefan Hajnoczi +S: Maintained +F: trace/ +F: scripts/tracetool.py +F: scripts/tracetool/ +F: docs/tracing.txt +T: git git://github.com/stefanha/qemu.git tracing + +Checkpatch +M: Blue Swirl +S: Odd Fixes +F: scripts/checkpatch.pl + +Migration +M: Juan Quintela +M: Amit Shah +S: Maintained +F: include/migration/ +F: migration/ +F: savevm.c +F: arch_init.c +F: scripts/vmstate-static-checker.py +F: tests/vmstate-static-checker-data/ + +Seccomp +M: Eduardo Otubo +S: Supported +F: qemu-seccomp.c +F: include/sysemu/seccomp.h + +Usermode Emulation +------------------ +Overall +M: Riku Voipio +S: Maintained +F: thunk.c +F: user-exec.c + +BSD user +M: Blue Swirl +S: Maintained +F: bsd-user/ + +Linux user +M: Riku Voipio +S: Maintained +F: linux-user/ + +Tiny Code Generator (TCG) +------------------------- +Common code +M: Richard Henderson +S: Maintained +F: tcg/ + +AArch64 target +M: Claudio Fontana +M: Claudio Fontana +S: Maintained +F: tcg/aarch64/ + +ARM target +M: Andrzej Zaborowski +S: Maintained +F: tcg/arm/ + +i386 target +L: qemu-devel@nongnu.org +S: Maintained +F: tcg/i386/ + +IA64 target +M: Aurelien Jarno +S: Maintained +F: tcg/ia64/ + +MIPS target +M: Aurelien Jarno +S: Maintained +F: tcg/mips/ + +PPC +M: Vassili Karpov (malc) +S: Maintained +F: tcg/ppc/ + +PPC64 target +M: Vassili Karpov (malc) +S: Maintained +F: tcg/ppc64/ + +S390 target +M: Alexander Graf +M: Richard Henderson +S: Maintained +F: tcg/s390/ + +SPARC target +M: Blue Swirl +S: Maintained +F: tcg/sparc/ + +TCI target +M: Stefan Weil +S: Maintained +F: tcg/tci/ +F: tci.c + +Stable branches +--------------- +Stable 1.0 +L: qemu-stable@nongnu.org +T: git git://git.qemu-project.org/qemu-stable-1.0.git +S: Orphan + +Stable 0.15 +L: qemu-stable@nongnu.org +M: Andreas Färber +T: git git://git.qemu-project.org/qemu-stable-0.15.git +S: Supported + +Stable 0.14 +L: qemu-stable@nongnu.org +T: git git://git.qemu-project.org/qemu-stable-0.14.git +S: Orphan + +Stable 0.10 +L: qemu-stable@nongnu.org +T: git git://git.qemu-project.org/qemu-stable-0.10.git +S: Orphan + +Block drivers +------------- +VMDK +M: Fam Zheng +L: qemu-block@nongnu.org +S: Supported +F: block/vmdk.c + +RBD +M: Josh Durgin +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: block/rbd.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +Sheepdog +M: Hitoshi Mitake +M: Liu Yuan +M: Jeff Cody +L: qemu-block@nongnu.org +L: sheepdog@lists.wpkg.org +S: Supported +F: block/sheepdog.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +VHDX +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: block/vhdx* +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +VDI +M: Stefan Weil +L: qemu-block@nongnu.org +S: Maintained +F: block/vdi.c + +iSCSI +M: Ronnie Sahlberg +M: Paolo Bonzini +M: Peter Lieven +L: qemu-block@nongnu.org +S: Supported +F: block/iscsi.c + +NFS +M: Jeff Cody +M: Peter Lieven +L: qemu-block@nongnu.org +S: Maintained +F: block/nfs.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +SSH +M: Richard W.M. Jones +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: block/ssh.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +ARCHIPELAGO +M: Chrysostomos Nanakos +M: Jeff Cody +L: qemu-block@nongnu.org +S: Maintained +F: block/archipelago.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +CURL +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: block/curl.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +GLUSTER +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: block/gluster.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +Null Block Driver +M: Fam Zheng +L: qemu-block@nongnu.org +S: Supported +F: block/null.c + +Bootdevice +M: Gonglei +S: Maintained +F: bootdevice.c + +Quorum +M: Alberto Garcia +S: Supported +F: block/quorum.c +L: qemu-block@nongnu.org + +blkverify +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: block/blkverify.c + +bochs +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: block/bochs.c + +cloop +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: block/cloop.c + +dmg +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: block/dmg.c + +parallels +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: block/parallels.c + +qed +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: block/qed.c + +raw +M: Kevin Wolf +L: qemu-block@nongnu.org +S: Supported +F: block/linux-aio.c +F: block/raw-aio.h +F: block/raw-posix.c +F: block/raw-win32.c +F: block/raw_bsd.c +F: block/win32-aio.c + +qcow2 +M: Kevin Wolf +L: qemu-block@nongnu.org +S: Supported +F: block/qcow2* + +qcow +M: Kevin Wolf +L: qemu-block@nongnu.org +S: Supported +F: block/qcow.c + +blkdebug +M: Kevin Wolf +L: qemu-block@nongnu.org +S: Supported +F: block/blkdebug.c + +vpc +M: Kevin Wolf +L: qemu-block@nongnu.org +S: Supported +F: block/vpc.c + +vvfat +M: Kevin Wolf +L: qemu-block@nongnu.org +S: Supported +F: block/vvfat.c + +Image format fuzzer +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Supported +F: tests/image-fuzzer/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d945804 --- /dev/null +++ b/Makefile @@ -0,0 +1,592 @@ +# Makefile for QEMU. + +# Always point to the root of the build tree (needs GNU make). +BUILD_DIR=$(CURDIR) + +# All following code might depend on configuration variables +ifneq ($(wildcard config-host.mak),) +# Put the all: rule here so that config-host.mak can contain dependencies. +all: +include config-host.mak + +# Check that we're not trying to do an out-of-tree build from +# a tree that's been used for an in-tree build. +ifneq ($(realpath $(SRC_PATH)),$(realpath .)) +ifneq ($(wildcard $(SRC_PATH)/config-host.mak),) +$(error This is an out of tree build but your source tree ($(SRC_PATH)) \ +seems to have been used for an in-tree build. You can fix this by running \ +"make distclean && rm -rf *-linux-user *-softmmu" in your source tree) +endif +endif + +CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y) +CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y) +CONFIG_ALL=y +-include config-all-devices.mak +-include config-all-disas.mak + +include $(SRC_PATH)/rules.mak +config-host.mak: $(SRC_PATH)/configure + @echo $@ is out-of-date, running configure + @# TODO: The next lines include code which supports a smooth + @# transition from old configurations without config.status. + @# This code can be removed after QEMU 1.7. + @if test -x config.status; then \ + ./config.status; \ + else \ + sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh; \ + fi +else +config-host.mak: +ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) + @echo "Please call configure before running make!" + @exit 1 +endif +endif + +GENERATED_HEADERS = config-host.h qemu-options.def +GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h +GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c + +GENERATED_HEADERS += trace/generated-events.h +GENERATED_SOURCES += trace/generated-events.c + +GENERATED_HEADERS += trace/generated-tracers.h +ifeq ($(findstring dtrace,$(TRACE_BACKENDS)),dtrace) +GENERATED_HEADERS += trace/generated-tracers-dtrace.h +endif +GENERATED_SOURCES += trace/generated-tracers.c + +GENERATED_HEADERS += trace/generated-tcg-tracers.h + +GENERATED_HEADERS += trace/generated-helpers-wrappers.h +GENERATED_HEADERS += trace/generated-helpers.h +GENERATED_SOURCES += trace/generated-helpers.c + +ifeq ($(findstring ust,$(TRACE_BACKENDS)),ust) +GENERATED_HEADERS += trace/generated-ust-provider.h +GENERATED_SOURCES += trace/generated-ust.c +endif + +# Don't try to regenerate Makefile or configure +# We don't generate any of them +Makefile: ; +configure: ; + +.PHONY: all clean cscope distclean dvi html info install install-doc \ + pdf recurse-all speed test dist + +$(call set-vpath, $(SRC_PATH)) + +LIBS+=-lz $(LIBS_TOOLS) + +HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) + +ifdef BUILD_DOCS +DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt +ifdef CONFIG_LINUX +DOCS+=kvm_stat.1 +endif +ifdef CONFIG_VIRTFS +DOCS+=fsdev/virtfs-proxy-helper.1 +endif +else +DOCS= +endif + +SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) BUILD_DIR=$(BUILD_DIR) +SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) +SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS)) + +ifeq ($(SUBDIR_DEVICES_MAK),) +config-all-devices.mak: + $(call quiet-command,echo '# no devices' > $@," GEN $@") +else +config-all-devices.mak: $(SUBDIR_DEVICES_MAK) + $(call quiet-command, sed -n \ + 's|^\([^=]*\)=\(.*\)$$|\1:=$$(findstring y,$$(\1)\2)|p' \ + $(SUBDIR_DEVICES_MAK) | sort -u > $@, \ + " GEN $@") +endif + +-include $(SUBDIR_DEVICES_MAK_DEP) + +%/config-devices.mak: default-configs/%.mak + $(call quiet-command, \ + $(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp, " GEN $@.tmp") + $(call quiet-command, if test -f $@; then \ + if cmp -s $@.old $@; then \ + mv $@.tmp $@; \ + cp -p $@ $@.old; \ + else \ + if test -f $@.old; then \ + echo "WARNING: $@ (user modified) out of date.";\ + else \ + echo "WARNING: $@ out of date.";\ + fi; \ + echo "Run \"make defconfig\" to regenerate."; \ + rm $@.tmp; \ + fi; \ + else \ + mv $@.tmp $@; \ + cp -p $@ $@.old; \ + fi, " GEN $@"); + +defconfig: + rm -f config-all-devices.mak $(SUBDIR_DEVICES_MAK) + +ifneq ($(wildcard config-host.mak),) +include $(SRC_PATH)/Makefile.objs +endif + +dummy := $(call unnest-vars,, \ + stub-obj-y \ + util-obj-y \ + qga-obj-y \ + qga-vss-dll-obj-y \ + block-obj-y \ + block-obj-m \ + common-obj-y \ + common-obj-m) + +ifneq ($(wildcard config-host.mak),) +include $(SRC_PATH)/tests/Makefile +endif +ifeq ($(CONFIG_SMARTCARD_NSS),y) +include $(SRC_PATH)/libcacard/Makefile +endif + +all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules + +config-host.h: config-host.h-timestamp +config-host.h-timestamp: config-host.mak +qemu-options.def: $(SRC_PATH)/qemu-options.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") + +SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) +SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) + +$(SOFTMMU_SUBDIR_RULES): $(block-obj-y) +$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak + +subdir-%: + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,) + +subdir-pixman: pixman/Makefile + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,) + +pixman/Makefile: $(SRC_PATH)/pixman/configure + (cd pixman; CFLAGS="$(CFLAGS) -fPIC $(extra_cflags) $(extra_ldflags)" $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static) + +$(SRC_PATH)/pixman/configure: + (cd $(SRC_PATH)/pixman; autoreconf -v --install) + +DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_srcdir=$(SRC_PATH)/dtc/libfdt +DTC_CFLAGS=$(CFLAGS) $(QEMU_CFLAGS) +DTC_CPPFLAGS=-I$(BUILD_DIR)/dtc -I$(SRC_PATH)/dtc -I$(SRC_PATH)/dtc/libfdt + +subdir-dtc:dtc/libfdt dtc/tests + $(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) + +dtc/%: + mkdir -p $@ + +$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) + +ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) +romsubdir-%: + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pc-bios/$* V="$(V)" TARGET_DIR="$*/",) + +ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS)) + +recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES) + +$(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc config-host.h | $(BUILD_DIR)/version.lo + $(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<," RC version.o") +$(BUILD_DIR)/version.lo: $(SRC_PATH)/version.rc config-host.h + $(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<," RC version.lo") + +Makefile: $(version-obj-y) $(version-lobj-y) + +###################################################################### +# Build libraries + +libqemustub.a: $(stub-obj-y) +libqemuutil.a: $(util-obj-y) + +block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL +util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' + +###################################################################### + +qemu-img.o: qemu-img-cmds.h + +qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a + +qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o + +fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o libqemuutil.a libqemustub.a +fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap + +qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") + +qemu-ga$(EXESUF): LIBS = $(LIBS_QGA) +qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated + +gen-out-type = $(subst .,-,$(suffix $@)) + +qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py + +qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\ +$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \ + $(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \ + " GEN $@") +qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\ +$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \ + $(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \ + " GEN $@") +qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\ +$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \ + $(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \ + " GEN $@") + +qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ + $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ + $(SRC_PATH)/qapi/event.json + +qapi-types.c qapi-types.h :\ +$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \ + $(gen-out-type) -o "." -b $<, \ + " GEN $@") +qapi-visit.c qapi-visit.h :\ +$(qapi-modules) $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \ + $(gen-out-type) -o "." -b $<, \ + " GEN $@") +qapi-event.c qapi-event.h :\ +$(qapi-modules) $(SRC_PATH)/scripts/qapi-event.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \ + $(gen-out-type) -o "." $<, \ + " GEN $@") +qmp-commands.h qmp-marshal.c :\ +$(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \ + $(gen-out-type) -o "." -m $<, \ + " GEN $@") + +QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h) +$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) + +qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a + $(call LINK, $^) + +clean: +# avoid old build problems by removing potentially incorrect old files + rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h + rm -f qemu-options.def + find . \( -name '*.l[oa]' -o -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} + + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ + rm -f fsdev/*.pod + rm -rf .libs */.libs + rm -f qemu-img-cmds.h + rm -f ui/shader/*-vert.h ui/shader/*-frag.h + @# May not be present in GENERATED_HEADERS + rm -f trace/generated-tracers-dtrace.dtrace* + rm -f trace/generated-tracers-dtrace.h* + rm -f $(foreach f,$(GENERATED_HEADERS),$(f) $(f)-timestamp) + rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp) + rm -rf qapi-generated + rm -rf qga/qapi-generated + for d in $(ALL_SUBDIRS); do \ + if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ + rm -f $$d/qemu-options.def; \ + done + +VERSION ?= $(shell cat VERSION) + +dist: qemu-$(VERSION).tar.bz2 + +qemu-%.tar.bz2: + $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" + +distclean: clean + rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi + rm -f config-all-devices.mak config-all-disas.mak config.status + rm -f po/*.mo tests/qemu-iotests/common.env + rm -f roms/seabios/config.mak roms/vgabios/config.mak + rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps qemu-doc.dvi + rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys + rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp + rm -f qemu-doc.vr + rm -f config.log + rm -f linux-headers/asm + rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr + for d in $(TARGET_DIRS); do \ + rm -rf $$d || exit 1 ; \ + done + rm -Rf .sdk + if test -f pixman/config.log; then $(MAKE) -C pixman distclean; fi + if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi + +KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \ +ar de en-us fi fr-be hr it lv nl pl ru th \ +common de-ch es fo fr-ca hu ja mk nl-be pt sl tr \ +bepo cz + +ifdef INSTALL_BLOBS +BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ +vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \ +acpi-dsdt.aml q35-acpi-dsdt.aml \ +ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ +pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ +pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ +efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ +efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ +qemu-icon.bmp qemu_logo_no_text.svg \ +bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ +multiboot.bin linuxboot.bin kvmvapic.bin \ +s390-zipl.rom \ +s390-ccw.img \ +spapr-rtas.bin slof.bin \ +palcode-clipper \ +u-boot.e500 +else +BLOBS= +endif + +install-doc: $(DOCS) + $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" + $(INSTALL_DATA) qemu-doc.html qemu-tech.html "$(DESTDIR)$(qemu_docdir)" + $(INSTALL_DATA) qmp-commands.txt "$(DESTDIR)$(qemu_docdir)" +ifdef CONFIG_POSIX + $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" + $(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1" +ifneq ($(TOOLS),) + $(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" + $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" + $(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" +endif +endif +ifdef CONFIG_VIRTFS + $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" + $(INSTALL_DATA) fsdev/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1" +endif + +install-datadir: + $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)" + +install-localstatedir: +ifdef CONFIG_POSIX +ifneq (,$(findstring qemu-ga,$(TOOLS))) + $(INSTALL_DIR) "$(DESTDIR)$(qemu_localstatedir)"/run +endif +endif + +install-confdir: + $(INSTALL_DIR) "$(DESTDIR)$(qemu_confdir)" + +install-sysconfig: install-datadir install-confdir + $(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(qemu_confdir)" + +install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig \ +install-datadir install-localstatedir +ifneq ($(TOOLS),) + $(call install-prog,$(TOOLS),$(DESTDIR)$(bindir)) +endif +ifneq ($(CONFIG_MODULES),) + $(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)" + for s in $(modules-m:.mo=$(DSOSUF)); do \ + t="$(DESTDIR)$(qemu_moddir)/$$(echo $$s | tr / -)"; \ + $(INSTALL_LIB) $$s "$$t"; \ + test -z "$(STRIP)" || $(STRIP) "$$t"; \ + done +endif +ifneq ($(HELPERS-y),) + $(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) +endif +ifneq ($(BLOBS),) + set -e; for x in $(BLOBS); do \ + $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ + done +endif +ifeq ($(CONFIG_GTK),y) + $(MAKE) -C po $@ +endif + $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" + set -e; for x in $(KEYMAPS); do \ + $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \ + done + $(INSTALL_DATA) $(SRC_PATH)/trace-events "$(DESTDIR)$(qemu_datadir)/trace-events" + for d in $(TARGET_DIRS); do \ + $(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \ + done + +# various test targets +test speed: all + $(MAKE) -C tests/tcg $@ + +.PHONY: TAGS +TAGS: + rm -f $@ + find "$(SRC_PATH)" -name '*.[hc]' -exec etags --append {} + + +cscope: + rm -f ./cscope.* + find "$(SRC_PATH)" -name "*.[chsS]" -print | sed 's,^\./,,' > ./cscope.files + cscope -b + +# opengl shader programs +ui/shader/%-vert.h: $(SRC_PATH)/ui/shader/%.vert $(SRC_PATH)/scripts/shaderinclude.pl + @mkdir -p $(dir $@) + $(call quiet-command,\ + perl $(SRC_PATH)/scripts/shaderinclude.pl $< > $@,\ + " VERT $@") + +ui/shader/%-frag.h: $(SRC_PATH)/ui/shader/%.frag $(SRC_PATH)/scripts/shaderinclude.pl + @mkdir -p $(dir $@) + $(call quiet-command,\ + perl $(SRC_PATH)/scripts/shaderinclude.pl $< > $@,\ + " FRAG $@") + +ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \ + ui/shader/texture-blit-vert.h ui/shader/texture-blit-frag.h + +# documentation +MAKEINFO=makeinfo +MAKEINFOFLAGS=--no-headers --no-split --number-sections +TEXIFLAG=$(if $(V),,--quiet) +%.dvi: %.texi + $(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<," GEN $@") + +%.html: %.texi + $(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \ + " GEN $@") + +%.info: %.texi + $(call quiet-command,$(MAKEINFO) $< -o $@," GEN $@") + +%.pdf: %.texi + $(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<," GEN $@") + +qemu-options.texi: $(SRC_PATH)/qemu-options.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") + +qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") + +qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@") + +qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") + +qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \ + $(POD2MAN) --section=1 --center=" " --release=" " qemu.pod > $@, \ + " GEN $@") + +qemu-img.1: qemu-img.texi qemu-img-cmds.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \ + $(POD2MAN) --section=1 --center=" " --release=" " qemu-img.pod > $@, \ + " GEN $@") + +fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< fsdev/virtfs-proxy-helper.pod && \ + $(POD2MAN) --section=1 --center=" " --release=" " fsdev/virtfs-proxy-helper.pod > $@, \ + " GEN $@") + +qemu-nbd.8: qemu-nbd.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \ + $(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ + " GEN $@") + +kvm_stat.1: scripts/kvm/kvm_stat.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \ + $(POD2MAN) --section=1 --center=" " --release=" " kvm_stat.pod > $@, \ + " GEN $@") + +dvi: qemu-doc.dvi qemu-tech.dvi +html: qemu-doc.html qemu-tech.html +info: qemu-doc.info qemu-tech.info +pdf: qemu-doc.pdf qemu-tech.pdf + +qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \ + qemu-img.texi qemu-nbd.texi qemu-options.texi \ + qemu-monitor.texi qemu-img-cmds.texi + +ifdef CONFIG_WIN32 + +INSTALLER = qemu-setup-$(VERSION)$(EXESUF) + +nsisflags = -V2 -NOCD + +ifneq ($(wildcard $(SRC_PATH)/dll),) +ifeq ($(ARCH),x86_64) +# 64 bit executables +DLL_PATH = $(SRC_PATH)/dll/w64 +nsisflags += -DW64 +else +# 32 bit executables +DLL_PATH = $(SRC_PATH)/dll/w32 +endif +endif + +.PHONY: installer +installer: $(INSTALLER) + +INSTDIR=/tmp/qemu-nsis + +$(INSTALLER): $(SRC_PATH)/qemu.nsi + $(MAKE) install prefix=${INSTDIR} +ifdef SIGNCODE + (cd ${INSTDIR}; \ + for i in *.exe; do \ + $(SIGNCODE) $${i}; \ + done \ + ) +endif # SIGNCODE + (cd ${INSTDIR}; \ + for i in qemu-system-*.exe; do \ + arch=$${i%.exe}; \ + arch=$${arch#qemu-system-}; \ + echo Section \"$$arch\" Section_$$arch; \ + echo SetOutPath \"\$$INSTDIR\"; \ + echo File \"\$${BINDIR}\\$$i\"; \ + echo SectionEnd; \ + done \ + ) >${INSTDIR}/system-emulations.nsh + makensis $(nsisflags) \ + $(if $(BUILD_DOCS),-DCONFIG_DOCUMENTATION="y") \ + $(if $(CONFIG_GTK),-DCONFIG_GTK="y") \ + -DBINDIR="${INSTDIR}" \ + $(if $(DLL_PATH),-DDLLDIR="$(DLL_PATH)") \ + -DSRCDIR="$(SRC_PATH)" \ + -DOUTFILE="$(INSTALLER)" \ + $(SRC_PATH)/qemu.nsi + rm -r ${INSTDIR} +ifdef SIGNCODE + $(SIGNCODE) $(INSTALLER) +endif # SIGNCODE +endif # CONFIG_WIN + +# Add a dependency on the generated files, so that they are always +# rebuilt before other object files +ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) +Makefile: $(GENERATED_HEADERS) +endif + +# Include automatically generated dependency files +# Dependencies in Makefile.objs files come from our recursive subdir rules +-include $(wildcard *.d tests/*.d) diff --git a/Makefile.objs b/Makefile.objs new file mode 100644 index 0000000..28999d3 --- /dev/null +++ b/Makefile.objs @@ -0,0 +1,110 @@ +####################################################################### +# Common libraries for tools and emulators +stub-obj-y = stubs/ +util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o + +####################################################################### +# block-obj-y is code used by both qemu system emulation and qemu-img + +block-obj-y = async.o thread-pool.o +block-obj-y += nbd.o block.o blockjob.o +block-obj-y += main-loop.o iohandler.o qemu-timer.o +block-obj-$(CONFIG_POSIX) += aio-posix.o +block-obj-$(CONFIG_WIN32) += aio-win32.o +block-obj-y += block/ +block-obj-y += qemu-io-cmds.o + +block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o +block-obj-y += qemu-coroutine-sleep.o +block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o + +block-obj-m = block/ + + +###################################################################### +# smartcard + +libcacard-y += libcacard/cac.o libcacard/event.o +libcacard-y += libcacard/vcard.o libcacard/vreader.o +libcacard-y += libcacard/vcard_emul_nss.o +libcacard-y += libcacard/vcard_emul_type.o +libcacard-y += libcacard/card_7816.o +libcacard-y += libcacard/vcardt.o +libcacard/vcard_emul_nss.o-cflags := $(NSS_CFLAGS) +libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS) + +###################################################################### +# Target independent part of system emulation. The long term path is to +# suppress *all* target specific code in case of system emulation, i.e. a +# single QEMU executable should support all CPUs and machines. + +ifeq ($(CONFIG_SOFTMMU),y) +common-obj-y = blockdev.o blockdev-nbd.o block/ +common-obj-y += iothread.o +common-obj-y += net/ +common-obj-y += qdev-monitor.o device-hotplug.o +common-obj-$(CONFIG_WIN32) += os-win32.o +common-obj-$(CONFIG_POSIX) += os-posix.o + +common-obj-$(CONFIG_LINUX) += fsdev/ + +common-obj-y += migration/ +common-obj-y += qemu-char.o #aio.o +common-obj-y += page_cache.o +common-obj-y += qjson.o + +common-obj-$(CONFIG_SPICE) += spice-qemu-char.o + +common-obj-y += audio/ +common-obj-y += hw/ +common-obj-y += accel.o + +common-obj-y += ui/ +common-obj-y += bt-host.o bt-vhci.o +bt-host.o-cflags := $(BLUEZ_CFLAGS) + +common-obj-y += dma-helpers.o +common-obj-y += vl.o +vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) +common-obj-y += tpm.o + +common-obj-$(CONFIG_SLIRP) += slirp/ + +common-obj-y += backends/ + +common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o + +common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) + +###################################################################### +# qapi + +common-obj-y += qmp-marshal.o +common-obj-y += qmp.o hmp.o +endif + +####################################################################### +# Target-independent parts used in system and user emulation +common-obj-y += qemu-log.o +common-obj-y += tcg-runtime.o +common-obj-y += hw/ +common-obj-y += qom/ +common-obj-y += disas/ + +###################################################################### +# Resource file for Windows executables +version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o +version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo + +###################################################################### +# tracing +util-obj-y += trace/ +target-obj-y += trace/ + +###################################################################### +# guest agent + +# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed +# by libqemuutil.a. These should be moved to a separate .json schema. +qga-obj-y = qga/ +qga-vss-dll-obj-y = qga/ diff --git a/Makefile.target b/Makefile.target new file mode 100644 index 0000000..be01dd3 --- /dev/null +++ b/Makefile.target @@ -0,0 +1,216 @@ +# -*- Mode: makefile -*- + +include ../config-host.mak +include config-target.mak +include config-devices.mak +include $(SRC_PATH)/rules.mak + +$(call set-vpath, $(SRC_PATH)) +ifdef CONFIG_LINUX +QEMU_CFLAGS += -I../linux-headers +endif +QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target-$(TARGET_BASE_ARCH) -DNEED_CPU_H + +QEMU_CFLAGS+=-I$(SRC_PATH)/include + +ifdef CONFIG_USER_ONLY +# user emulator name +QEMU_PROG=qemu-$(TARGET_NAME) +QEMU_PROG_BUILD = $(QEMU_PROG) +else +# system emulator name +QEMU_PROG=qemu-system-$(TARGET_NAME)$(EXESUF) +ifneq (,$(findstring -mwindows,$(libs_softmmu))) +# Terminate program name with a 'w' because the linker builds a windows executable. +QEMU_PROGW=qemu-system-$(TARGET_NAME)w$(EXESUF) +$(QEMU_PROG): $(QEMU_PROGW) + $(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)") +QEMU_PROG_BUILD = $(QEMU_PROGW) +else +QEMU_PROG_BUILD = $(QEMU_PROG) +endif +endif + +PROGS=$(QEMU_PROG) $(QEMU_PROGW) +STPFILES= + +config-target.h: config-target.h-timestamp +config-target.h-timestamp: config-target.mak + +ifdef CONFIG_TRACE_SYSTEMTAP +stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp + +ifdef CONFIG_USER_ONLY +TARGET_TYPE=user +else +TARGET_TYPE=system +endif + +$(QEMU_PROG).stp-installed: $(SRC_PATH)/trace-events + $(call quiet-command,$(TRACETOOL) \ + --format=stap \ + --backends=$(TRACE_BACKENDS) \ + --binary=$(bindir)/$(QEMU_PROG) \ + --target-name=$(TARGET_NAME) \ + --target-type=$(TARGET_TYPE) \ + < $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG).stp-installed") + +$(QEMU_PROG).stp: $(SRC_PATH)/trace-events + $(call quiet-command,$(TRACETOOL) \ + --format=stap \ + --backends=$(TRACE_BACKENDS) \ + --binary=$(realpath .)/$(QEMU_PROG) \ + --target-name=$(TARGET_NAME) \ + --target-type=$(TARGET_TYPE) \ + < $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG).stp") + +$(QEMU_PROG)-simpletrace.stp: $(SRC_PATH)/trace-events + $(call quiet-command,$(TRACETOOL) \ + --format=simpletrace-stap \ + --backends=$(TRACE_BACKENDS) \ + --probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ + < $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") + +else +stap: +endif + +all: $(PROGS) stap + +# Dummy command so that make thinks it has done something + @true + +######################################################### +# cpu emulator library +obj-y = exec.o translate-all.o cpu-exec.o +obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o +obj-$(CONFIG_TCG_INTERPRETER) += tci.o +obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o +obj-y += fpu/softfloat.o +obj-y += target-$(TARGET_BASE_ARCH)/ +obj-y += disas.o +obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o +obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o + +obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o +obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decNumber.o +obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal32.o +obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal64.o +obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal128.o + +######################################################### +# Linux user emulator target + +ifdef CONFIG_LINUX_USER + +QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user + +obj-y += linux-user/ +obj-y += gdbstub.o thunk.o user-exec.o + +endif #CONFIG_LINUX_USER + +######################################################### +# BSD user emulator target + +ifdef CONFIG_BSD_USER + +QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ABI_DIR) \ + -I$(SRC_PATH)/bsd-user/$(HOST_VARIANT_DIR) + +obj-y += bsd-user/ +obj-y += gdbstub.o user-exec.o + +endif #CONFIG_BSD_USER + +######################################################### +# System emulator target +ifdef CONFIG_SOFTMMU +obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o +obj-y += qtest.o bootdevice.o +obj-y += hw/ +obj-$(CONFIG_FDT) += device_tree.o +obj-$(CONFIG_KVM) += kvm-all.o +obj-y += memory.o savevm.o cputlb.o +obj-y += memory_mapping.o +obj-y += dump.o +LIBS := $(libs_softmmu) $(LIBS) + +# xen support +obj-$(CONFIG_XEN) += xen-common.o +obj-$(CONFIG_XEN_I386) += xen-hvm.o xen-mapcache.o +obj-$(call lnot,$(CONFIG_XEN)) += xen-common-stub.o +obj-$(call lnot,$(CONFIG_XEN_I386)) += xen-hvm-stub.o + +# Hardware support +ifeq ($(TARGET_NAME), sparc64) +obj-y += hw/sparc64/ +else +obj-y += hw/$(TARGET_BASE_ARCH)/ +endif + +GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h + +endif # CONFIG_SOFTMMU + +# Workaround for http://gcc.gnu.org/PR55489, see configure. +%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) + +dummy := $(call unnest-vars,,obj-y) +all-obj-y := $(obj-y) + +target-obj-y := +block-obj-y := +common-obj-y := +include $(SRC_PATH)/Makefile.objs +dummy := $(call unnest-vars,,target-obj-y) +target-obj-y-save := $(target-obj-y) +dummy := $(call unnest-vars,.., \ + block-obj-y \ + block-obj-m \ + common-obj-y \ + common-obj-m) +target-obj-y := $(target-obj-y-save) +all-obj-y += $(common-obj-y) +all-obj-y += $(target-obj-y) +all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) + +$(QEMU_PROG_BUILD): config-devices.mak + +# build either PROG or PROGW +$(QEMU_PROG_BUILD): $(all-obj-y) ../libqemuutil.a ../libqemustub.a + $(call LINK, $(filter-out %.mak, $^)) +ifdef CONFIG_DARWIN + $(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@," REZ $(TARGET_DIR)$@") + $(call quiet-command,SetFile -a C $@," SETFILE $(TARGET_DIR)$@") +endif + +gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh + $(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@") + +hmp-commands.h: $(SRC_PATH)/hmp-commands.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") + +qmp-commands-old.h: $(SRC_PATH)/qmp-commands.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") + +clean: + rm -f *.a *~ $(PROGS) + rm -f $(shell find . -name '*.[od]') + rm -f hmp-commands.h qmp-commands-old.h gdbstub-xml.c +ifdef CONFIG_TRACE_SYSTEMTAP + rm -f *.stp +endif + +install: all +ifneq ($(PROGS),) + $(call install-prog,$(PROGS),$(DESTDIR)$(bindir)) +endif +ifdef CONFIG_TRACE_SYSTEMTAP + $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" + $(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" + $(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" +endif + +GENERATED_HEADERS += config-target.h +Makefile: $(GENERATED_HEADERS) diff --git a/README b/README new file mode 100644 index 0000000..c7c990d --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +Read the documentation in qemu-doc.html or on http://wiki.qemu-project.org + +- QEMU team diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..58e1f4f --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.3.50 diff --git a/accel.c b/accel.c new file mode 100644 index 0000000..74e41da --- /dev/null +++ b/accel.c @@ -0,0 +1,157 @@ +/* + * QEMU System Emulator, accelerator interfaces + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysemu/accel.h" +#include "hw/boards.h" +#include "qemu-common.h" +#include "sysemu/arch_init.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "sysemu/qtest.h" +#include "hw/xen/xen.h" +#include "qom/object.h" +#include "hw/boards.h" + +int tcg_tb_size; +static bool tcg_allowed = true; + +static int tcg_init(MachineState *ms) +{ + tcg_exec_init(tcg_tb_size * 1024 * 1024); + return 0; +} + +static const TypeInfo accel_type = { + .name = TYPE_ACCEL, + .parent = TYPE_OBJECT, + .class_size = sizeof(AccelClass), + .instance_size = sizeof(AccelState), +}; + +/* Lookup AccelClass from opt_name. Returns NULL if not found */ +static AccelClass *accel_find(const char *opt_name) +{ + char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); + AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name)); + g_free(class_name); + return ac; +} + +static int accel_init_machine(AccelClass *acc, MachineState *ms) +{ + ObjectClass *oc = OBJECT_CLASS(acc); + const char *cname = object_class_get_name(oc); + AccelState *accel = ACCEL(object_new(cname)); + int ret; + ms->accelerator = accel; + *(acc->allowed) = true; + ret = acc->init_machine(ms); + if (ret < 0) { + ms->accelerator = NULL; + *(acc->allowed) = false; + object_unref(OBJECT(accel)); + } + return ret; +} + +int configure_accelerator(MachineState *ms) +{ + const char *p; + char buf[10]; + int ret; + bool accel_initialised = false; + bool init_failed = false; + AccelClass *acc = NULL; + + p = qemu_opt_get(qemu_get_machine_opts(), "accel"); + if (p == NULL) { + /* Use the default "accelerator", tcg */ + p = "tcg"; + } + + while (!accel_initialised && *p != '\0') { + if (*p == ':') { + p++; + } + p = get_opt_name(buf, sizeof(buf), p, ':'); + acc = accel_find(buf); + if (!acc) { + fprintf(stderr, "\"%s\" accelerator not found.\n", buf); + continue; + } + if (acc->available && !acc->available()) { + printf("%s not supported for this target\n", + acc->name); + continue; + } + ret = accel_init_machine(acc, ms); + if (ret < 0) { + init_failed = true; + fprintf(stderr, "failed to initialize %s: %s\n", + acc->name, + strerror(-ret)); + } else { + accel_initialised = true; + } + } + + if (!accel_initialised) { + if (!init_failed) { + fprintf(stderr, "No accelerator found!\n"); + } + exit(1); + } + + if (init_failed) { + fprintf(stderr, "Back to %s accelerator.\n", acc->name); + } + + return !accel_initialised; +} + + +static void tcg_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "tcg"; + ac->init_machine = tcg_init; + ac->allowed = &tcg_allowed; +} + +#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg") + +static const TypeInfo tcg_accel_type = { + .name = TYPE_TCG_ACCEL, + .parent = TYPE_ACCEL, + .class_init = tcg_accel_class_init, +}; + +static void register_accel_types(void) +{ + type_register_static(&accel_type); + type_register_static(&tcg_accel_type); +} + +type_init(register_accel_types); diff --git a/aio-posix.c b/aio-posix.c new file mode 100644 index 0000000..4abec38 --- /dev/null +++ b/aio-posix.c @@ -0,0 +1,299 @@ +/* + * QEMU aio implementation + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu-common.h" +#include "block/block.h" +#include "qemu/queue.h" +#include "qemu/sockets.h" + +struct AioHandler +{ + GPollFD pfd; + IOHandler *io_read; + IOHandler *io_write; + int deleted; + void *opaque; + QLIST_ENTRY(AioHandler) node; +}; + +static AioHandler *find_aio_handler(AioContext *ctx, int fd) +{ + AioHandler *node; + + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (node->pfd.fd == fd) + if (!node->deleted) + return node; + } + + return NULL; +} + +void aio_set_fd_handler(AioContext *ctx, + int fd, + IOHandler *io_read, + IOHandler *io_write, + void *opaque) +{ + AioHandler *node; + + node = find_aio_handler(ctx, fd); + + /* Are we deleting the fd handler? */ + if (!io_read && !io_write) { + if (node) { + g_source_remove_poll(&ctx->source, &node->pfd); + + /* If the lock is held, just mark the node as deleted */ + if (ctx->walking_handlers) { + node->deleted = 1; + node->pfd.revents = 0; + } else { + /* Otherwise, delete it for real. We can't just mark it as + * deleted because deleted nodes are only cleaned up after + * releasing the walking_handlers lock. + */ + QLIST_REMOVE(node, node); + g_free(node); + } + } + } else { + if (node == NULL) { + /* Alloc and insert if it's not already there */ + node = g_new0(AioHandler, 1); + node->pfd.fd = fd; + QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); + + g_source_add_poll(&ctx->source, &node->pfd); + } + /* Update handler with latest information */ + node->io_read = io_read; + node->io_write = io_write; + node->opaque = opaque; + + node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0); + node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0); + } + + aio_notify(ctx); +} + +void aio_set_event_notifier(AioContext *ctx, + EventNotifier *notifier, + EventNotifierHandler *io_read) +{ + aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), + (IOHandler *)io_read, NULL, notifier); +} + +bool aio_prepare(AioContext *ctx) +{ + return false; +} + +bool aio_pending(AioContext *ctx) +{ + AioHandler *node; + + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + int revents; + + revents = node->pfd.revents & node->pfd.events; + if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) { + return true; + } + if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) { + return true; + } + } + + return false; +} + +bool aio_dispatch(AioContext *ctx) +{ + AioHandler *node; + bool progress = false; + + /* + * If there are callbacks left that have been queued, we need to call them. + * Do not call select in this case, because it is possible that the caller + * does not need a complete flush (as is the case for aio_poll loops). + */ + if (aio_bh_poll(ctx)) { + progress = true; + } + + /* + * We have to walk very carefully in case aio_set_fd_handler is + * called while we're walking. + */ + node = QLIST_FIRST(&ctx->aio_handlers); + while (node) { + AioHandler *tmp; + int revents; + + ctx->walking_handlers++; + + revents = node->pfd.revents & node->pfd.events; + node->pfd.revents = 0; + + if (!node->deleted && + (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) && + node->io_read) { + node->io_read(node->opaque); + + /* aio_notify() does not count as progress */ + if (node->opaque != &ctx->notifier) { + progress = true; + } + } + if (!node->deleted && + (revents & (G_IO_OUT | G_IO_ERR)) && + node->io_write) { + node->io_write(node->opaque); + progress = true; + } + + tmp = node; + node = QLIST_NEXT(node, node); + + ctx->walking_handlers--; + + if (!ctx->walking_handlers && tmp->deleted) { + QLIST_REMOVE(tmp, node); + g_free(tmp); + } + } + + /* Run our timers */ + progress |= timerlistgroup_run_timers(&ctx->tlg); + + return progress; +} + +/* These thread-local variables are used only in a small part of aio_poll + * around the call to the poll() system call. In particular they are not + * used while aio_poll is performing callbacks, which makes it much easier + * to think about reentrancy! + * + * Stack-allocated arrays would be perfect but they have size limitations; + * heap allocation is expensive enough that we want to reuse arrays across + * calls to aio_poll(). And because poll() has to be called without holding + * any lock, the arrays cannot be stored in AioContext. Thread-local data + * has none of the disadvantages of these three options. + */ +static __thread GPollFD *pollfds; +static __thread AioHandler **nodes; +static __thread unsigned npfd, nalloc; +static __thread Notifier pollfds_cleanup_notifier; + +static void pollfds_cleanup(Notifier *n, void *unused) +{ + g_assert(npfd == 0); + g_free(pollfds); + g_free(nodes); + nalloc = 0; +} + +static void add_pollfd(AioHandler *node) +{ + if (npfd == nalloc) { + if (nalloc == 0) { + pollfds_cleanup_notifier.notify = pollfds_cleanup; + qemu_thread_atexit_add(&pollfds_cleanup_notifier); + nalloc = 8; + } else { + g_assert(nalloc <= INT_MAX); + nalloc *= 2; + } + pollfds = g_renew(GPollFD, pollfds, nalloc); + nodes = g_renew(AioHandler *, nodes, nalloc); + } + nodes[npfd] = node; + pollfds[npfd] = (GPollFD) { + .fd = node->pfd.fd, + .events = node->pfd.events, + }; + npfd++; +} + +bool aio_poll(AioContext *ctx, bool blocking) +{ + AioHandler *node; + bool was_dispatching; + int i, ret; + bool progress; + int64_t timeout; + + aio_context_acquire(ctx); + was_dispatching = ctx->dispatching; + progress = false; + + /* aio_notify can avoid the expensive event_notifier_set if + * everything (file descriptors, bottom halves, timers) will + * be re-evaluated before the next blocking poll(). This is + * already true when aio_poll is called with blocking == false; + * if blocking == true, it is only true after poll() returns. + * + * If we're in a nested event loop, ctx->dispatching might be true. + * In that case we can restore it just before returning, but we + * have to clear it now. + */ + aio_set_dispatching(ctx, !blocking); + + ctx->walking_handlers++; + + assert(npfd == 0); + + /* fill pollfds */ + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (!node->deleted && node->pfd.events) { + add_pollfd(node); + } + } + + timeout = blocking ? aio_compute_timeout(ctx) : 0; + + /* wait until next event */ + if (timeout) { + aio_context_release(ctx); + } + ret = qemu_poll_ns((GPollFD *)pollfds, npfd, timeout); + if (timeout) { + aio_context_acquire(ctx); + } + + /* if we have any readable fds, dispatch event */ + if (ret > 0) { + for (i = 0; i < npfd; i++) { + nodes[i]->pfd.revents = pollfds[i].revents; + } + } + + npfd = 0; + ctx->walking_handlers--; + + /* Run dispatch even if there were no readable fds to run timers */ + aio_set_dispatching(ctx, true); + if (aio_dispatch(ctx)) { + progress = true; + } + + aio_set_dispatching(ctx, was_dispatching); + aio_context_release(ctx); + + return progress; +} diff --git a/aio-win32.c b/aio-win32.c new file mode 100644 index 0000000..233d8f5 --- /dev/null +++ b/aio-win32.c @@ -0,0 +1,361 @@ +/* + * QEMU aio implementation + * + * Copyright IBM Corp., 2008 + * Copyright Red Hat Inc., 2012 + * + * Authors: + * Anthony Liguori + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu-common.h" +#include "block/block.h" +#include "qemu/queue.h" +#include "qemu/sockets.h" + +struct AioHandler { + EventNotifier *e; + IOHandler *io_read; + IOHandler *io_write; + EventNotifierHandler *io_notify; + GPollFD pfd; + int deleted; + void *opaque; + QLIST_ENTRY(AioHandler) node; +}; + +void aio_set_fd_handler(AioContext *ctx, + int fd, + IOHandler *io_read, + IOHandler *io_write, + void *opaque) +{ + /* fd is a SOCKET in our case */ + AioHandler *node; + + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (node->pfd.fd == fd && !node->deleted) { + break; + } + } + + /* Are we deleting the fd handler? */ + if (!io_read && !io_write) { + if (node) { + /* If the lock is held, just mark the node as deleted */ + if (ctx->walking_handlers) { + node->deleted = 1; + node->pfd.revents = 0; + } else { + /* Otherwise, delete it for real. We can't just mark it as + * deleted because deleted nodes are only cleaned up after + * releasing the walking_handlers lock. + */ + QLIST_REMOVE(node, node); + g_free(node); + } + } + } else { + HANDLE event; + + if (node == NULL) { + /* Alloc and insert if it's not already there */ + node = g_new0(AioHandler, 1); + node->pfd.fd = fd; + QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); + } + + node->pfd.events = 0; + if (node->io_read) { + node->pfd.events |= G_IO_IN; + } + if (node->io_write) { + node->pfd.events |= G_IO_OUT; + } + + node->e = &ctx->notifier; + + /* Update handler with latest information */ + node->opaque = opaque; + node->io_read = io_read; + node->io_write = io_write; + + event = event_notifier_get_handle(&ctx->notifier); + WSAEventSelect(node->pfd.fd, event, + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB); + } + + aio_notify(ctx); +} + +void aio_set_event_notifier(AioContext *ctx, + EventNotifier *e, + EventNotifierHandler *io_notify) +{ + AioHandler *node; + + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (node->e == e && !node->deleted) { + break; + } + } + + /* Are we deleting the fd handler? */ + if (!io_notify) { + if (node) { + g_source_remove_poll(&ctx->source, &node->pfd); + + /* If the lock is held, just mark the node as deleted */ + if (ctx->walking_handlers) { + node->deleted = 1; + node->pfd.revents = 0; + } else { + /* Otherwise, delete it for real. We can't just mark it as + * deleted because deleted nodes are only cleaned up after + * releasing the walking_handlers lock. + */ + QLIST_REMOVE(node, node); + g_free(node); + } + } + } else { + if (node == NULL) { + /* Alloc and insert if it's not already there */ + node = g_new0(AioHandler, 1); + node->e = e; + node->pfd.fd = (uintptr_t)event_notifier_get_handle(e); + node->pfd.events = G_IO_IN; + QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); + + g_source_add_poll(&ctx->source, &node->pfd); + } + /* Update handler with latest information */ + node->io_notify = io_notify; + } + + aio_notify(ctx); +} + +bool aio_prepare(AioContext *ctx) +{ + static struct timeval tv0; + AioHandler *node; + bool have_select_revents = false; + fd_set rfds, wfds; + + /* fill fd sets */ + FD_ZERO(&rfds); + FD_ZERO(&wfds); + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (node->io_read) { + FD_SET ((SOCKET)node->pfd.fd, &rfds); + } + if (node->io_write) { + FD_SET ((SOCKET)node->pfd.fd, &wfds); + } + } + + if (select(0, &rfds, &wfds, NULL, &tv0) > 0) { + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + node->pfd.revents = 0; + if (FD_ISSET(node->pfd.fd, &rfds)) { + node->pfd.revents |= G_IO_IN; + have_select_revents = true; + } + + if (FD_ISSET(node->pfd.fd, &wfds)) { + node->pfd.revents |= G_IO_OUT; + have_select_revents = true; + } + } + } + + return have_select_revents; +} + +bool aio_pending(AioContext *ctx) +{ + AioHandler *node; + + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (node->pfd.revents && node->io_notify) { + return true; + } + + if ((node->pfd.revents & G_IO_IN) && node->io_read) { + return true; + } + if ((node->pfd.revents & G_IO_OUT) && node->io_write) { + return true; + } + } + + return false; +} + +static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event) +{ + AioHandler *node; + bool progress = false; + + /* + * We have to walk very carefully in case aio_set_fd_handler is + * called while we're walking. + */ + node = QLIST_FIRST(&ctx->aio_handlers); + while (node) { + AioHandler *tmp; + int revents = node->pfd.revents; + + ctx->walking_handlers++; + + if (!node->deleted && + (revents || event_notifier_get_handle(node->e) == event) && + node->io_notify) { + node->pfd.revents = 0; + node->io_notify(node->e); + + /* aio_notify() does not count as progress */ + if (node->e != &ctx->notifier) { + progress = true; + } + } + + if (!node->deleted && + (node->io_read || node->io_write)) { + node->pfd.revents = 0; + if ((revents & G_IO_IN) && node->io_read) { + node->io_read(node->opaque); + progress = true; + } + if ((revents & G_IO_OUT) && node->io_write) { + node->io_write(node->opaque); + progress = true; + } + + /* if the next select() will return an event, we have progressed */ + if (event == event_notifier_get_handle(&ctx->notifier)) { + WSANETWORKEVENTS ev; + WSAEnumNetworkEvents(node->pfd.fd, event, &ev); + if (ev.lNetworkEvents) { + progress = true; + } + } + } + + tmp = node; + node = QLIST_NEXT(node, node); + + ctx->walking_handlers--; + + if (!ctx->walking_handlers && tmp->deleted) { + QLIST_REMOVE(tmp, node); + g_free(tmp); + } + } + + return progress; +} + +bool aio_dispatch(AioContext *ctx) +{ + bool progress; + + progress = aio_bh_poll(ctx); + progress |= aio_dispatch_handlers(ctx, INVALID_HANDLE_VALUE); + progress |= timerlistgroup_run_timers(&ctx->tlg); + return progress; +} + +bool aio_poll(AioContext *ctx, bool blocking) +{ + AioHandler *node; + HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; + bool was_dispatching, progress, have_select_revents, first; + int count; + int timeout; + + aio_context_acquire(ctx); + have_select_revents = aio_prepare(ctx); + if (have_select_revents) { + blocking = false; + } + + was_dispatching = ctx->dispatching; + progress = false; + + /* aio_notify can avoid the expensive event_notifier_set if + * everything (file descriptors, bottom halves, timers) will + * be re-evaluated before the next blocking poll(). This is + * already true when aio_poll is called with blocking == false; + * if blocking == true, it is only true after poll() returns. + * + * If we're in a nested event loop, ctx->dispatching might be true. + * In that case we can restore it just before returning, but we + * have to clear it now. + */ + aio_set_dispatching(ctx, !blocking); + + ctx->walking_handlers++; + + /* fill fd sets */ + count = 0; + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (!node->deleted && node->io_notify) { + events[count++] = event_notifier_get_handle(node->e); + } + } + + ctx->walking_handlers--; + first = true; + + /* wait until next event */ + while (count > 0) { + HANDLE event; + int ret; + + timeout = blocking + ? qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)) : 0; + if (timeout) { + aio_context_release(ctx); + } + ret = WaitForMultipleObjects(count, events, FALSE, timeout); + if (timeout) { + aio_context_acquire(ctx); + } + aio_set_dispatching(ctx, true); + + if (first && aio_bh_poll(ctx)) { + progress = true; + } + first = false; + + /* if we have any signaled events, dispatch event */ + event = NULL; + if ((DWORD) (ret - WAIT_OBJECT_0) < count) { + event = events[ret - WAIT_OBJECT_0]; + events[ret - WAIT_OBJECT_0] = events[--count]; + } else if (!have_select_revents) { + break; + } + + have_select_revents = false; + blocking = false; + + progress |= aio_dispatch_handlers(ctx, event); + } + + progress |= timerlistgroup_run_timers(&ctx->tlg); + + aio_set_dispatching(ctx, was_dispatching); + aio_context_release(ctx); + return progress; +} diff --git a/arch_init.c b/arch_init.c new file mode 100644 index 0000000..5502371 --- /dev/null +++ b/arch_init.c @@ -0,0 +1,2015 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#endif +#include "config.h" +#include "monitor/monitor.h" +#include "sysemu/sysemu.h" +#include "qemu/bitops.h" +#include "qemu/bitmap.h" +#include "sysemu/arch_init.h" +#include "audio/audio.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/audio/audio.h" +#include "sysemu/kvm.h" +#include "migration/migration.h" +#include "hw/i386/smbios.h" +#include "exec/address-spaces.h" +#include "hw/audio/pcspk.h" +#include "migration/page_cache.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "qmp-commands.h" +#include "trace.h" +#include "exec/cpu-all.h" +#include "exec/ram_addr.h" +#include "hw/acpi/acpi.h" +#include "qemu/host-utils.h" +#include "qemu/rcu_queue.h" + +#ifdef DEBUG_ARCH_INIT +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "arch_init: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +#ifdef TARGET_SPARC +int graphic_width = 1024; +int graphic_height = 768; +int graphic_depth = 8; +#else +int graphic_width = 800; +int graphic_height = 600; +int graphic_depth = 32; +#endif + + +#if defined(TARGET_ALPHA) +#define QEMU_ARCH QEMU_ARCH_ALPHA +#elif defined(TARGET_ARM) +#define QEMU_ARCH QEMU_ARCH_ARM +#elif defined(TARGET_CRIS) +#define QEMU_ARCH QEMU_ARCH_CRIS +#elif defined(TARGET_I386) +#define QEMU_ARCH QEMU_ARCH_I386 +#elif defined(TARGET_M68K) +#define QEMU_ARCH QEMU_ARCH_M68K +#elif defined(TARGET_LM32) +#define QEMU_ARCH QEMU_ARCH_LM32 +#elif defined(TARGET_MICROBLAZE) +#define QEMU_ARCH QEMU_ARCH_MICROBLAZE +#elif defined(TARGET_MIPS) +#define QEMU_ARCH QEMU_ARCH_MIPS +#elif defined(TARGET_MOXIE) +#define QEMU_ARCH QEMU_ARCH_MOXIE +#elif defined(TARGET_OPENRISC) +#define QEMU_ARCH QEMU_ARCH_OPENRISC +#elif defined(TARGET_PPC) +#define QEMU_ARCH QEMU_ARCH_PPC +#elif defined(TARGET_S390X) +#define QEMU_ARCH QEMU_ARCH_S390X +#elif defined(TARGET_SH4) +#define QEMU_ARCH QEMU_ARCH_SH4 +#elif defined(TARGET_SPARC) +#define QEMU_ARCH QEMU_ARCH_SPARC +#elif defined(TARGET_XTENSA) +#define QEMU_ARCH QEMU_ARCH_XTENSA +#elif defined(TARGET_UNICORE32) +#define QEMU_ARCH QEMU_ARCH_UNICORE32 +#elif defined(TARGET_TRICORE) +#define QEMU_ARCH QEMU_ARCH_TRICORE +#endif + +const uint32_t arch_type = QEMU_ARCH; +static bool mig_throttle_on; +static int dirty_rate_high_cnt; +static void check_guest_throttling(void); + +static uint64_t bitmap_sync_count; + +/***********************************************************/ +/* ram save/restore */ + +#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */ +#define RAM_SAVE_FLAG_COMPRESS 0x02 +#define RAM_SAVE_FLAG_MEM_SIZE 0x04 +#define RAM_SAVE_FLAG_PAGE 0x08 +#define RAM_SAVE_FLAG_EOS 0x10 +#define RAM_SAVE_FLAG_CONTINUE 0x20 +#define RAM_SAVE_FLAG_XBZRLE 0x40 +/* 0x80 is reserved in migration.h start with 0x100 next */ +#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 + +static struct defconfig_file { + const char *filename; + /* Indicates it is an user config file (disabled by -no-user-config) */ + bool userconfig; +} default_config_files[] = { + { CONFIG_QEMU_CONFDIR "/qemu.conf", true }, + { CONFIG_QEMU_CONFDIR "/target-" TARGET_NAME ".conf", true }, + { NULL }, /* end of list */ +}; + +static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE]; + +////static __u64 save_ptr[512] = {0}; + +#define PAGEMAP_LENGTH 8 +unsigned long get_pfn_of_address(void *addr) { + // Open the pagemap file for the current process + FILE *pagemap = fopen("/proc/self/pagemap", "rb"); + unsigned long page_frame_number = 0; + + // Seek to the page that the buffer is on + unsigned long offset = (unsigned long)addr / getpagesize() * PAGEMAP_LENGTH; + if(fseek(pagemap, (unsigned long)offset, SEEK_SET) != 0) { + fprintf(stderr, "Failed to seek pagemap to proper location\n"); + exit(1); + } + + // The page frame number is in bits 0-54 so read the first 7 bytes and clear the 55th bit + fread(&page_frame_number, 1, PAGEMAP_LENGTH-1, pagemap); + + page_frame_number &= 0x7FFFFFFFFFFFFF; + + fclose(pagemap); + + return page_frame_number; +} + +int qemu_read_default_config_files(bool userconfig) +{ + int ret; + struct defconfig_file *f; + + for (f = default_config_files; f->filename; f++) { + if (!userconfig && f->userconfig) { + continue; + } + ret = qemu_read_config_file(f->filename); + if (ret < 0 && ret != -ENOENT) { + return ret; + } + } + + return 0; +} + +static inline bool is_zero_range(uint8_t *p, uint64_t size) +{ + return buffer_find_nonzero_offset(p, size) == size; +} + +/* struct contains XBZRLE cache and a static page + used by the compression */ +static struct { + /* buffer used for XBZRLE encoding */ + uint8_t *encoded_buf; + /* buffer for storing page content */ + uint8_t *current_buf; + /* Cache for XBZRLE, Protected by lock. */ + PageCache *cache; + QemuMutex lock; +} XBZRLE; + +/* buffer used for XBZRLE decoding */ +static uint8_t *xbzrle_decoded_buf; + +static void XBZRLE_cache_lock(void) +{ + if (migrate_use_xbzrle()) + qemu_mutex_lock(&XBZRLE.lock); +} + +static void XBZRLE_cache_unlock(void) +{ + if (migrate_use_xbzrle()) + qemu_mutex_unlock(&XBZRLE.lock); +} + +/* + * called from qmp_migrate_set_cache_size in main thread, possibly while + * a migration is in progress. + * A running migration maybe using the cache and might finish during this + * call, hence changes to the cache are protected by XBZRLE.lock(). + */ +int64_t xbzrle_cache_resize(int64_t new_size) +{ + PageCache *new_cache; + int64_t ret; + + if (new_size < TARGET_PAGE_SIZE) { + return -1; + } + + XBZRLE_cache_lock(); + + if (XBZRLE.cache != NULL) { + if (pow2floor(new_size) == migrate_xbzrle_cache_size()) { + goto out_new_size; + } + new_cache = cache_init(new_size / TARGET_PAGE_SIZE, + TARGET_PAGE_SIZE); + if (!new_cache) { + error_report("Error creating cache"); + ret = -1; + goto out; + } + + cache_fini(XBZRLE.cache); + XBZRLE.cache = new_cache; + } + +out_new_size: + ret = pow2floor(new_size); +out: + XBZRLE_cache_unlock(); + return ret; +} + +/* accounting for migration statistics */ +typedef struct AccountingInfo { + uint64_t dup_pages; + uint64_t skipped_pages; + uint64_t norm_pages; + uint64_t iterations; + uint64_t xbzrle_bytes; + uint64_t xbzrle_pages; + uint64_t xbzrle_cache_miss; + double xbzrle_cache_miss_rate; + uint64_t xbzrle_overflows; +} AccountingInfo; + +static AccountingInfo acct_info; + +static void acct_clear(void) +{ + memset(&acct_info, 0, sizeof(acct_info)); +} + +uint64_t dup_mig_bytes_transferred(void) +{ + return acct_info.dup_pages * TARGET_PAGE_SIZE; +} + +uint64_t dup_mig_pages_transferred(void) +{ + return acct_info.dup_pages; +} + +uint64_t skipped_mig_bytes_transferred(void) +{ + return acct_info.skipped_pages * TARGET_PAGE_SIZE; +} + +uint64_t skipped_mig_pages_transferred(void) +{ + return acct_info.skipped_pages; +} + +uint64_t norm_mig_bytes_transferred(void) +{ + return acct_info.norm_pages * TARGET_PAGE_SIZE; +} + +uint64_t norm_mig_pages_transferred(void) +{ + return acct_info.norm_pages; +} + +uint64_t xbzrle_mig_bytes_transferred(void) +{ + return acct_info.xbzrle_bytes; +} + +uint64_t xbzrle_mig_pages_transferred(void) +{ + return acct_info.xbzrle_pages; +} + +uint64_t xbzrle_mig_pages_cache_miss(void) +{ + return acct_info.xbzrle_cache_miss; +} + +double xbzrle_mig_cache_miss_rate(void) +{ + return acct_info.xbzrle_cache_miss_rate; +} + +uint64_t xbzrle_mig_pages_overflow(void) +{ + return acct_info.xbzrle_overflows; +} + +/* This is the last block that we have visited serching for dirty pages + */ +static RAMBlock *last_seen_block; +/* This is the last block from where we have sent data */ +static RAMBlock *last_sent_block; +static ram_addr_t last_offset; +static unsigned long *migration_bitmap; +static uint64_t migration_dirty_pages; +static uint32_t last_version; +static bool ram_bulk_stage; + +struct CompressParam { + bool start; + bool done; + QEMUFile *file; + QemuMutex mutex; + QemuCond cond; + RAMBlock *block; + ram_addr_t offset; +}; +typedef struct CompressParam CompressParam; + +struct DecompressParam { + bool start; + QemuMutex mutex; + QemuCond cond; + void *des; + uint8 *compbuf; + int len; +}; +typedef struct DecompressParam DecompressParam; + +static CompressParam *comp_param; +static QemuThread *compress_threads; +/* comp_done_cond is used to wake up the migration thread when + * one of the compression threads has finished the compression. + * comp_done_lock is used to co-work with comp_done_cond. + */ +static QemuMutex *comp_done_lock; +static QemuCond *comp_done_cond; +/* The empty QEMUFileOps will be used by file in CompressParam */ +static const QEMUFileOps empty_ops = { }; + +static bool compression_switch; +static bool quit_comp_thread; +static bool quit_decomp_thread; +static DecompressParam *decomp_param; +static QemuThread *decompress_threads; +static uint8_t *compressed_data_buf; + +static int do_compress_ram_page(CompressParam *param); + +static void *do_data_compress(void *opaque) +{ + CompressParam *param = opaque; + + while (!quit_comp_thread) { + qemu_mutex_lock(¶m->mutex); + /* Re-check the quit_comp_thread in case of + * terminate_compression_threads is called just before + * qemu_mutex_lock(¶m->mutex) and after + * while(!quit_comp_thread), re-check it here can make + * sure the compression thread terminate as expected. + */ + while (!param->start && !quit_comp_thread) { + qemu_cond_wait(¶m->cond, ¶m->mutex); + } + if (!quit_comp_thread) { + do_compress_ram_page(param); + } + param->start = false; + qemu_mutex_unlock(¶m->mutex); + + qemu_mutex_lock(comp_done_lock); + param->done = true; + qemu_cond_signal(comp_done_cond); + qemu_mutex_unlock(comp_done_lock); + } + + return NULL; +} + +static inline void terminate_compression_threads(void) +{ + int idx, thread_count; + + thread_count = migrate_compress_threads(); + quit_comp_thread = true; + for (idx = 0; idx < thread_count; idx++) { + qemu_mutex_lock(&comp_param[idx].mutex); + qemu_cond_signal(&comp_param[idx].cond); + qemu_mutex_unlock(&comp_param[idx].mutex); + } +} + +void migrate_compress_threads_join(void) +{ + int i, thread_count; + + if (!migrate_use_compression()) { + return; + } + terminate_compression_threads(); + thread_count = migrate_compress_threads(); + for (i = 0; i < thread_count; i++) { + qemu_thread_join(compress_threads + i); + qemu_fclose(comp_param[i].file); + qemu_mutex_destroy(&comp_param[i].mutex); + qemu_cond_destroy(&comp_param[i].cond); + } + qemu_mutex_destroy(comp_done_lock); + qemu_cond_destroy(comp_done_cond); + g_free(compress_threads); + g_free(comp_param); + g_free(comp_done_cond); + g_free(comp_done_lock); + compress_threads = NULL; + comp_param = NULL; + comp_done_cond = NULL; + comp_done_lock = NULL; +} + +void migrate_compress_threads_create(void) +{ + int i, thread_count; + + if (!migrate_use_compression()) { + return; + } + quit_comp_thread = false; + compression_switch = true; + thread_count = migrate_compress_threads(); + compress_threads = g_new0(QemuThread, thread_count); + comp_param = g_new0(CompressParam, thread_count); + comp_done_cond = g_new0(QemuCond, 1); + comp_done_lock = g_new0(QemuMutex, 1); + qemu_cond_init(comp_done_cond); + qemu_mutex_init(comp_done_lock); + for (i = 0; i < thread_count; i++) { + /* com_param[i].file is just used as a dummy buffer to save data, set + * it's ops to empty. + */ + comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops); + comp_param[i].done = true; + qemu_mutex_init(&comp_param[i].mutex); + qemu_cond_init(&comp_param[i].cond); + qemu_thread_create(compress_threads + i, "compress", + do_data_compress, comp_param + i, + QEMU_THREAD_JOINABLE); + } +} + +/** + * save_page_header: Write page header to wire + * + * If this is the 1st block, it also writes the block identification + * + * Returns: Number of bytes written + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * in the lower bits, it contains flags + */ +static size_t save_page_header(QEMUFile *f, RAMBlock *block, ram_addr_t offset) +{ + size_t size; + + qemu_put_be64(f, offset); + size = 8; + + if (!(offset & RAM_SAVE_FLAG_CONTINUE)) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, + strlen(block->idstr)); + size += 1 + strlen(block->idstr); + } + return size; +} + +/* Update the xbzrle cache to reflect a page that's been sent as all 0. + * The important thing is that a stale (not-yet-0'd) page be replaced + * by the new data. + * As a bonus, if the page wasn't in the cache it gets added so that + * when a small write is made into the 0'd page it gets XBZRLE sent + */ +static void xbzrle_cache_zero_page(ram_addr_t current_addr) +{ + if (ram_bulk_stage || !migrate_use_xbzrle()) { + return; + } + + /* We don't care if this fails to allocate a new cache page + * as long as it updated an old one */ + cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE, + bitmap_sync_count); +} + +#define ENCODING_FLAG_XBZRLE 0x1 + +/** + * save_xbzrle_page: compress and send current page + * + * Returns: 1 means that we wrote the page + * 0 means that page is identical to the one already sent + * -1 means that xbzrle would be longer than normal + * + * @f: QEMUFile where to send the data + * @current_data: + * @current_addr: + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ +static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, + ram_addr_t current_addr, RAMBlock *block, + ram_addr_t offset, bool last_stage, + uint64_t *bytes_transferred) +{ + int encoded_len = 0, bytes_xbzrle; + uint8_t *prev_cached_page; + + if (!cache_is_cached(XBZRLE.cache, current_addr, bitmap_sync_count)) { + acct_info.xbzrle_cache_miss++; + if (!last_stage) { + if (cache_insert(XBZRLE.cache, current_addr, *current_data, + bitmap_sync_count) == -1) { + return -1; + } else { + /* update *current_data when the page has been + inserted into cache */ + *current_data = get_cached_data(XBZRLE.cache, current_addr); + } + } + return -1; + } + + prev_cached_page = get_cached_data(XBZRLE.cache, current_addr); + + /* save current buffer into memory */ + memcpy(XBZRLE.current_buf, *current_data, TARGET_PAGE_SIZE); + + /* XBZRLE encoding (if there is no overflow) */ + encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf, + TARGET_PAGE_SIZE, XBZRLE.encoded_buf, + TARGET_PAGE_SIZE); + if (encoded_len == 0) { + DPRINTF("Skipping unmodified page\n"); + return 0; + } else if (encoded_len == -1) { + DPRINTF("Overflow\n"); + acct_info.xbzrle_overflows++; + /* update data in the cache */ + if (!last_stage) { + memcpy(prev_cached_page, *current_data, TARGET_PAGE_SIZE); + *current_data = prev_cached_page; + } + return -1; + } + + /* we need to update the data in the cache, in order to get the same data */ + if (!last_stage) { + memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE); + } + + /* Send XBZRLE based compressed page */ + bytes_xbzrle = save_page_header(f, block, offset | RAM_SAVE_FLAG_XBZRLE); + qemu_put_byte(f, ENCODING_FLAG_XBZRLE); + qemu_put_be16(f, encoded_len); + qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len); + bytes_xbzrle += encoded_len + 1 + 2; + acct_info.xbzrle_pages++; + acct_info.xbzrle_bytes += bytes_xbzrle; + *bytes_transferred += bytes_xbzrle; + + return 1; +} + +static inline +ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr, + ram_addr_t start) +{ + unsigned long base = mr->ram_addr >> TARGET_PAGE_BITS; + unsigned long nr = base + (start >> TARGET_PAGE_BITS); + uint64_t mr_size = TARGET_PAGE_ALIGN(memory_region_size(mr)); + unsigned long size = base + (mr_size >> TARGET_PAGE_BITS); + + unsigned long next; + + if (ram_bulk_stage && nr > base) { + next = nr + 1; + } else { + next = find_next_bit(migration_bitmap, size, nr); + } + + if (next < size) { + clear_bit(next, migration_bitmap); + migration_dirty_pages--; + } + return (next - base) << TARGET_PAGE_BITS; +} + +static inline bool migration_bitmap_set_dirty(ram_addr_t addr) +{ + bool ret; + int nr = addr >> TARGET_PAGE_BITS; + + ret = test_and_set_bit(nr, migration_bitmap); + + if (!ret) { + migration_dirty_pages++; + } + return ret; +} + +static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) +{ + ram_addr_t addr; + unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); + + /* start address is aligned at the start of a word? */ + if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) { + int k; + int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); + unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]; + + for (k = page; k < page + nr; k++) { + if (src[k]) { + unsigned long new_dirty; + new_dirty = ~migration_bitmap[k]; + migration_bitmap[k] |= src[k]; + new_dirty &= src[k]; + migration_dirty_pages += ctpopl(new_dirty); + src[k] = 0; + } + } + } else { + for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION)) { + cpu_physical_memory_reset_dirty(start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION); + migration_bitmap_set_dirty(start + addr); + } + } + } +} + + +/* Fix me: there are too many global variables used in migration process. */ +static int64_t start_time; +static int64_t bytes_xfer_prev; +static int64_t num_dirty_pages_period; +static uint64_t xbzrle_cache_miss_prev; +static uint64_t iterations_prev; + +static void migration_bitmap_sync_init(void) +{ + start_time = 0; + bytes_xfer_prev = 0; + num_dirty_pages_period = 0; + xbzrle_cache_miss_prev = 0; + iterations_prev = 0; +} + +/* Called with iothread lock held, to protect ram_list.dirty_memory[] */ +static void migration_bitmap_sync(void) +{ + RAMBlock *block; + uint64_t num_dirty_pages_init = migration_dirty_pages; + MigrationState *s = migrate_get_current(); + int64_t end_time; + int64_t bytes_xfer_now; + + bitmap_sync_count++; + + if (!bytes_xfer_prev) { + bytes_xfer_prev = ram_bytes_transferred(); + } + + if (!start_time) { + start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + } + + trace_migration_bitmap_sync_start(); + address_space_sync_dirty_bitmap(&address_space_memory); + + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + migration_bitmap_sync_range(block->mr->ram_addr, block->used_length); + } + rcu_read_unlock(); + + trace_migration_bitmap_sync_end(migration_dirty_pages + - num_dirty_pages_init); + num_dirty_pages_period += migration_dirty_pages - num_dirty_pages_init; + end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + /* more than 1 second = 1000 millisecons */ + if (end_time > start_time + 1000) { + if (migrate_auto_converge()) { + /* The following detection logic can be refined later. For now: + Check to see if the dirtied bytes is 50% more than the approx. + amount of bytes that just got transferred since the last time we + were in this routine. If that happens >N times (for now N==4) + we turn on the throttle down logic */ + bytes_xfer_now = ram_bytes_transferred(); + if (s->dirty_pages_rate && + (num_dirty_pages_period * TARGET_PAGE_SIZE > + (bytes_xfer_now - bytes_xfer_prev)/2) && + (dirty_rate_high_cnt++ > 4)) { + trace_migration_throttle(); + mig_throttle_on = true; + dirty_rate_high_cnt = 0; + } + bytes_xfer_prev = bytes_xfer_now; + } else { + mig_throttle_on = false; + } + if (migrate_use_xbzrle()) { + if (iterations_prev != acct_info.iterations) { + acct_info.xbzrle_cache_miss_rate = + (double)(acct_info.xbzrle_cache_miss - + xbzrle_cache_miss_prev) / + (acct_info.iterations - iterations_prev); + } + iterations_prev = acct_info.iterations; + xbzrle_cache_miss_prev = acct_info.xbzrle_cache_miss; + } + s->dirty_pages_rate = num_dirty_pages_period * 1000 + / (end_time - start_time); + s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE; + start_time = end_time; + num_dirty_pages_period = 0; + } + s->dirty_sync_count = bitmap_sync_count; +} + +/** + * save_zero_page: Send the zero page to the stream + * + * Returns: Number of pages written. + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @p: pointer to the page + * @bytes_transferred: increase it with the number of transferred bytes + */ +static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset, + uint8_t *p, uint64_t *bytes_transferred) +{ + int pages = -1, el2_ret; + bool ret; + + el2_ret = kvm_is_zero_page((unsigned long)p); + if (el2_ret > 1) + ret = is_zero_range(p, TARGET_PAGE_SIZE); + else + ret = 0; + + if (ret) { + acct_info.dup_pages++; + *bytes_transferred += save_page_header(f, block, + offset | RAM_SAVE_FLAG_COMPRESS); + qemu_put_byte(f, 0); + *bytes_transferred += 1; + pages = 1; + } + + //fprintf(stderr, "save_zero_page %d p %lx\n", pages, (unsigned long)p); + return pages; +} + +/** + * ram_save_page: Send the given page to the stream + * + * Returns: Number of pages written. + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ +static uint8_t save_ptr[TARGET_PAGE_SIZE]; +static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset, + bool last_stage, uint64_t *bytes_transferred) +{ + int pages = -1; + uint64_t bytes_xmit; + ram_addr_t current_addr; + MemoryRegion *mr = block->mr; + uint8_t *p; + int ret; + //bool send_async = true; + bool send_async = false; + //void *save_ptr; + + //if (!save_ptr) { + //fprintf(stderr, "ptr %lx\n", (unsigned long)ptr); + //save_ptr = malloc(4096); + //if (!save_ptr) + // exit(1); + //} + + p = memory_region_get_ram_ptr(mr) + offset; + + /* In doubt sent page as normal */ + bytes_xmit = 0; + ret = ram_control_save_page(f, block->offset, + offset, TARGET_PAGE_SIZE, &bytes_xmit); + if (bytes_xmit) { + *bytes_transferred += bytes_xmit; + pages = 1; + } + + XBZRLE_cache_lock(); + + current_addr = block->offset + offset; + + if (block == last_sent_block) { + offset |= RAM_SAVE_FLAG_CONTINUE; + } + if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { + if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (bytes_xmit > 0) { + acct_info.norm_pages++; + } else if (bytes_xmit == 0) { + acct_info.dup_pages++; + } + } + } else { + pages = save_zero_page(f, block, offset, p, bytes_transferred); + if (pages > 0) { + /* Must let xbzrle know, otherwise a previous (now 0'd) cached + * page would be stale + */ + xbzrle_cache_zero_page(current_addr); + } else if (!ram_bulk_stage && migrate_use_xbzrle()) { + pages = save_xbzrle_page(f, &p, current_addr, block, + offset, last_stage, bytes_transferred); + if (!last_stage) { + /* Can't send this cached data async, since the cache page + * might get updated before it gets to the wire + */ + send_async = false; + } + } + } + + /* XBZRLE overflow or normal page */ + if (pages == -1) { + *bytes_transferred += save_page_header(f, block, + offset | RAM_SAVE_FLAG_PAGE); + if (send_async) { + //kvm_encrypt_buf((unsigned long)p, (unsigned long)save_page_buf); + //qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE); + //qemu_put_buffer_async(f, save_page_buf, TARGET_PAGE_SIZE); + } else { + //if (get_pfn_of_address(save_ptr) == 0) + // exit(1); + kvm_encrypt_buf((unsigned long)p, (unsigned long)save_ptr); + //qemu_put_buffer(f, p, TARGET_PAGE_SIZE); + qemu_put_buffer(f, (const uint8_t *)save_ptr, TARGET_PAGE_SIZE); + //free(save_ptr); + } + *bytes_transferred += TARGET_PAGE_SIZE; + pages = 1; + acct_info.norm_pages++; + //fprintf(stderr, "ram_save_page %d p %lx size %lx\n", pages, (unsigned long)p, TARGET_PAGE_SIZE); + } + + XBZRLE_cache_unlock(); + + return pages; +} + +static int do_compress_ram_page(CompressParam *param) +{ + int bytes_sent, blen; + uint8_t *p; + RAMBlock *block = param->block; + ram_addr_t offset = param->offset; + + p = memory_region_get_ram_ptr(block->mr) + (offset & TARGET_PAGE_MASK); + + bytes_sent = save_page_header(param->file, block, offset | + RAM_SAVE_FLAG_COMPRESS_PAGE); + blen = qemu_put_compression_data(param->file, p, TARGET_PAGE_SIZE, + migrate_compress_level()); + bytes_sent += blen; + + return bytes_sent; +} + +static inline void start_compression(CompressParam *param) +{ + param->done = false; + qemu_mutex_lock(¶m->mutex); + param->start = true; + qemu_cond_signal(¶m->cond); + qemu_mutex_unlock(¶m->mutex); +} + +static inline void start_decompression(DecompressParam *param) +{ + qemu_mutex_lock(¶m->mutex); + param->start = true; + qemu_cond_signal(¶m->cond); + qemu_mutex_unlock(¶m->mutex); +} + +static uint64_t bytes_transferred; + +static void flush_compressed_data(QEMUFile *f) +{ + int idx, len, thread_count; + + if (!migrate_use_compression()) { + return; + } + thread_count = migrate_compress_threads(); + for (idx = 0; idx < thread_count; idx++) { + if (!comp_param[idx].done) { + qemu_mutex_lock(comp_done_lock); + while (!comp_param[idx].done && !quit_comp_thread) { + qemu_cond_wait(comp_done_cond, comp_done_lock); + } + qemu_mutex_unlock(comp_done_lock); + } + if (!quit_comp_thread) { + len = qemu_put_qemu_file(f, comp_param[idx].file); + bytes_transferred += len; + } + } +} + +static inline void set_compress_params(CompressParam *param, RAMBlock *block, + ram_addr_t offset) +{ + param->block = block; + param->offset = offset; +} + +static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block, + ram_addr_t offset, + uint64_t *bytes_transferred) +{ + int idx, thread_count, bytes_xmit = -1, pages = -1; + + thread_count = migrate_compress_threads(); + qemu_mutex_lock(comp_done_lock); + while (true) { + for (idx = 0; idx < thread_count; idx++) { + if (comp_param[idx].done) { + bytes_xmit = qemu_put_qemu_file(f, comp_param[idx].file); + set_compress_params(&comp_param[idx], block, offset); + start_compression(&comp_param[idx]); + pages = 1; + acct_info.norm_pages++; + *bytes_transferred += bytes_xmit; + break; + } + } + if (pages > 0) { + break; + } else { + qemu_cond_wait(comp_done_cond, comp_done_lock); + } + } + qemu_mutex_unlock(comp_done_lock); + + return pages; +} + +/** + * ram_save_compressed_page: compress the given page and send it to the stream + * + * Returns: Number of pages written. + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ +static int ram_save_compressed_page(QEMUFile *f, RAMBlock *block, + ram_addr_t offset, bool last_stage, + uint64_t *bytes_transferred) +{ + int pages = -1; + uint64_t bytes_xmit; + MemoryRegion *mr = block->mr; + uint8_t *p; + int ret; + + p = memory_region_get_ram_ptr(mr) + offset; + + bytes_xmit = 0; + ret = ram_control_save_page(f, block->offset, + offset, TARGET_PAGE_SIZE, &bytes_xmit); + if (bytes_xmit) { + *bytes_transferred += bytes_xmit; + pages = 1; + } + if (block == last_sent_block) { + offset |= RAM_SAVE_FLAG_CONTINUE; + } + if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { + if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (bytes_xmit > 0) { + acct_info.norm_pages++; + } else if (bytes_xmit == 0) { + acct_info.dup_pages++; + } + } + } else { + /* When starting the process of a new block, the first page of + * the block should be sent out before other pages in the same + * block, and all the pages in last block should have been sent + * out, keeping this order is important, because the 'cont' flag + * is used to avoid resending the block name. + */ + if (block != last_sent_block) { + flush_compressed_data(f); + pages = save_zero_page(f, block, offset, p, bytes_transferred); + if (pages == -1) { + set_compress_params(&comp_param[0], block, offset); + /* Use the qemu thread to compress the data to make sure the + * first page is sent out before other pages + */ + bytes_xmit = do_compress_ram_page(&comp_param[0]); + acct_info.norm_pages++; + qemu_put_qemu_file(f, comp_param[0].file); + *bytes_transferred += bytes_xmit; + pages = 1; + } + } else { + pages = save_zero_page(f, block, offset, p, bytes_transferred); + if (pages == -1) { + pages = compress_page_with_multi_thread(f, block, offset, + bytes_transferred); + } + } + } + + return pages; +} + +/** + * ram_find_and_save_block: Finds a dirty page and sends it to f + * + * Called within an RCU critical section. + * + * Returns: The number of pages written + * 0 means no dirty pages + * + * @f: QEMUFile where to send the data + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ + +static int ram_find_and_save_block(QEMUFile *f, bool last_stage, + uint64_t *bytes_transferred) +{ + RAMBlock *block = last_seen_block; + ram_addr_t offset = last_offset; + bool complete_round = false; + int pages = 0; + MemoryRegion *mr; + + if (!block) + block = QLIST_FIRST_RCU(&ram_list.blocks); + + while (true) { + mr = block->mr; + offset = migration_bitmap_find_and_reset_dirty(mr, offset); + if (complete_round && block == last_seen_block && + offset >= last_offset) { + break; + } + if (offset >= block->used_length) { + offset = 0; + block = QLIST_NEXT_RCU(block, next); + if (!block) { + block = QLIST_FIRST_RCU(&ram_list.blocks); + complete_round = true; + ram_bulk_stage = false; + if (migrate_use_xbzrle()) { + /* If xbzrle is on, stop using the data compression at this + * point. In theory, xbzrle can do better than compression. + */ + flush_compressed_data(f); + compression_switch = false; + } + } + } else { + if (compression_switch && migrate_use_compression()) { + pages = ram_save_compressed_page(f, block, offset, last_stage, + bytes_transferred); + } else { + pages = ram_save_page(f, block, offset, last_stage, + bytes_transferred); + } + + /* if page is unmodified, continue to the next */ + if (pages > 0) { + last_sent_block = block; + break; + } + } + } + + last_seen_block = block; + last_offset = offset; + + return pages; +} + +void acct_update_position(QEMUFile *f, size_t size, bool zero) +{ + uint64_t pages = size / TARGET_PAGE_SIZE; + if (zero) { + acct_info.dup_pages += pages; + } else { + acct_info.norm_pages += pages; + bytes_transferred += size; + qemu_update_position(f, size); + } +} + +static ram_addr_t ram_save_remaining(void) +{ + return migration_dirty_pages; +} + +uint64_t ram_bytes_remaining(void) +{ + return ram_save_remaining() * TARGET_PAGE_SIZE; +} + +uint64_t ram_bytes_transferred(void) +{ + return bytes_transferred; +} + +uint64_t ram_bytes_total(void) +{ + RAMBlock *block; + uint64_t total = 0; + + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) + total += block->used_length; + rcu_read_unlock(); + return total; +} + +void free_xbzrle_decoded_buf(void) +{ + g_free(xbzrle_decoded_buf); + xbzrle_decoded_buf = NULL; +} + +static void migration_end(void) +{ + if (migration_bitmap) { + memory_global_dirty_log_stop(); + g_free(migration_bitmap); + migration_bitmap = NULL; + } + + XBZRLE_cache_lock(); + if (XBZRLE.cache) { + cache_fini(XBZRLE.cache); + g_free(XBZRLE.encoded_buf); + g_free(XBZRLE.current_buf); + XBZRLE.cache = NULL; + XBZRLE.encoded_buf = NULL; + XBZRLE.current_buf = NULL; + } + XBZRLE_cache_unlock(); +} + +static void ram_migration_cancel(void *opaque) +{ + migration_end(); +} + +static void reset_ram_globals(void) +{ + last_seen_block = NULL; + last_sent_block = NULL; + last_offset = 0; + last_version = ram_list.version; + ram_bulk_stage = true; +} + +#define MAX_WAIT 50 /* ms, half buffered_file limit */ + + +/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has + * long-running RCU critical section. When rcu-reclaims in the code + * start to become numerous it will be necessary to reduce the + * granularity of these critical sections. + */ + +static int ram_save_setup(QEMUFile *f, void *opaque) +{ + RAMBlock *block; + int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */ + + mig_throttle_on = false; + dirty_rate_high_cnt = 0; + bitmap_sync_count = 0; + migration_bitmap_sync_init(); + + if (migrate_use_xbzrle()) { + XBZRLE_cache_lock(); + XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() / + TARGET_PAGE_SIZE, + TARGET_PAGE_SIZE); + if (!XBZRLE.cache) { + XBZRLE_cache_unlock(); + error_report("Error creating cache"); + return -1; + } + XBZRLE_cache_unlock(); + + /* We prefer not to abort if there is no memory */ + XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE); + if (!XBZRLE.encoded_buf) { + error_report("Error allocating encoded_buf"); + return -1; + } + + XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE); + if (!XBZRLE.current_buf) { + error_report("Error allocating current_buf"); + g_free(XBZRLE.encoded_buf); + XBZRLE.encoded_buf = NULL; + return -1; + } + + acct_clear(); + } + + /* iothread lock needed for ram_list.dirty_memory[] */ + qemu_mutex_lock_iothread(); + qemu_mutex_lock_ramlist(); + rcu_read_lock(); + bytes_transferred = 0; + reset_ram_globals(); + + ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS; + migration_bitmap = bitmap_new(ram_bitmap_pages); + bitmap_set(migration_bitmap, 0, ram_bitmap_pages); + + /* + * Count the total number of pages used by ram blocks not including any + * gaps due to alignment or unplugs. + */ + migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS; + + memory_global_dirty_log_start(); + migration_bitmap_sync(); + qemu_mutex_unlock_ramlist(); + qemu_mutex_unlock_iothread(); + + qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); + qemu_put_be64(f, block->used_length); + } + + rcu_read_unlock(); + + ram_control_before_iterate(f, RAM_CONTROL_SETUP); + ram_control_after_iterate(f, RAM_CONTROL_SETUP); + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + return 0; +} + +static int ram_save_iterate(QEMUFile *f, void *opaque) +{ + int ret; + int i; + int64_t t0; + int pages_sent = 0; + + rcu_read_lock(); + if (ram_list.version != last_version) { + reset_ram_globals(); + } + + /* Read version before ram_list.blocks */ + smp_rmb(); + + ram_control_before_iterate(f, RAM_CONTROL_ROUND); + + t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + i = 0; + while ((ret = qemu_file_rate_limit(f)) == 0) { + int pages; + + pages = ram_find_and_save_block(f, false, &bytes_transferred); + /* no more pages to sent */ + if (pages == 0) { + break; + } + pages_sent += pages; + acct_info.iterations++; + check_guest_throttling(); + /* we want to check in the 1st loop, just in case it was the 1st time + and we had to sync the dirty bitmap. + qemu_get_clock_ns() is a bit expensive, so we only check each some + iterations + */ + if ((i & 63) == 0) { + uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / 1000000; + if (t1 > MAX_WAIT) { + DPRINTF("big wait: %" PRIu64 " milliseconds, %d iterations\n", + t1, i); + break; + } + } + i++; + } + flush_compressed_data(f); + rcu_read_unlock(); + + /* + * Must occur before EOS (or any QEMUFile operation) + * because of RDMA protocol. + */ + ram_control_after_iterate(f, RAM_CONTROL_ROUND); + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + bytes_transferred += 8; + + ret = qemu_file_get_error(f); + if (ret < 0) { + return ret; + } + + return pages_sent; +} + +/* Called with iothread lock */ +static int ram_save_complete(QEMUFile *f, void *opaque) +{ + rcu_read_lock(); + + migration_bitmap_sync(); + + ram_control_before_iterate(f, RAM_CONTROL_FINISH); + + /* try transferring iterative blocks of memory */ + + /* flush all remaining blocks regardless of rate limiting */ + while (true) { + int pages; + + pages = ram_find_and_save_block(f, true, &bytes_transferred); + /* no more blocks to sent */ + if (pages == 0) { + break; + } + } + + flush_compressed_data(f); + ram_control_after_iterate(f, RAM_CONTROL_FINISH); + migration_end(); + + rcu_read_unlock(); + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + return 0; +} + +static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) +{ + uint64_t remaining_size; + + remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; + + if (remaining_size < max_size) { + qemu_mutex_lock_iothread(); + rcu_read_lock(); + migration_bitmap_sync(); + rcu_read_unlock(); + qemu_mutex_unlock_iothread(); + remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; + } + return remaining_size; +} + +static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) +{ + unsigned int xh_len; + int xh_flags; + + if (!xbzrle_decoded_buf) { + xbzrle_decoded_buf = g_malloc(TARGET_PAGE_SIZE); + } + + /* extract RLE header */ + xh_flags = qemu_get_byte(f); + xh_len = qemu_get_be16(f); + + if (xh_flags != ENCODING_FLAG_XBZRLE) { + error_report("Failed to load XBZRLE page - wrong compression!"); + return -1; + } + + if (xh_len > TARGET_PAGE_SIZE) { + error_report("Failed to load XBZRLE page - len overflow!"); + return -1; + } + /* load data and decode */ + qemu_get_buffer(f, xbzrle_decoded_buf, xh_len); + + /* decode RLE */ + if (xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host, + TARGET_PAGE_SIZE) == -1) { + error_report("Failed to load XBZRLE page - decode error!"); + return -1; + } + + return 0; +} + +/* Must be called from within a rcu critical section. + * Returns a pointer from within the RCU-protected ram_list. + */ +static inline void *host_from_stream_offset(QEMUFile *f, + ram_addr_t offset, + int flags) +{ + static RAMBlock *block = NULL; + char id[256]; + uint8_t len; + + if (flags & RAM_SAVE_FLAG_CONTINUE) { + if (!block || block->max_length <= offset) { + error_report("Ack, bad migration stream!"); + return NULL; + } + + return memory_region_get_ram_ptr(block->mr) + offset; + } + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id)) && + block->max_length > offset) { + return memory_region_get_ram_ptr(block->mr) + offset; + } + } + + error_report("Can't find block %s!", id); + return NULL; +} + +/* + * If a page (or a whole RDMA chunk) has been + * determined to be zero, then zap it. + */ +void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) +{ + if (ch != 0 || !is_zero_range(host, size)) { + memset(host, ch, size); + } +} + +static void *do_data_decompress(void *opaque) +{ + DecompressParam *param = opaque; + unsigned long pagesize; + + while (!quit_decomp_thread) { + qemu_mutex_lock(¶m->mutex); + while (!param->start && !quit_decomp_thread) { + qemu_cond_wait(¶m->cond, ¶m->mutex); + pagesize = TARGET_PAGE_SIZE; + if (!quit_decomp_thread) { + /* uncompress() will return failed in some case, especially + * when the page is dirted when doing the compression, it's + * not a problem because the dirty page will be retransferred + * and uncompress() won't break the data in other pages. + */ + uncompress((Bytef *)param->des, &pagesize, + (const Bytef *)param->compbuf, param->len); + } + param->start = false; + } + qemu_mutex_unlock(¶m->mutex); + } + + return NULL; +} + +void migrate_decompress_threads_create(void) +{ + int i, thread_count; + + thread_count = migrate_decompress_threads(); + decompress_threads = g_new0(QemuThread, thread_count); + decomp_param = g_new0(DecompressParam, thread_count); + compressed_data_buf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); + quit_decomp_thread = false; + for (i = 0; i < thread_count; i++) { + qemu_mutex_init(&decomp_param[i].mutex); + qemu_cond_init(&decomp_param[i].cond); + decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); + qemu_thread_create(decompress_threads + i, "decompress", + do_data_decompress, decomp_param + i, + QEMU_THREAD_JOINABLE); + } +} + +void migrate_decompress_threads_join(void) +{ + int i, thread_count; + + quit_decomp_thread = true; + thread_count = migrate_decompress_threads(); + for (i = 0; i < thread_count; i++) { + qemu_mutex_lock(&decomp_param[i].mutex); + qemu_cond_signal(&decomp_param[i].cond); + qemu_mutex_unlock(&decomp_param[i].mutex); + } + for (i = 0; i < thread_count; i++) { + qemu_thread_join(decompress_threads + i); + qemu_mutex_destroy(&decomp_param[i].mutex); + qemu_cond_destroy(&decomp_param[i].cond); + g_free(decomp_param[i].compbuf); + } + g_free(decompress_threads); + g_free(decomp_param); + g_free(compressed_data_buf); + decompress_threads = NULL; + decomp_param = NULL; + compressed_data_buf = NULL; +} + +static void decompress_data_with_multi_threads(uint8_t *compbuf, + void *host, int len) +{ + int idx, thread_count; + + thread_count = migrate_decompress_threads(); + while (true) { + for (idx = 0; idx < thread_count; idx++) { + if (!decomp_param[idx].start) { + memcpy(decomp_param[idx].compbuf, compbuf, len); + decomp_param[idx].des = host; + decomp_param[idx].len = len; + start_decompression(&decomp_param[idx]); + break; + } + } + if (idx < thread_count) { + break; + } + } +} + +static int ram_load(QEMUFile *f, void *opaque, int version_id) +{ + int flags = 0, ret = 0; + static uint64_t seq_iter; + int len = 0; + + seq_iter++; + + if (version_id != 4) { + ret = -EINVAL; + } + + /* This RCU critical section can be very long running. + * When RCU reclaims in the code start to become numerous, + * it will be necessary to reduce the granularity of this + * critical section. + */ + rcu_read_lock(); + while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { + ram_addr_t addr, total_ram_bytes; + void *host; + uint8_t ch; + + addr = qemu_get_be64(f); + flags = addr & ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + + switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { + case RAM_SAVE_FLAG_MEM_SIZE: + /* Synchronize RAM block list */ + total_ram_bytes = addr; + while (!ret && total_ram_bytes) { + RAMBlock *block; + uint8_t len; + char id[256]; + ram_addr_t length; + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + length = qemu_get_be64(f); + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id))) { + if (length != block->used_length) { + Error *local_err = NULL; + + ret = qemu_ram_resize(block->offset, length, &local_err); + if (local_err) { + error_report_err(local_err); + } + } + break; + } + } + + if (!block) { + error_report("Unknown ramblock \"%s\", cannot " + "accept migration", id); + ret = -EINVAL; + } + + total_ram_bytes -= length; + } + break; + case RAM_SAVE_FLAG_COMPRESS: + host = host_from_stream_offset(f, addr, flags); + if (!host) { + error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + ch = qemu_get_byte(f); + ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); + break; + case RAM_SAVE_FLAG_PAGE: + host = host_from_stream_offset(f, addr, flags); + if (!host) { + error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + qemu_get_buffer(f, host, TARGET_PAGE_SIZE); + kvm_decrypt_buf((unsigned long)host); + break; + case RAM_SAVE_FLAG_COMPRESS_PAGE: + host = host_from_stream_offset(f, addr, flags); + if (!host) { + error_report("Invalid RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + + len = qemu_get_be32(f); + if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { + error_report("Invalid compressed data length: %d", len); + ret = -EINVAL; + break; + } + qemu_get_buffer(f, compressed_data_buf, len); + decompress_data_with_multi_threads(compressed_data_buf, host, len); + break; + case RAM_SAVE_FLAG_XBZRLE: + host = host_from_stream_offset(f, addr, flags); + if (!host) { + error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + if (load_xbzrle(f, addr, host) < 0) { + error_report("Failed to decompress XBZRLE page at " + RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + break; + case RAM_SAVE_FLAG_EOS: + /* normal exit */ + break; + default: + if (flags & RAM_SAVE_FLAG_HOOK) { + ram_control_load_hook(f, flags); + } else { + error_report("Unknown combination of migration flags: %#x", + flags); + ret = -EINVAL; + } + } + if (!ret) { + ret = qemu_file_get_error(f); + } + } + + rcu_read_unlock(); + DPRINTF("Completed load of VM with exit code %d seq iteration " + "%" PRIu64 "\n", ret, seq_iter); + return ret; +} + +static SaveVMHandlers savevm_ram_handlers = { + .save_live_setup = ram_save_setup, + .save_live_iterate = ram_save_iterate, + .save_live_complete = ram_save_complete, + .save_live_pending = ram_save_pending, + .load_state = ram_load, + .cancel = ram_migration_cancel, +}; + +void ram_mig_init(void) +{ + qemu_mutex_init(&XBZRLE.lock); + register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL); +} + +struct soundhw { + const char *name; + const char *descr; + int enabled; + int isa; + union { + int (*init_isa) (ISABus *bus); + int (*init_pci) (PCIBus *bus); + } init; +}; + +static struct soundhw soundhw[9]; +static int soundhw_count; + +void isa_register_soundhw(const char *name, const char *descr, + int (*init_isa)(ISABus *bus)) +{ + assert(soundhw_count < ARRAY_SIZE(soundhw) - 1); + soundhw[soundhw_count].name = name; + soundhw[soundhw_count].descr = descr; + soundhw[soundhw_count].isa = 1; + soundhw[soundhw_count].init.init_isa = init_isa; + soundhw_count++; +} + +void pci_register_soundhw(const char *name, const char *descr, + int (*init_pci)(PCIBus *bus)) +{ + assert(soundhw_count < ARRAY_SIZE(soundhw) - 1); + soundhw[soundhw_count].name = name; + soundhw[soundhw_count].descr = descr; + soundhw[soundhw_count].isa = 0; + soundhw[soundhw_count].init.init_pci = init_pci; + soundhw_count++; +} + +void select_soundhw(const char *optarg) +{ + struct soundhw *c; + + if (is_help_option(optarg)) { + show_valid_cards: + + if (soundhw_count) { + printf("Valid sound card names (comma separated):\n"); + for (c = soundhw; c->name; ++c) { + printf ("%-11s %s\n", c->name, c->descr); + } + printf("\n-soundhw all will enable all of the above\n"); + } else { + printf("Machine has no user-selectable audio hardware " + "(it may or may not have always-present audio hardware).\n"); + } + exit(!is_help_option(optarg)); + } + else { + size_t l; + const char *p; + char *e; + int bad_card = 0; + + if (!strcmp(optarg, "all")) { + for (c = soundhw; c->name; ++c) { + c->enabled = 1; + } + return; + } + + p = optarg; + while (*p) { + e = strchr(p, ','); + l = !e ? strlen(p) : (size_t) (e - p); + + for (c = soundhw; c->name; ++c) { + if (!strncmp(c->name, p, l) && !c->name[l]) { + c->enabled = 1; + break; + } + } + + if (!c->name) { + if (l > 80) { + error_report("Unknown sound card name (too big to show)"); + } + else { + error_report("Unknown sound card name `%.*s'", + (int) l, p); + } + bad_card = 1; + } + p += l + (e != NULL); + } + + if (bad_card) { + goto show_valid_cards; + } + } +} + +void audio_init(void) +{ + struct soundhw *c; + ISABus *isa_bus = (ISABus *) object_resolve_path_type("", TYPE_ISA_BUS, NULL); + PCIBus *pci_bus = (PCIBus *) object_resolve_path_type("", TYPE_PCI_BUS, NULL); + + for (c = soundhw; c->name; ++c) { + if (c->enabled) { + if (c->isa) { + if (!isa_bus) { + error_report("ISA bus not available for %s", c->name); + exit(1); + } + c->init.init_isa(isa_bus); + } else { + if (!pci_bus) { + error_report("PCI bus not available for %s", c->name); + exit(1); + } + c->init.init_pci(pci_bus); + } + } + } +} + +int qemu_uuid_parse(const char *str, uint8_t *uuid) +{ + int ret; + + if (strlen(str) != 36) { + return -1; + } + + ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3], + &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9], + &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], + &uuid[15]); + + if (ret != 16) { + return -1; + } + return 0; +} + +void do_acpitable_option(const QemuOpts *opts) +{ +#ifdef TARGET_I386 + Error *err = NULL; + + acpi_table_add(opts, &err); + if (err) { + error_report("Wrong acpi table provided: %s", + error_get_pretty(err)); + error_free(err); + exit(1); + } +#endif +} + +void do_smbios_option(QemuOpts *opts) +{ +#ifdef TARGET_I386 + smbios_entry_add(opts); +#endif +} + +void cpudef_init(void) +{ +#if defined(cpudef_setup) + cpudef_setup(); /* parse cpu definitions in target config file */ +#endif +} + +int kvm_available(void) +{ +#ifdef CONFIG_KVM + return 1; +#else + return 0; +#endif +} + +int xen_available(void) +{ +#ifdef CONFIG_XEN + return 1; +#else + return 0; +#endif +} + + +TargetInfo *qmp_query_target(Error **errp) +{ + TargetInfo *info = g_malloc0(sizeof(*info)); + + info->arch = g_strdup(TARGET_NAME); + + return info; +} + +/* Stub function that's gets run on the vcpu when its brought out of the + VM to run inside qemu via async_run_on_cpu()*/ +static void mig_sleep_cpu(void *opq) +{ + qemu_mutex_unlock_iothread(); + g_usleep(30*1000); + qemu_mutex_lock_iothread(); +} + +/* To reduce the dirty rate explicitly disallow the VCPUs from spending + much time in the VM. The migration thread will try to catchup. + Workload will experience a performance drop. +*/ +static void mig_throttle_guest_down(void) +{ + CPUState *cpu; + + qemu_mutex_lock_iothread(); + CPU_FOREACH(cpu) { + async_run_on_cpu(cpu, mig_sleep_cpu, NULL); + } + qemu_mutex_unlock_iothread(); +} + +static void check_guest_throttling(void) +{ + static int64_t t0; + int64_t t1; + + if (!mig_throttle_on) { + return; + } + + if (!t0) { + t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + return; + } + + t1 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + + /* If it has been more than 40 ms since the last time the guest + * was throttled then do it again. + */ + if (40 < (t1-t0)/1000000) { + mig_throttle_guest_down(); + t0 = t1; + } +} diff --git a/async.c b/async.c new file mode 100644 index 0000000..46d9e63 --- /dev/null +++ b/async.c @@ -0,0 +1,324 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "block/aio.h" +#include "block/thread-pool.h" +#include "qemu/main-loop.h" +#include "qemu/atomic.h" + +/***********************************************************/ +/* bottom halves (can be seen as timers which expire ASAP) */ + +struct QEMUBH { + AioContext *ctx; + QEMUBHFunc *cb; + void *opaque; + QEMUBH *next; + bool scheduled; + bool idle; + bool deleted; +}; + +QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) +{ + QEMUBH *bh; + bh = g_new(QEMUBH, 1); + *bh = (QEMUBH){ + .ctx = ctx, + .cb = cb, + .opaque = opaque, + }; + qemu_mutex_lock(&ctx->bh_lock); + bh->next = ctx->first_bh; + /* Make sure that the members are ready before putting bh into list */ + smp_wmb(); + ctx->first_bh = bh; + qemu_mutex_unlock(&ctx->bh_lock); + return bh; +} + +/* Multiple occurrences of aio_bh_poll cannot be called concurrently */ +int aio_bh_poll(AioContext *ctx) +{ + QEMUBH *bh, **bhp, *next; + int ret; + + ctx->walking_bh++; + + ret = 0; + for (bh = ctx->first_bh; bh; bh = next) { + /* Make sure that fetching bh happens before accessing its members */ + smp_read_barrier_depends(); + next = bh->next; + /* The atomic_xchg is paired with the one in qemu_bh_schedule. The + * implicit memory barrier ensures that the callback sees all writes + * done by the scheduling thread. It also ensures that the scheduling + * thread sees the zero before bh->cb has run, and thus will call + * aio_notify again if necessary. + */ + if (!bh->deleted && atomic_xchg(&bh->scheduled, 0)) { + if (!bh->idle) + ret = 1; + bh->idle = 0; + bh->cb(bh->opaque); + } + } + + ctx->walking_bh--; + + /* remove deleted bhs */ + if (!ctx->walking_bh) { + qemu_mutex_lock(&ctx->bh_lock); + bhp = &ctx->first_bh; + while (*bhp) { + bh = *bhp; + if (bh->deleted) { + *bhp = bh->next; + g_free(bh); + } else { + bhp = &bh->next; + } + } + qemu_mutex_unlock(&ctx->bh_lock); + } + + return ret; +} + +void qemu_bh_schedule_idle(QEMUBH *bh) +{ + bh->idle = 1; + /* Make sure that idle & any writes needed by the callback are done + * before the locations are read in the aio_bh_poll. + */ + atomic_mb_set(&bh->scheduled, 1); +} + +void qemu_bh_schedule(QEMUBH *bh) +{ + AioContext *ctx; + + ctx = bh->ctx; + bh->idle = 0; + /* The memory barrier implicit in atomic_xchg makes sure that: + * 1. idle & any writes needed by the callback are done before the + * locations are read in the aio_bh_poll. + * 2. ctx is loaded before scheduled is set and the callback has a chance + * to execute. + */ + if (atomic_xchg(&bh->scheduled, 1) == 0) { + aio_notify(ctx); + } +} + + +/* This func is async. + */ +void qemu_bh_cancel(QEMUBH *bh) +{ + bh->scheduled = 0; +} + +/* This func is async.The bottom half will do the delete action at the finial + * end. + */ +void qemu_bh_delete(QEMUBH *bh) +{ + bh->scheduled = 0; + bh->deleted = 1; +} + +int64_t +aio_compute_timeout(AioContext *ctx) +{ + int64_t deadline; + int timeout = -1; + QEMUBH *bh; + + for (bh = ctx->first_bh; bh; bh = bh->next) { + if (!bh->deleted && bh->scheduled) { + if (bh->idle) { + /* idle bottom halves will be polled at least + * every 10ms */ + timeout = 10000000; + } else { + /* non-idle bottom halves will be executed + * immediately */ + return 0; + } + } + } + + deadline = timerlistgroup_deadline_ns(&ctx->tlg); + if (deadline == 0) { + return 0; + } else { + return qemu_soonest_timeout(timeout, deadline); + } +} + +static gboolean +aio_ctx_prepare(GSource *source, gint *timeout) +{ + AioContext *ctx = (AioContext *) source; + + /* We assume there is no timeout already supplied */ + *timeout = qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)); + + if (aio_prepare(ctx)) { + *timeout = 0; + } + + return *timeout == 0; +} + +static gboolean +aio_ctx_check(GSource *source) +{ + AioContext *ctx = (AioContext *) source; + QEMUBH *bh; + + for (bh = ctx->first_bh; bh; bh = bh->next) { + if (!bh->deleted && bh->scheduled) { + return true; + } + } + return aio_pending(ctx) || (timerlistgroup_deadline_ns(&ctx->tlg) == 0); +} + +static gboolean +aio_ctx_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + AioContext *ctx = (AioContext *) source; + + assert(callback == NULL); + aio_dispatch(ctx); + return true; +} + +static void +aio_ctx_finalize(GSource *source) +{ + AioContext *ctx = (AioContext *) source; + + thread_pool_free(ctx->thread_pool); + aio_set_event_notifier(ctx, &ctx->notifier, NULL); + event_notifier_cleanup(&ctx->notifier); + rfifolock_destroy(&ctx->lock); + qemu_mutex_destroy(&ctx->bh_lock); + timerlistgroup_deinit(&ctx->tlg); +} + +static GSourceFuncs aio_source_funcs = { + aio_ctx_prepare, + aio_ctx_check, + aio_ctx_dispatch, + aio_ctx_finalize +}; + +GSource *aio_get_g_source(AioContext *ctx) +{ + g_source_ref(&ctx->source); + return &ctx->source; +} + +ThreadPool *aio_get_thread_pool(AioContext *ctx) +{ + if (!ctx->thread_pool) { + ctx->thread_pool = thread_pool_new(ctx); + } + return ctx->thread_pool; +} + +void aio_set_dispatching(AioContext *ctx, bool dispatching) +{ + ctx->dispatching = dispatching; + if (!dispatching) { + /* Write ctx->dispatching before reading e.g. bh->scheduled. + * Optimization: this is only needed when we're entering the "unsafe" + * phase where other threads must call event_notifier_set. + */ + smp_mb(); + } +} + +void aio_notify(AioContext *ctx) +{ + /* Write e.g. bh->scheduled before reading ctx->dispatching. */ + smp_mb(); + if (!ctx->dispatching) { + event_notifier_set(&ctx->notifier); + } +} + +static void aio_timerlist_notify(void *opaque) +{ + aio_notify(opaque); +} + +AioContext *aio_context_new(Error **errp) +{ + int ret; + AioContext *ctx; + ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext)); + ret = event_notifier_init(&ctx->notifier, false); + if (ret < 0) { + g_source_destroy(&ctx->source); + error_setg_errno(errp, -ret, "Failed to initialize event notifier"); + return NULL; + } + g_source_set_can_recurse(&ctx->source, true); + aio_set_event_notifier(ctx, &ctx->notifier, + (EventNotifierHandler *) + event_notifier_test_and_clear); + ctx->thread_pool = NULL; + qemu_mutex_init(&ctx->bh_lock); + rfifolock_init(&ctx->lock, NULL, NULL); + timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); + + return ctx; +} + +void aio_context_ref(AioContext *ctx) +{ + g_source_ref(&ctx->source); +} + +void aio_context_unref(AioContext *ctx) +{ + g_source_unref(&ctx->source); +} + +void aio_context_acquire(AioContext *ctx) +{ + rfifolock_lock(&ctx->lock); +} + +void aio_context_release(AioContext *ctx) +{ + rfifolock_unlock(&ctx->lock); +} diff --git a/audio/Makefile.objs b/audio/Makefile.objs new file mode 100644 index 0000000..26a0ac9 --- /dev/null +++ b/audio/Makefile.objs @@ -0,0 +1,17 @@ +common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o +common-obj-$(CONFIG_SDL) += sdlaudio.o +common-obj-$(CONFIG_OSS) += ossaudio.o +common-obj-$(CONFIG_SPICE) += spiceaudio.o +common-obj-$(CONFIG_COREAUDIO) += coreaudio.o +common-obj-$(CONFIG_ALSA) += alsaaudio.o +common-obj-$(CONFIG_DSOUND) += dsoundaudio.o +common-obj-$(CONFIG_FMOD) += fmodaudio.o +common-obj-$(CONFIG_ESD) += esdaudio.o +common-obj-$(CONFIG_PA) += paaudio.o +common-obj-$(CONFIG_WINWAVE) += winwaveaudio.o +common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o +common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o +common-obj-y += wavcapture.o + +$(obj)/audio.o $(obj)/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) +sdlaudio.o-cflags := $(SDL_CFLAGS) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c new file mode 100644 index 0000000..74ead97 --- /dev/null +++ b/audio/alsaaudio.c @@ -0,0 +1,1256 @@ +/* + * QEMU ALSA audio driver + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "qemu-common.h" +#include "qemu/main-loop.h" +#include "audio.h" + +#if QEMU_GNUC_PREREQ(4, 3) +#pragma GCC diagnostic ignored "-Waddress" +#endif + +#define AUDIO_CAP "alsa" +#include "audio_int.h" + +struct pollhlp { + snd_pcm_t *handle; + struct pollfd *pfds; + int count; + int mask; +}; + +typedef struct ALSAVoiceOut { + HWVoiceOut hw; + int wpos; + int pending; + void *pcm_buf; + snd_pcm_t *handle; + struct pollhlp pollhlp; +} ALSAVoiceOut; + +typedef struct ALSAVoiceIn { + HWVoiceIn hw; + snd_pcm_t *handle; + void *pcm_buf; + struct pollhlp pollhlp; +} ALSAVoiceIn; + +static struct { + int size_in_usec_in; + int size_in_usec_out; + const char *pcm_name_in; + const char *pcm_name_out; + unsigned int buffer_size_in; + unsigned int period_size_in; + unsigned int buffer_size_out; + unsigned int period_size_out; + unsigned int threshold; + + int buffer_size_in_overridden; + int period_size_in_overridden; + + int buffer_size_out_overridden; + int period_size_out_overridden; + int verbose; +} conf = { + .buffer_size_out = 4096, + .period_size_out = 1024, + .pcm_name_out = "default", + .pcm_name_in = "default", +}; + +struct alsa_params_req { + int freq; + snd_pcm_format_t fmt; + int nchannels; + int size_in_usec; + int override_mask; + unsigned int buffer_size; + unsigned int period_size; +}; + +struct alsa_params_obt { + int freq; + audfmt_e fmt; + int endianness; + int nchannels; + snd_pcm_uframes_t samples; +}; + +static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); +} + +static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( + int err, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); +} + +static void alsa_fini_poll (struct pollhlp *hlp) +{ + int i; + struct pollfd *pfds = hlp->pfds; + + if (pfds) { + for (i = 0; i < hlp->count; ++i) { + qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); + } + g_free (pfds); + } + hlp->pfds = NULL; + hlp->count = 0; + hlp->handle = NULL; +} + +static void alsa_anal_close1 (snd_pcm_t **handlep) +{ + int err = snd_pcm_close (*handlep); + if (err) { + alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep); + } + *handlep = NULL; +} + +static void alsa_anal_close (snd_pcm_t **handlep, struct pollhlp *hlp) +{ + alsa_fini_poll (hlp); + alsa_anal_close1 (handlep); +} + +static int alsa_recover (snd_pcm_t *handle) +{ + int err = snd_pcm_prepare (handle); + if (err < 0) { + alsa_logerr (err, "Failed to prepare handle %p\n", handle); + return -1; + } + return 0; +} + +static int alsa_resume (snd_pcm_t *handle) +{ + int err = snd_pcm_resume (handle); + if (err < 0) { + alsa_logerr (err, "Failed to resume handle %p\n", handle); + return -1; + } + return 0; +} + +static void alsa_poll_handler (void *opaque) +{ + int err, count; + snd_pcm_state_t state; + struct pollhlp *hlp = opaque; + unsigned short revents; + + count = poll (hlp->pfds, hlp->count, 0); + if (count < 0) { + dolog ("alsa_poll_handler: poll %s\n", strerror (errno)); + return; + } + + if (!count) { + return; + } + + /* XXX: ALSA example uses initial count, not the one returned by + poll, correct? */ + err = snd_pcm_poll_descriptors_revents (hlp->handle, hlp->pfds, + hlp->count, &revents); + if (err < 0) { + alsa_logerr (err, "snd_pcm_poll_descriptors_revents"); + return; + } + + if (!(revents & hlp->mask)) { + if (conf.verbose) { + dolog ("revents = %d\n", revents); + } + return; + } + + state = snd_pcm_state (hlp->handle); + switch (state) { + case SND_PCM_STATE_SETUP: + alsa_recover (hlp->handle); + break; + + case SND_PCM_STATE_XRUN: + alsa_recover (hlp->handle); + break; + + case SND_PCM_STATE_SUSPENDED: + alsa_resume (hlp->handle); + break; + + case SND_PCM_STATE_PREPARED: + audio_run ("alsa run (prepared)"); + break; + + case SND_PCM_STATE_RUNNING: + audio_run ("alsa run (running)"); + break; + + default: + dolog ("Unexpected state %d\n", state); + } +} + +static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) +{ + int i, count, err; + struct pollfd *pfds; + + count = snd_pcm_poll_descriptors_count (handle); + if (count <= 0) { + dolog ("Could not initialize poll mode\n" + "Invalid number of poll descriptors %d\n", count); + return -1; + } + + pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds)); + if (!pfds) { + dolog ("Could not initialize poll mode\n"); + return -1; + } + + err = snd_pcm_poll_descriptors (handle, pfds, count); + if (err < 0) { + alsa_logerr (err, "Could not initialize poll mode\n" + "Could not obtain poll descriptors\n"); + g_free (pfds); + return -1; + } + + for (i = 0; i < count; ++i) { + if (pfds[i].events & POLLIN) { + err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, + NULL, hlp); + } + if (pfds[i].events & POLLOUT) { + if (conf.verbose) { + dolog ("POLLOUT %d %d\n", i, pfds[i].fd); + } + err = qemu_set_fd_handler (pfds[i].fd, NULL, + alsa_poll_handler, hlp); + } + if (conf.verbose) { + dolog ("Set handler events=%#x index=%d fd=%d err=%d\n", + pfds[i].events, i, pfds[i].fd, err); + } + + if (err) { + dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n", + pfds[i].events, i, pfds[i].fd, err); + + while (i--) { + qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); + } + g_free (pfds); + return -1; + } + } + hlp->pfds = pfds; + hlp->count = count; + hlp->handle = handle; + hlp->mask = mask; + return 0; +} + +static int alsa_poll_out (HWVoiceOut *hw) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + + return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLOUT); +} + +static int alsa_poll_in (HWVoiceIn *hw) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + + return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN); +} + +static int alsa_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) +{ + switch (fmt) { + case AUD_FMT_S8: + return SND_PCM_FORMAT_S8; + + case AUD_FMT_U8: + return SND_PCM_FORMAT_U8; + + case AUD_FMT_S16: + if (endianness) { + return SND_PCM_FORMAT_S16_BE; + } + else { + return SND_PCM_FORMAT_S16_LE; + } + + case AUD_FMT_U16: + if (endianness) { + return SND_PCM_FORMAT_U16_BE; + } + else { + return SND_PCM_FORMAT_U16_LE; + } + + case AUD_FMT_S32: + if (endianness) { + return SND_PCM_FORMAT_S32_BE; + } + else { + return SND_PCM_FORMAT_S32_LE; + } + + case AUD_FMT_U32: + if (endianness) { + return SND_PCM_FORMAT_U32_BE; + } + else { + return SND_PCM_FORMAT_U32_LE; + } + + default: + dolog ("Internal logic error: Bad audio format %d\n", fmt); +#ifdef DEBUG_AUDIO + abort (); +#endif + return SND_PCM_FORMAT_U8; + } +} + +static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, + int *endianness) +{ + switch (alsafmt) { + case SND_PCM_FORMAT_S8: + *endianness = 0; + *fmt = AUD_FMT_S8; + break; + + case SND_PCM_FORMAT_U8: + *endianness = 0; + *fmt = AUD_FMT_U8; + break; + + case SND_PCM_FORMAT_S16_LE: + *endianness = 0; + *fmt = AUD_FMT_S16; + break; + + case SND_PCM_FORMAT_U16_LE: + *endianness = 0; + *fmt = AUD_FMT_U16; + break; + + case SND_PCM_FORMAT_S16_BE: + *endianness = 1; + *fmt = AUD_FMT_S16; + break; + + case SND_PCM_FORMAT_U16_BE: + *endianness = 1; + *fmt = AUD_FMT_U16; + break; + + case SND_PCM_FORMAT_S32_LE: + *endianness = 0; + *fmt = AUD_FMT_S32; + break; + + case SND_PCM_FORMAT_U32_LE: + *endianness = 0; + *fmt = AUD_FMT_U32; + break; + + case SND_PCM_FORMAT_S32_BE: + *endianness = 1; + *fmt = AUD_FMT_S32; + break; + + case SND_PCM_FORMAT_U32_BE: + *endianness = 1; + *fmt = AUD_FMT_U32; + break; + + default: + dolog ("Unrecognized audio format %d\n", alsafmt); + return -1; + } + + return 0; +} + +static void alsa_dump_info (struct alsa_params_req *req, + struct alsa_params_obt *obt, + snd_pcm_format_t obtfmt) +{ + dolog ("parameter | requested value | obtained value\n"); + dolog ("format | %10d | %10d\n", req->fmt, obtfmt); + dolog ("channels | %10d | %10d\n", + req->nchannels, obt->nchannels); + dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); + dolog ("============================================\n"); + dolog ("requested: buffer size %d period size %d\n", + req->buffer_size, req->period_size); + dolog ("obtained: samples %ld\n", obt->samples); +} + +static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) +{ + int err; + snd_pcm_sw_params_t *sw_params; + + snd_pcm_sw_params_alloca (&sw_params); + + err = snd_pcm_sw_params_current (handle, sw_params); + if (err < 0) { + dolog ("Could not fully initialize DAC\n"); + alsa_logerr (err, "Failed to get current software parameters\n"); + return; + } + + err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold); + if (err < 0) { + dolog ("Could not fully initialize DAC\n"); + alsa_logerr (err, "Failed to set software threshold to %ld\n", + threshold); + return; + } + + err = snd_pcm_sw_params (handle, sw_params); + if (err < 0) { + dolog ("Could not fully initialize DAC\n"); + alsa_logerr (err, "Failed to set software parameters\n"); + return; + } +} + +static int alsa_open (int in, struct alsa_params_req *req, + struct alsa_params_obt *obt, snd_pcm_t **handlep) +{ + snd_pcm_t *handle; + snd_pcm_hw_params_t *hw_params; + int err; + int size_in_usec; + unsigned int freq, nchannels; + const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; + snd_pcm_uframes_t obt_buffer_size; + const char *typ = in ? "ADC" : "DAC"; + snd_pcm_format_t obtfmt; + + freq = req->freq; + nchannels = req->nchannels; + size_in_usec = req->size_in_usec; + + snd_pcm_hw_params_alloca (&hw_params); + + err = snd_pcm_open ( + &handle, + pcm_name, + in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK + ); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name); + return -1; + } + + err = snd_pcm_hw_params_any (handle, hw_params); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n"); + goto err; + } + + err = snd_pcm_hw_params_set_access ( + handle, + hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED + ); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set access type\n"); + goto err; + } + + err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); + if (err < 0 && conf.verbose) { + alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); + } + + err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq); + goto err; + } + + err = snd_pcm_hw_params_set_channels_near ( + handle, + hw_params, + &nchannels + ); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set number of channels %d\n", + req->nchannels); + goto err; + } + + if (nchannels != 1 && nchannels != 2) { + alsa_logerr2 (err, typ, + "Can not handle obtained number of channels %d\n", + nchannels); + goto err; + } + + if (req->buffer_size) { + unsigned long obt; + + if (size_in_usec) { + int dir = 0; + unsigned int btime = req->buffer_size; + + err = snd_pcm_hw_params_set_buffer_time_near ( + handle, + hw_params, + &btime, + &dir + ); + obt = btime; + } + else { + snd_pcm_uframes_t bsize = req->buffer_size; + + err = snd_pcm_hw_params_set_buffer_size_near ( + handle, + hw_params, + &bsize + ); + obt = bsize; + } + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n", + size_in_usec ? "time" : "size", req->buffer_size); + goto err; + } + + if ((req->override_mask & 2) && (obt - req->buffer_size)) + dolog ("Requested buffer %s %u was rejected, using %lu\n", + size_in_usec ? "time" : "size", req->buffer_size, obt); + } + + if (req->period_size) { + unsigned long obt; + + if (size_in_usec) { + int dir = 0; + unsigned int ptime = req->period_size; + + err = snd_pcm_hw_params_set_period_time_near ( + handle, + hw_params, + &ptime, + &dir + ); + obt = ptime; + } + else { + int dir = 0; + snd_pcm_uframes_t psize = req->period_size; + + err = snd_pcm_hw_params_set_period_size_near ( + handle, + hw_params, + &psize, + &dir + ); + obt = psize; + } + + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set period %s to %d\n", + size_in_usec ? "time" : "size", req->period_size); + goto err; + } + + if (((req->override_mask & 1) && (obt - req->period_size))) + dolog ("Requested period %s %u was rejected, using %lu\n", + size_in_usec ? "time" : "size", req->period_size, obt); + } + + err = snd_pcm_hw_params (handle, hw_params); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to apply audio parameters\n"); + goto err; + } + + err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to get buffer size\n"); + goto err; + } + + err = snd_pcm_hw_params_get_format (hw_params, &obtfmt); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to get format\n"); + goto err; + } + + if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) { + dolog ("Invalid format was returned %d\n", obtfmt); + goto err; + } + + err = snd_pcm_prepare (handle); + if (err < 0) { + alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle); + goto err; + } + + if (!in && conf.threshold) { + snd_pcm_uframes_t threshold; + int bytes_per_sec; + + bytes_per_sec = freq << (nchannels == 2); + + switch (obt->fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + break; + + case AUD_FMT_S16: + case AUD_FMT_U16: + bytes_per_sec <<= 1; + break; + + case AUD_FMT_S32: + case AUD_FMT_U32: + bytes_per_sec <<= 2; + break; + } + + threshold = (conf.threshold * bytes_per_sec) / 1000; + alsa_set_threshold (handle, threshold); + } + + obt->nchannels = nchannels; + obt->freq = freq; + obt->samples = obt_buffer_size; + + *handlep = handle; + + if (conf.verbose && + (obtfmt != req->fmt || + obt->nchannels != req->nchannels || + obt->freq != req->freq)) { + dolog ("Audio parameters for %s\n", typ); + alsa_dump_info (req, obt, obtfmt); + } + +#ifdef DEBUG + alsa_dump_info (req, obt, obtfmt); +#endif + return 0; + + err: + alsa_anal_close1 (&handle); + return -1; +} + +static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) +{ + snd_pcm_sframes_t avail; + + avail = snd_pcm_avail_update (handle); + if (avail < 0) { + if (avail == -EPIPE) { + if (!alsa_recover (handle)) { + avail = snd_pcm_avail_update (handle); + } + } + + if (avail < 0) { + alsa_logerr (avail, + "Could not obtain number of available frames\n"); + return -1; + } + } + + return avail; +} + +static void alsa_write_pending (ALSAVoiceOut *alsa) +{ + HWVoiceOut *hw = &alsa->hw; + + while (alsa->pending) { + int left_till_end_samples = hw->samples - alsa->wpos; + int len = audio_MIN (alsa->pending, left_till_end_samples); + char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift); + + while (len) { + snd_pcm_sframes_t written; + + written = snd_pcm_writei (alsa->handle, src, len); + + if (written <= 0) { + switch (written) { + case 0: + if (conf.verbose) { + dolog ("Failed to write %d frames (wrote zero)\n", len); + } + return; + + case -EPIPE: + if (alsa_recover (alsa->handle)) { + alsa_logerr (written, "Failed to write %d frames\n", + len); + return; + } + if (conf.verbose) { + dolog ("Recovering from playback xrun\n"); + } + continue; + + case -ESTRPIPE: + /* stream is suspended and waiting for an + application recovery */ + if (alsa_resume (alsa->handle)) { + alsa_logerr (written, "Failed to write %d frames\n", + len); + return; + } + if (conf.verbose) { + dolog ("Resuming suspended output stream\n"); + } + continue; + + case -EAGAIN: + return; + + default: + alsa_logerr (written, "Failed to write %d frames from %p\n", + len, src); + return; + } + } + + alsa->wpos = (alsa->wpos + written) % hw->samples; + alsa->pending -= written; + len -= written; + } + } +} + +static int alsa_run_out (HWVoiceOut *hw, int live) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + int decr; + snd_pcm_sframes_t avail; + + avail = alsa_get_avail (alsa->handle); + if (avail < 0) { + dolog ("Could not get number of available playback frames\n"); + return 0; + } + + decr = audio_MIN (live, avail); + decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending); + alsa->pending += decr; + alsa_write_pending (alsa); + return decr; +} + +static void alsa_fini_out (HWVoiceOut *hw) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + + ldebug ("alsa_fini\n"); + alsa_anal_close (&alsa->handle, &alsa->pollhlp); + + g_free(alsa->pcm_buf); + alsa->pcm_buf = NULL; +} + +static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + struct alsa_params_req req; + struct alsa_params_obt obt; + snd_pcm_t *handle; + struct audsettings obt_as; + + req.fmt = aud_to_alsafmt (as->fmt, as->endianness); + req.freq = as->freq; + req.nchannels = as->nchannels; + req.period_size = conf.period_size_out; + req.buffer_size = conf.buffer_size_out; + req.size_in_usec = conf.size_in_usec_out; + req.override_mask = + (conf.period_size_out_overridden ? 1 : 0) | + (conf.buffer_size_out_overridden ? 2 : 0); + + if (alsa_open (0, &req, &obt, &handle)) { + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.nchannels; + obt_as.fmt = obt.fmt; + obt_as.endianness = obt.endianness; + + audio_pcm_init_info (&hw->info, &obt_as); + hw->samples = obt.samples; + + alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift); + if (!alsa->pcm_buf) { + dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n", + hw->samples, 1 << hw->info.shift); + alsa_anal_close1 (&handle); + return -1; + } + + alsa->handle = handle; + return 0; +} + +#define VOICE_CTL_PAUSE 0 +#define VOICE_CTL_PREPARE 1 +#define VOICE_CTL_START 2 + +static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) +{ + int err; + + if (ctl == VOICE_CTL_PAUSE) { + err = snd_pcm_drop (handle); + if (err < 0) { + alsa_logerr (err, "Could not stop %s\n", typ); + return -1; + } + } + else { + err = snd_pcm_prepare (handle); + if (err < 0) { + alsa_logerr (err, "Could not prepare handle for %s\n", typ); + return -1; + } + if (ctl == VOICE_CTL_START) { + err = snd_pcm_start(handle); + if (err < 0) { + alsa_logerr (err, "Could not start handle for %s\n", typ); + return -1; + } + } + } + + return 0; +} + +static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + + switch (cmd) { + case VOICE_ENABLE: + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + ldebug ("enabling voice\n"); + if (poll_mode && alsa_poll_out (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE); + } + + case VOICE_DISABLE: + ldebug ("disabling voice\n"); + if (hw->poll_mode) { + hw->poll_mode = 0; + alsa_fini_poll (&alsa->pollhlp); + } + return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE); + } + + return -1; +} + +static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + struct alsa_params_req req; + struct alsa_params_obt obt; + snd_pcm_t *handle; + struct audsettings obt_as; + + req.fmt = aud_to_alsafmt (as->fmt, as->endianness); + req.freq = as->freq; + req.nchannels = as->nchannels; + req.period_size = conf.period_size_in; + req.buffer_size = conf.buffer_size_in; + req.size_in_usec = conf.size_in_usec_in; + req.override_mask = + (conf.period_size_in_overridden ? 1 : 0) | + (conf.buffer_size_in_overridden ? 2 : 0); + + if (alsa_open (1, &req, &obt, &handle)) { + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.nchannels; + obt_as.fmt = obt.fmt; + obt_as.endianness = obt.endianness; + + audio_pcm_init_info (&hw->info, &obt_as); + hw->samples = obt.samples; + + alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!alsa->pcm_buf) { + dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", + hw->samples, 1 << hw->info.shift); + alsa_anal_close1 (&handle); + return -1; + } + + alsa->handle = handle; + return 0; +} + +static void alsa_fini_in (HWVoiceIn *hw) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + + alsa_anal_close (&alsa->handle, &alsa->pollhlp); + + g_free(alsa->pcm_buf); + alsa->pcm_buf = NULL; +} + +static int alsa_run_in (HWVoiceIn *hw) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + int hwshift = hw->info.shift; + int i; + int live = audio_pcm_hw_get_live_in (hw); + int dead = hw->samples - live; + int decr; + struct { + int add; + int len; + } bufs[2] = { + { .add = hw->wpos, .len = 0 }, + { .add = 0, .len = 0 } + }; + snd_pcm_sframes_t avail; + snd_pcm_uframes_t read_samples = 0; + + if (!dead) { + return 0; + } + + avail = alsa_get_avail (alsa->handle); + if (avail < 0) { + dolog ("Could not get number of captured frames\n"); + return 0; + } + + if (!avail) { + snd_pcm_state_t state; + + state = snd_pcm_state (alsa->handle); + switch (state) { + case SND_PCM_STATE_PREPARED: + avail = hw->samples; + break; + case SND_PCM_STATE_SUSPENDED: + /* stream is suspended and waiting for an application recovery */ + if (alsa_resume (alsa->handle)) { + dolog ("Failed to resume suspended input stream\n"); + return 0; + } + if (conf.verbose) { + dolog ("Resuming suspended input stream\n"); + } + break; + default: + if (conf.verbose) { + dolog ("No frames available and ALSA state is %d\n", state); + } + return 0; + } + } + + decr = audio_MIN (dead, avail); + if (!decr) { + return 0; + } + + if (hw->wpos + decr > hw->samples) { + bufs[0].len = (hw->samples - hw->wpos); + bufs[1].len = (decr - (hw->samples - hw->wpos)); + } + else { + bufs[0].len = decr; + } + + for (i = 0; i < 2; ++i) { + void *src; + struct st_sample *dst; + snd_pcm_sframes_t nread; + snd_pcm_uframes_t len; + + len = bufs[i].len; + + src = advance (alsa->pcm_buf, bufs[i].add << hwshift); + dst = hw->conv_buf + bufs[i].add; + + while (len) { + nread = snd_pcm_readi (alsa->handle, src, len); + + if (nread <= 0) { + switch (nread) { + case 0: + if (conf.verbose) { + dolog ("Failed to read %ld frames (read zero)\n", len); + } + goto exit; + + case -EPIPE: + if (alsa_recover (alsa->handle)) { + alsa_logerr (nread, "Failed to read %ld frames\n", len); + goto exit; + } + if (conf.verbose) { + dolog ("Recovering from capture xrun\n"); + } + continue; + + case -EAGAIN: + goto exit; + + default: + alsa_logerr ( + nread, + "Failed to read %ld frames from %p\n", + len, + src + ); + goto exit; + } + } + + hw->conv (dst, src, nread); + + src = advance (src, nread << hwshift); + dst += nread; + + read_samples += nread; + len -= nread; + } + } + + exit: + hw->wpos = (hw->wpos + read_samples) % hw->samples; + return read_samples; +} + +static int alsa_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + + switch (cmd) { + case VOICE_ENABLE: + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + ldebug ("enabling voice\n"); + if (poll_mode && alsa_poll_in (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + + return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START); + } + + case VOICE_DISABLE: + ldebug ("disabling voice\n"); + if (hw->poll_mode) { + hw->poll_mode = 0; + alsa_fini_poll (&alsa->pollhlp); + } + return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE); + } + + return -1; +} + +static void *alsa_audio_init (void) +{ + return &conf; +} + +static void alsa_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_option alsa_options[] = { + { + .name = "DAC_SIZE_IN_USEC", + .tag = AUD_OPT_BOOL, + .valp = &conf.size_in_usec_out, + .descr = "DAC period/buffer size in microseconds (otherwise in frames)" + }, + { + .name = "DAC_PERIOD_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.period_size_out, + .descr = "DAC period size (0 to go with system default)", + .overriddenp = &conf.period_size_out_overridden + }, + { + .name = "DAC_BUFFER_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.buffer_size_out, + .descr = "DAC buffer size (0 to go with system default)", + .overriddenp = &conf.buffer_size_out_overridden + }, + { + .name = "ADC_SIZE_IN_USEC", + .tag = AUD_OPT_BOOL, + .valp = &conf.size_in_usec_in, + .descr = + "ADC period/buffer size in microseconds (otherwise in frames)" + }, + { + .name = "ADC_PERIOD_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.period_size_in, + .descr = "ADC period size (0 to go with system default)", + .overriddenp = &conf.period_size_in_overridden + }, + { + .name = "ADC_BUFFER_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.buffer_size_in, + .descr = "ADC buffer size (0 to go with system default)", + .overriddenp = &conf.buffer_size_in_overridden + }, + { + .name = "THRESHOLD", + .tag = AUD_OPT_INT, + .valp = &conf.threshold, + .descr = "(undocumented)" + }, + { + .name = "DAC_DEV", + .tag = AUD_OPT_STR, + .valp = &conf.pcm_name_out, + .descr = "DAC device name (for instance dmix)" + }, + { + .name = "ADC_DEV", + .tag = AUD_OPT_STR, + .valp = &conf.pcm_name_in, + .descr = "ADC device name" + }, + { + .name = "VERBOSE", + .tag = AUD_OPT_BOOL, + .valp = &conf.verbose, + .descr = "Behave in a more verbose way" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops alsa_pcm_ops = { + .init_out = alsa_init_out, + .fini_out = alsa_fini_out, + .run_out = alsa_run_out, + .write = alsa_write, + .ctl_out = alsa_ctl_out, + + .init_in = alsa_init_in, + .fini_in = alsa_fini_in, + .run_in = alsa_run_in, + .read = alsa_read, + .ctl_in = alsa_ctl_in, +}; + +struct audio_driver alsa_audio_driver = { + .name = "alsa", + .descr = "ALSA http://www.alsa-project.org", + .options = alsa_options, + .init = alsa_audio_init, + .fini = alsa_audio_fini, + .pcm_ops = &alsa_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (ALSAVoiceOut), + .voice_size_in = sizeof (ALSAVoiceIn) +}; diff --git a/audio/audio.c b/audio/audio.c new file mode 100644 index 0000000..9d018e9 --- /dev/null +++ b/audio/audio.c @@ -0,0 +1,2092 @@ +/* + * QEMU Audio subsystem + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "audio.h" +#include "monitor/monitor.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" + +#define AUDIO_CAP "audio" +#include "audio_int.h" + +/* #define DEBUG_PLIVE */ +/* #define DEBUG_LIVE */ +/* #define DEBUG_OUT */ +/* #define DEBUG_CAPTURE */ +/* #define DEBUG_POLL */ + +#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown" + + +/* Order of CONFIG_AUDIO_DRIVERS is import. + The 1st one is the one used by default, that is the reason + that we generate the list. +*/ +static struct audio_driver *drvtab[] = { +#ifdef CONFIG_SPICE + &spice_audio_driver, +#endif + CONFIG_AUDIO_DRIVERS + &no_audio_driver, + &wav_audio_driver +}; + +struct fixed_settings { + int enabled; + int nb_voices; + int greedy; + struct audsettings settings; +}; + +static struct { + struct fixed_settings fixed_out; + struct fixed_settings fixed_in; + union { + int hertz; + int64_t ticks; + } period; + int plive; + int log_to_monitor; + int try_poll_in; + int try_poll_out; +} conf = { + .fixed_out = { /* DAC fixed settings */ + .enabled = 1, + .nb_voices = 1, + .greedy = 1, + .settings = { + .freq = 44100, + .nchannels = 2, + .fmt = AUD_FMT_S16, + .endianness = AUDIO_HOST_ENDIANNESS, + } + }, + + .fixed_in = { /* ADC fixed settings */ + .enabled = 1, + .nb_voices = 1, + .greedy = 1, + .settings = { + .freq = 44100, + .nchannels = 2, + .fmt = AUD_FMT_S16, + .endianness = AUDIO_HOST_ENDIANNESS, + } + }, + + .period = { .hertz = 100 }, + .plive = 0, + .log_to_monitor = 0, + .try_poll_in = 1, + .try_poll_out = 1, +}; + +static AudioState glob_audio_state; + +const struct mixeng_volume nominal_volume = { + .mute = 0, +#ifdef FLOAT_MIXENG + .r = 1.0, + .l = 1.0, +#else + .r = 1ULL << 32, + .l = 1ULL << 32, +#endif +}; + +#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED +#error No its not +#else +static void audio_print_options (const char *prefix, + struct audio_option *opt); + +int audio_bug (const char *funcname, int cond) +{ + if (cond) { + static int shown; + + AUD_log (NULL, "A bug was just triggered in %s\n", funcname); + if (!shown) { + struct audio_driver *d; + + shown = 1; + AUD_log (NULL, "Save all your work and restart without audio\n"); + AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); + AUD_log (NULL, "I am sorry\n"); + d = glob_audio_state.drv; + if (d) { + audio_print_options (d->name, d->options); + } + } + AUD_log (NULL, "Context:\n"); + +#if defined AUDIO_BREAKPOINT_ON_BUG +# if defined HOST_I386 +# if defined __GNUC__ + __asm__ ("int3"); +# elif defined _MSC_VER + _asm _emit 0xcc; +# else + abort (); +# endif +# else + abort (); +# endif +#endif + } + + return cond; +} +#endif + +static inline int audio_bits_to_index (int bits) +{ + switch (bits) { + case 8: + return 0; + + case 16: + return 1; + + case 32: + return 2; + + default: + audio_bug ("bits_to_index", 1); + AUD_log (NULL, "invalid bits %d\n", bits); + return 0; + } +} + +void *audio_calloc (const char *funcname, int nmemb, size_t size) +{ + int cond; + size_t len; + + len = nmemb * size; + cond = !nmemb || !size; + cond |= nmemb < 0; + cond |= len < size; + + if (audio_bug ("audio_calloc", cond)) { + AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n", + funcname); + AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len); + return NULL; + } + + return g_malloc0 (len); +} + +static char *audio_alloc_prefix (const char *s) +{ + const char qemu_prefix[] = "QEMU_"; + size_t len, i; + char *r, *u; + + if (!s) { + return NULL; + } + + len = strlen (s); + r = g_malloc (len + sizeof (qemu_prefix)); + + u = r + sizeof (qemu_prefix) - 1; + + pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix); + pstrcat (r, len + sizeof (qemu_prefix), s); + + for (i = 0; i < len; ++i) { + u[i] = qemu_toupper(u[i]); + } + + return r; +} + +static const char *audio_audfmt_to_string (audfmt_e fmt) +{ + switch (fmt) { + case AUD_FMT_U8: + return "U8"; + + case AUD_FMT_U16: + return "U16"; + + case AUD_FMT_S8: + return "S8"; + + case AUD_FMT_S16: + return "S16"; + + case AUD_FMT_U32: + return "U32"; + + case AUD_FMT_S32: + return "S32"; + } + + dolog ("Bogus audfmt %d returning S16\n", fmt); + return "S16"; +} + +static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval, + int *defaultp) +{ + if (!strcasecmp (s, "u8")) { + *defaultp = 0; + return AUD_FMT_U8; + } + else if (!strcasecmp (s, "u16")) { + *defaultp = 0; + return AUD_FMT_U16; + } + else if (!strcasecmp (s, "u32")) { + *defaultp = 0; + return AUD_FMT_U32; + } + else if (!strcasecmp (s, "s8")) { + *defaultp = 0; + return AUD_FMT_S8; + } + else if (!strcasecmp (s, "s16")) { + *defaultp = 0; + return AUD_FMT_S16; + } + else if (!strcasecmp (s, "s32")) { + *defaultp = 0; + return AUD_FMT_S32; + } + else { + dolog ("Bogus audio format `%s' using %s\n", + s, audio_audfmt_to_string (defval)); + *defaultp = 1; + return defval; + } +} + +static audfmt_e audio_get_conf_fmt (const char *envname, + audfmt_e defval, + int *defaultp) +{ + const char *var = getenv (envname); + if (!var) { + *defaultp = 1; + return defval; + } + return audio_string_to_audfmt (var, defval, defaultp); +} + +static int audio_get_conf_int (const char *key, int defval, int *defaultp) +{ + int val; + char *strval; + + strval = getenv (key); + if (strval) { + *defaultp = 0; + val = atoi (strval); + return val; + } + else { + *defaultp = 1; + return defval; + } +} + +static const char *audio_get_conf_str (const char *key, + const char *defval, + int *defaultp) +{ + const char *val = getenv (key); + if (!val) { + *defaultp = 1; + return defval; + } + else { + *defaultp = 0; + return val; + } +} + +void AUD_vlog (const char *cap, const char *fmt, va_list ap) +{ + if (conf.log_to_monitor) { + if (cap) { + monitor_printf(default_mon, "%s: ", cap); + } + + monitor_vprintf(default_mon, fmt, ap); + } + else { + if (cap) { + fprintf (stderr, "%s: ", cap); + } + + vfprintf (stderr, fmt, ap); + } +} + +void AUD_log (const char *cap, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (cap, fmt, ap); + va_end (ap); +} + +static void audio_print_options (const char *prefix, + struct audio_option *opt) +{ + char *uprefix; + + if (!prefix) { + dolog ("No prefix specified\n"); + return; + } + + if (!opt) { + dolog ("No options\n"); + return; + } + + uprefix = audio_alloc_prefix (prefix); + + for (; opt->name; opt++) { + const char *state = "default"; + printf (" %s_%s: ", uprefix, opt->name); + + if (opt->overriddenp && *opt->overriddenp) { + state = "current"; + } + + switch (opt->tag) { + case AUD_OPT_BOOL: + { + int *intp = opt->valp; + printf ("boolean, %s = %d\n", state, *intp ? 1 : 0); + } + break; + + case AUD_OPT_INT: + { + int *intp = opt->valp; + printf ("integer, %s = %d\n", state, *intp); + } + break; + + case AUD_OPT_FMT: + { + audfmt_e *fmtp = opt->valp; + printf ( + "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n", + state, + audio_audfmt_to_string (*fmtp) + ); + } + break; + + case AUD_OPT_STR: + { + const char **strp = opt->valp; + printf ("string, %s = %s\n", + state, + *strp ? *strp : "(not set)"); + } + break; + + default: + printf ("???\n"); + dolog ("Bad value tag for option %s_%s %d\n", + uprefix, opt->name, opt->tag); + break; + } + printf (" %s\n", opt->descr); + } + + g_free (uprefix); +} + +static void audio_process_options (const char *prefix, + struct audio_option *opt) +{ + char *optname; + const char qemu_prefix[] = "QEMU_"; + size_t preflen, optlen; + + if (audio_bug (AUDIO_FUNC, !prefix)) { + dolog ("prefix = NULL\n"); + return; + } + + if (audio_bug (AUDIO_FUNC, !opt)) { + dolog ("opt = NULL\n"); + return; + } + + preflen = strlen (prefix); + + for (; opt->name; opt++) { + size_t len, i; + int def; + + if (!opt->valp) { + dolog ("Option value pointer for `%s' is not set\n", + opt->name); + continue; + } + + len = strlen (opt->name); + /* len of opt->name + len of prefix + size of qemu_prefix + * (includes trailing zero) + zero + underscore (on behalf of + * sizeof) */ + optlen = len + preflen + sizeof (qemu_prefix) + 1; + optname = g_malloc (optlen); + + pstrcpy (optname, optlen, qemu_prefix); + + /* copy while upper-casing, including trailing zero */ + for (i = 0; i <= preflen; ++i) { + optname[i + sizeof (qemu_prefix) - 1] = qemu_toupper(prefix[i]); + } + pstrcat (optname, optlen, "_"); + pstrcat (optname, optlen, opt->name); + + def = 1; + switch (opt->tag) { + case AUD_OPT_BOOL: + case AUD_OPT_INT: + { + int *intp = opt->valp; + *intp = audio_get_conf_int (optname, *intp, &def); + } + break; + + case AUD_OPT_FMT: + { + audfmt_e *fmtp = opt->valp; + *fmtp = audio_get_conf_fmt (optname, *fmtp, &def); + } + break; + + case AUD_OPT_STR: + { + const char **strp = opt->valp; + *strp = audio_get_conf_str (optname, *strp, &def); + } + break; + + default: + dolog ("Bad value tag for option `%s' - %d\n", + optname, opt->tag); + break; + } + + if (!opt->overriddenp) { + opt->overriddenp = &opt->overridden; + } + *opt->overriddenp = !def; + g_free (optname); + } +} + +static void audio_print_settings (struct audsettings *as) +{ + dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels); + + switch (as->fmt) { + case AUD_FMT_S8: + AUD_log (NULL, "S8"); + break; + case AUD_FMT_U8: + AUD_log (NULL, "U8"); + break; + case AUD_FMT_S16: + AUD_log (NULL, "S16"); + break; + case AUD_FMT_U16: + AUD_log (NULL, "U16"); + break; + case AUD_FMT_S32: + AUD_log (NULL, "S32"); + break; + case AUD_FMT_U32: + AUD_log (NULL, "U32"); + break; + default: + AUD_log (NULL, "invalid(%d)", as->fmt); + break; + } + + AUD_log (NULL, " endianness="); + switch (as->endianness) { + case 0: + AUD_log (NULL, "little"); + break; + case 1: + AUD_log (NULL, "big"); + break; + default: + AUD_log (NULL, "invalid"); + break; + } + AUD_log (NULL, "\n"); +} + +static int audio_validate_settings (struct audsettings *as) +{ + int invalid; + + invalid = as->nchannels != 1 && as->nchannels != 2; + invalid |= as->endianness != 0 && as->endianness != 1; + + switch (as->fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + case AUD_FMT_S16: + case AUD_FMT_U16: + case AUD_FMT_S32: + case AUD_FMT_U32: + break; + default: + invalid = 1; + break; + } + + invalid |= as->freq <= 0; + return invalid ? -1 : 0; +} + +static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as) +{ + int bits = 8, sign = 0; + + switch (as->fmt) { + case AUD_FMT_S8: + sign = 1; + /* fall through */ + case AUD_FMT_U8: + break; + + case AUD_FMT_S16: + sign = 1; + /* fall through */ + case AUD_FMT_U16: + bits = 16; + break; + + case AUD_FMT_S32: + sign = 1; + /* fall through */ + case AUD_FMT_U32: + bits = 32; + break; + } + return info->freq == as->freq + && info->nchannels == as->nchannels + && info->sign == sign + && info->bits == bits + && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS); +} + +void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) +{ + int bits = 8, sign = 0, shift = 0; + + switch (as->fmt) { + case AUD_FMT_S8: + sign = 1; + case AUD_FMT_U8: + break; + + case AUD_FMT_S16: + sign = 1; + case AUD_FMT_U16: + bits = 16; + shift = 1; + break; + + case AUD_FMT_S32: + sign = 1; + case AUD_FMT_U32: + bits = 32; + shift = 2; + break; + } + + info->freq = as->freq; + info->bits = bits; + info->sign = sign; + info->nchannels = as->nchannels; + info->shift = (as->nchannels == 2) + shift; + info->align = (1 << info->shift) - 1; + info->bytes_per_second = info->freq << info->shift; + info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS); +} + +void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) +{ + if (!len) { + return; + } + + if (info->sign) { + memset (buf, 0x00, len << info->shift); + } + else { + switch (info->bits) { + case 8: + memset (buf, 0x80, len << info->shift); + break; + + case 16: + { + int i; + uint16_t *p = buf; + int shift = info->nchannels - 1; + short s = INT16_MAX; + + if (info->swap_endianness) { + s = bswap16 (s); + } + + for (i = 0; i < len << shift; i++) { + p[i] = s; + } + } + break; + + case 32: + { + int i; + uint32_t *p = buf; + int shift = info->nchannels - 1; + int32_t s = INT32_MAX; + + if (info->swap_endianness) { + s = bswap32 (s); + } + + for (i = 0; i < len << shift; i++) { + p[i] = s; + } + } + break; + + default: + AUD_log (NULL, "audio_pcm_info_clear_buf: invalid bits %d\n", + info->bits); + break; + } + } +} + +/* + * Capture + */ +static void noop_conv (struct st_sample *dst, const void *src, int samples) +{ + (void) src; + (void) dst; + (void) samples; +} + +static CaptureVoiceOut *audio_pcm_capture_find_specific ( + struct audsettings *as + ) +{ + CaptureVoiceOut *cap; + AudioState *s = &glob_audio_state; + + for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { + if (audio_pcm_info_eq (&cap->hw.info, as)) { + return cap; + } + } + return NULL; +} + +static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd) +{ + struct capture_callback *cb; + +#ifdef DEBUG_CAPTURE + dolog ("notification %d sent\n", cmd); +#endif + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { + cb->ops.notify (cb->opaque, cmd); + } +} + +static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled) +{ + if (cap->hw.enabled != enabled) { + audcnotification_e cmd; + cap->hw.enabled = enabled; + cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE; + audio_notify_capture (cap, cmd); + } +} + +static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap) +{ + HWVoiceOut *hw = &cap->hw; + SWVoiceOut *sw; + int enabled = 0; + + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (sw->active) { + enabled = 1; + break; + } + } + audio_capture_maybe_changed (cap, enabled); +} + +static void audio_detach_capture (HWVoiceOut *hw) +{ + SWVoiceCap *sc = hw->cap_head.lh_first; + + while (sc) { + SWVoiceCap *sc1 = sc->entries.le_next; + SWVoiceOut *sw = &sc->sw; + CaptureVoiceOut *cap = sc->cap; + int was_active = sw->active; + + if (sw->rate) { + st_rate_stop (sw->rate); + sw->rate = NULL; + } + + QLIST_REMOVE (sw, entries); + QLIST_REMOVE (sc, entries); + g_free (sc); + if (was_active) { + /* We have removed soft voice from the capture: + this might have changed the overall status of the capture + since this might have been the only active voice */ + audio_recalc_and_notify_capture (cap); + } + sc = sc1; + } +} + +static int audio_attach_capture (HWVoiceOut *hw) +{ + AudioState *s = &glob_audio_state; + CaptureVoiceOut *cap; + + audio_detach_capture (hw); + for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { + SWVoiceCap *sc; + SWVoiceOut *sw; + HWVoiceOut *hw_cap = &cap->hw; + + sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc)); + if (!sc) { + dolog ("Could not allocate soft capture voice (%zu bytes)\n", + sizeof (*sc)); + return -1; + } + + sc->cap = cap; + sw = &sc->sw; + sw->hw = hw_cap; + sw->info = hw->info; + sw->empty = 1; + sw->active = hw->enabled; + sw->conv = noop_conv; + sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq; + sw->vol = nominal_volume; + sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq); + if (!sw->rate) { + dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw)); + g_free (sw); + return -1; + } + QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries); + QLIST_INSERT_HEAD (&hw->cap_head, sc, entries); +#ifdef DEBUG_CAPTURE + sw->name = g_strdup_printf ("for %p %d,%d,%d", + hw, sw->info.freq, sw->info.bits, + sw->info.nchannels); + dolog ("Added %s active = %d\n", sw->name, sw->active); +#endif + if (sw->active) { + audio_capture_maybe_changed (cap, 1); + } + } + return 0; +} + +/* + * Hard voice (capture) + */ +static int audio_pcm_hw_find_min_in (HWVoiceIn *hw) +{ + SWVoiceIn *sw; + int m = hw->total_samples_captured; + + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (sw->active) { + m = audio_MIN (m, sw->total_hw_samples_acquired); + } + } + return m; +} + +int audio_pcm_hw_get_live_in (HWVoiceIn *hw) +{ + int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + return live; +} + +int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, + int live, int pending) +{ + int left = hw->samples - pending; + int len = audio_MIN (left, live); + int clipped = 0; + + while (len) { + struct st_sample *src = hw->mix_buf + hw->rpos; + uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift); + int samples_till_end_of_buf = hw->samples - hw->rpos; + int samples_to_clip = audio_MIN (len, samples_till_end_of_buf); + + hw->clip (dst, src, samples_to_clip); + + hw->rpos = (hw->rpos + samples_to_clip) % hw->samples; + len -= samples_to_clip; + clipped += samples_to_clip; + } + return clipped; +} + +/* + * Soft voice (capture) + */ +static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw) +{ + HWVoiceIn *hw = sw->hw; + int live = hw->total_samples_captured - sw->total_hw_samples_acquired; + int rpos; + + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + + rpos = hw->wpos - live; + if (rpos >= 0) { + return rpos; + } + else { + return hw->samples + rpos; + } +} + +int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) +{ + HWVoiceIn *hw = sw->hw; + int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; + struct st_sample *src, *dst = sw->buf; + + rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples; + + live = hw->total_samples_captured - sw->total_hw_samples_acquired; + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live_in=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + + samples = size >> sw->info.shift; + if (!live) { + return 0; + } + + swlim = (live * sw->ratio) >> 32; + swlim = audio_MIN (swlim, samples); + + while (swlim) { + src = hw->conv_buf + rpos; + isamp = hw->wpos - rpos; + /* XXX: <= ? */ + if (isamp <= 0) { + isamp = hw->samples - rpos; + } + + if (!isamp) { + break; + } + osamp = swlim; + + if (audio_bug (AUDIO_FUNC, osamp < 0)) { + dolog ("osamp=%d\n", osamp); + return 0; + } + + st_rate_flow (sw->rate, src, dst, &isamp, &osamp); + swlim -= osamp; + rpos = (rpos + isamp) % hw->samples; + dst += osamp; + ret += osamp; + total += isamp; + } + + if (!(hw->ctl_caps & VOICE_VOLUME_CAP)) { + mixeng_volume (sw->buf, ret, &sw->vol); + } + + sw->clip (buf, sw->buf, ret); + sw->total_hw_samples_acquired += total; + return ret << sw->info.shift; +} + +/* + * Hard voice (playback) + */ +static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) +{ + SWVoiceOut *sw; + int m = INT_MAX; + int nb_live = 0; + + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (sw->active || !sw->empty) { + m = audio_MIN (m, sw->total_hw_samples_mixed); + nb_live += 1; + } + } + + *nb_livep = nb_live; + return m; +} + +static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live) +{ + int smin; + int nb_live1; + + smin = audio_pcm_hw_find_min_out (hw, &nb_live1); + if (nb_live) { + *nb_live = nb_live1; + } + + if (nb_live1) { + int live = smin; + + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + return live; + } + return 0; +} + +/* + * Soft voice (playback) + */ +int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) +{ + int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; + int ret = 0, pos = 0, total = 0; + + if (!sw) { + return size; + } + + hwsamples = sw->hw->samples; + + live = sw->total_hw_samples_mixed; + if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){ + dolog ("live=%d hw->samples=%d\n", live, hwsamples); + return 0; + } + + if (live == hwsamples) { +#ifdef DEBUG_OUT + dolog ("%s is full %d\n", sw->name, live); +#endif + return 0; + } + + wpos = (sw->hw->rpos + live) % hwsamples; + samples = size >> sw->info.shift; + + dead = hwsamples - live; + swlim = ((int64_t) dead << 32) / sw->ratio; + swlim = audio_MIN (swlim, samples); + if (swlim) { + sw->conv (sw->buf, buf, swlim); + + if (!(sw->hw->ctl_caps & VOICE_VOLUME_CAP)) { + mixeng_volume (sw->buf, swlim, &sw->vol); + } + } + + while (swlim) { + dead = hwsamples - live; + left = hwsamples - wpos; + blck = audio_MIN (dead, left); + if (!blck) { + break; + } + isamp = swlim; + osamp = blck; + st_rate_flow_mix ( + sw->rate, + sw->buf + pos, + sw->hw->mix_buf + wpos, + &isamp, + &osamp + ); + ret += isamp; + swlim -= isamp; + pos += isamp; + live += osamp; + wpos = (wpos + osamp) % hwsamples; + total += osamp; + } + + sw->total_hw_samples_mixed += total; + sw->empty = sw->total_hw_samples_mixed == 0; + +#ifdef DEBUG_OUT + dolog ( + "%s: write size %d ret %d total sw %d\n", + SW_NAME (sw), + size >> sw->info.shift, + ret, + sw->total_hw_samples_mixed + ); +#endif + + return ret << sw->info.shift; +} + +#ifdef DEBUG_AUDIO +static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) +{ + dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n", + cap, info->bits, info->sign, info->freq, info->nchannels); +} +#endif + +#define DAC +#include "audio_template.h" +#undef DAC +#include "audio_template.h" + +/* + * Timer + */ +static int audio_is_timer_needed (void) +{ + HWVoiceIn *hwi = NULL; + HWVoiceOut *hwo = NULL; + + while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { + if (!hwo->poll_mode) return 1; + } + while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { + if (!hwi->poll_mode) return 1; + } + return 0; +} + +static void audio_reset_timer (AudioState *s) +{ + if (audio_is_timer_needed ()) { + timer_mod (s->ts, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); + } + else { + timer_del (s->ts); + } +} + +static void audio_timer (void *opaque) +{ + audio_run ("timer"); + audio_reset_timer (opaque); +} + +/* + * Public API + */ +int AUD_write (SWVoiceOut *sw, void *buf, int size) +{ + int bytes; + + if (!sw) { + /* XXX: Consider options */ + return size; + } + + if (!sw->hw->enabled) { + dolog ("Writing to disabled voice %s\n", SW_NAME (sw)); + return 0; + } + + bytes = sw->hw->pcm_ops->write (sw, buf, size); + return bytes; +} + +int AUD_read (SWVoiceIn *sw, void *buf, int size) +{ + int bytes; + + if (!sw) { + /* XXX: Consider options */ + return size; + } + + if (!sw->hw->enabled) { + dolog ("Reading from disabled voice %s\n", SW_NAME (sw)); + return 0; + } + + bytes = sw->hw->pcm_ops->read (sw, buf, size); + return bytes; +} + +int AUD_get_buffer_size_out (SWVoiceOut *sw) +{ + return sw->hw->samples << sw->hw->info.shift; +} + +void AUD_set_active_out (SWVoiceOut *sw, int on) +{ + HWVoiceOut *hw; + + if (!sw) { + return; + } + + hw = sw->hw; + if (sw->active != on) { + AudioState *s = &glob_audio_state; + SWVoiceOut *temp_sw; + SWVoiceCap *sc; + + if (on) { + hw->pending_disable = 0; + if (!hw->enabled) { + hw->enabled = 1; + if (s->vm_running) { + hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out); + audio_reset_timer (s); + } + } + } + else { + if (hw->enabled) { + int nb_active = 0; + + for (temp_sw = hw->sw_head.lh_first; temp_sw; + temp_sw = temp_sw->entries.le_next) { + nb_active += temp_sw->active != 0; + } + + hw->pending_disable = nb_active == 1; + } + } + + for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { + sc->sw.active = hw->enabled; + if (hw->enabled) { + audio_capture_maybe_changed (sc->cap, 1); + } + } + sw->active = on; + } +} + +void AUD_set_active_in (SWVoiceIn *sw, int on) +{ + HWVoiceIn *hw; + + if (!sw) { + return; + } + + hw = sw->hw; + if (sw->active != on) { + AudioState *s = &glob_audio_state; + SWVoiceIn *temp_sw; + + if (on) { + if (!hw->enabled) { + hw->enabled = 1; + if (s->vm_running) { + hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in); + audio_reset_timer (s); + } + } + sw->total_hw_samples_acquired = hw->total_samples_captured; + } + else { + if (hw->enabled) { + int nb_active = 0; + + for (temp_sw = hw->sw_head.lh_first; temp_sw; + temp_sw = temp_sw->entries.le_next) { + nb_active += temp_sw->active != 0; + } + + if (nb_active == 1) { + hw->enabled = 0; + hw->pcm_ops->ctl_in (hw, VOICE_DISABLE); + } + } + } + sw->active = on; + } +} + +static int audio_get_avail (SWVoiceIn *sw) +{ + int live; + + if (!sw) { + return 0; + } + + live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; + if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { + dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); + return 0; + } + + ldebug ( + "%s: get_avail live %d ret %" PRId64 "\n", + SW_NAME (sw), + live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift + ); + + return (((int64_t) live << 32) / sw->ratio) << sw->info.shift; +} + +static int audio_get_free (SWVoiceOut *sw) +{ + int live, dead; + + if (!sw) { + return 0; + } + + live = sw->total_hw_samples_mixed; + + if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { + dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); + return 0; + } + + dead = sw->hw->samples - live; + +#ifdef DEBUG_OUT + dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n", + SW_NAME (sw), + live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift); +#endif + + return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift; +} + +static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) +{ + int n; + + if (hw->enabled) { + SWVoiceCap *sc; + + for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { + SWVoiceOut *sw = &sc->sw; + int rpos2 = rpos; + + n = samples; + while (n) { + int till_end_of_hw = hw->samples - rpos2; + int to_write = audio_MIN (till_end_of_hw, n); + int bytes = to_write << hw->info.shift; + int written; + + sw->buf = hw->mix_buf + rpos2; + written = audio_pcm_sw_write (sw, NULL, bytes); + if (written - bytes) { + dolog ("Could not mix %d bytes into a capture " + "buffer, mixed %d\n", + bytes, written); + break; + } + n -= to_write; + rpos2 = (rpos2 + to_write) % hw->samples; + } + } + } + + n = audio_MIN (samples, hw->samples - rpos); + mixeng_clear (hw->mix_buf + rpos, n); + mixeng_clear (hw->mix_buf, samples - n); +} + +static void audio_run_out (AudioState *s) +{ + HWVoiceOut *hw = NULL; + SWVoiceOut *sw; + + while ((hw = audio_pcm_hw_find_any_enabled_out (hw))) { + int played; + int live, free, nb_live, cleanup_required, prev_rpos; + + live = audio_pcm_hw_get_live_out (hw, &nb_live); + if (!nb_live) { + live = 0; + } + + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + continue; + } + + if (hw->pending_disable && !nb_live) { + SWVoiceCap *sc; +#ifdef DEBUG_OUT + dolog ("Disabling voice\n"); +#endif + hw->enabled = 0; + hw->pending_disable = 0; + hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); + for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { + sc->sw.active = 0; + audio_recalc_and_notify_capture (sc->cap); + } + continue; + } + + if (!live) { + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (sw->active) { + free = audio_get_free (sw); + if (free > 0) { + sw->callback.fn (sw->callback.opaque, free); + } + } + } + continue; + } + + prev_rpos = hw->rpos; + played = hw->pcm_ops->run_out (hw, live); + if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { + dolog ("hw->rpos=%d hw->samples=%d played=%d\n", + hw->rpos, hw->samples, played); + hw->rpos = 0; + } + +#ifdef DEBUG_OUT + dolog ("played=%d\n", played); +#endif + + if (played) { + hw->ts_helper += played; + audio_capture_mix_and_clear (hw, prev_rpos, played); + } + + cleanup_required = 0; + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (!sw->active && sw->empty) { + continue; + } + + if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) { + dolog ("played=%d sw->total_hw_samples_mixed=%d\n", + played, sw->total_hw_samples_mixed); + played = sw->total_hw_samples_mixed; + } + + sw->total_hw_samples_mixed -= played; + + if (!sw->total_hw_samples_mixed) { + sw->empty = 1; + cleanup_required |= !sw->active && !sw->callback.fn; + } + + if (sw->active) { + free = audio_get_free (sw); + if (free > 0) { + sw->callback.fn (sw->callback.opaque, free); + } + } + } + + if (cleanup_required) { + SWVoiceOut *sw1; + + sw = hw->sw_head.lh_first; + while (sw) { + sw1 = sw->entries.le_next; + if (!sw->active && !sw->callback.fn) { +#ifdef DEBUG_PLIVE + dolog ("Finishing with old voice\n"); +#endif + audio_close_out (sw); + } + sw = sw1; + } + } + } +} + +static void audio_run_in (AudioState *s) +{ + HWVoiceIn *hw = NULL; + + while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) { + SWVoiceIn *sw; + int captured, min; + + captured = hw->pcm_ops->run_in (hw); + + min = audio_pcm_hw_find_min_in (hw); + hw->total_samples_captured += captured - min; + hw->ts_helper += captured; + + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + sw->total_hw_samples_acquired -= min; + + if (sw->active) { + int avail; + + avail = audio_get_avail (sw); + if (avail > 0) { + sw->callback.fn (sw->callback.opaque, avail); + } + } + } + } +} + +static void audio_run_capture (AudioState *s) +{ + CaptureVoiceOut *cap; + + for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { + int live, rpos, captured; + HWVoiceOut *hw = &cap->hw; + SWVoiceOut *sw; + + captured = live = audio_pcm_hw_get_live_out (hw, NULL); + rpos = hw->rpos; + while (live) { + int left = hw->samples - rpos; + int to_capture = audio_MIN (live, left); + struct st_sample *src; + struct capture_callback *cb; + + src = hw->mix_buf + rpos; + hw->clip (cap->buf, src, to_capture); + mixeng_clear (src, to_capture); + + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { + cb->ops.capture (cb->opaque, cap->buf, + to_capture << hw->info.shift); + } + rpos = (rpos + to_capture) % hw->samples; + live -= to_capture; + } + hw->rpos = rpos; + + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (!sw->active && sw->empty) { + continue; + } + + if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) { + dolog ("captured=%d sw->total_hw_samples_mixed=%d\n", + captured, sw->total_hw_samples_mixed); + captured = sw->total_hw_samples_mixed; + } + + sw->total_hw_samples_mixed -= captured; + sw->empty = sw->total_hw_samples_mixed == 0; + } + } +} + +void audio_run (const char *msg) +{ + AudioState *s = &glob_audio_state; + + audio_run_out (s); + audio_run_in (s); + audio_run_capture (s); +#ifdef DEBUG_POLL + { + static double prevtime; + double currtime; + struct timeval tv; + + if (gettimeofday (&tv, NULL)) { + perror ("audio_run: gettimeofday"); + return; + } + + currtime = tv.tv_sec + tv.tv_usec * 1e-6; + dolog ("Elapsed since last %s: %f\n", msg, currtime - prevtime); + prevtime = currtime; + } +#endif +} + +static struct audio_option audio_options[] = { + /* DAC */ + { + .name = "DAC_FIXED_SETTINGS", + .tag = AUD_OPT_BOOL, + .valp = &conf.fixed_out.enabled, + .descr = "Use fixed settings for host DAC" + }, + { + .name = "DAC_FIXED_FREQ", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_out.settings.freq, + .descr = "Frequency for fixed host DAC" + }, + { + .name = "DAC_FIXED_FMT", + .tag = AUD_OPT_FMT, + .valp = &conf.fixed_out.settings.fmt, + .descr = "Format for fixed host DAC" + }, + { + .name = "DAC_FIXED_CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_out.settings.nchannels, + .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)" + }, + { + .name = "DAC_VOICES", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_out.nb_voices, + .descr = "Number of voices for DAC" + }, + { + .name = "DAC_TRY_POLL", + .tag = AUD_OPT_BOOL, + .valp = &conf.try_poll_out, + .descr = "Attempt using poll mode for DAC" + }, + /* ADC */ + { + .name = "ADC_FIXED_SETTINGS", + .tag = AUD_OPT_BOOL, + .valp = &conf.fixed_in.enabled, + .descr = "Use fixed settings for host ADC" + }, + { + .name = "ADC_FIXED_FREQ", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_in.settings.freq, + .descr = "Frequency for fixed host ADC" + }, + { + .name = "ADC_FIXED_FMT", + .tag = AUD_OPT_FMT, + .valp = &conf.fixed_in.settings.fmt, + .descr = "Format for fixed host ADC" + }, + { + .name = "ADC_FIXED_CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_in.settings.nchannels, + .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)" + }, + { + .name = "ADC_VOICES", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_in.nb_voices, + .descr = "Number of voices for ADC" + }, + { + .name = "ADC_TRY_POLL", + .tag = AUD_OPT_BOOL, + .valp = &conf.try_poll_in, + .descr = "Attempt using poll mode for ADC" + }, + /* Misc */ + { + .name = "TIMER_PERIOD", + .tag = AUD_OPT_INT, + .valp = &conf.period.hertz, + .descr = "Timer period in HZ (0 - use lowest possible)" + }, + { + .name = "PLIVE", + .tag = AUD_OPT_BOOL, + .valp = &conf.plive, + .descr = "(undocumented)" + }, + { + .name = "LOG_TO_MONITOR", + .tag = AUD_OPT_BOOL, + .valp = &conf.log_to_monitor, + .descr = "Print logging messages to monitor instead of stderr" + }, + { /* End of list */ } +}; + +static void audio_pp_nb_voices (const char *typ, int nb) +{ + switch (nb) { + case 0: + printf ("Does not support %s\n", typ); + break; + case 1: + printf ("One %s voice\n", typ); + break; + case INT_MAX: + printf ("Theoretically supports many %s voices\n", typ); + break; + default: + printf ("Theoretically supports up to %d %s voices\n", nb, typ); + break; + } + +} + +void AUD_help (void) +{ + size_t i; + + audio_process_options ("AUDIO", audio_options); + for (i = 0; i < ARRAY_SIZE (drvtab); i++) { + struct audio_driver *d = drvtab[i]; + if (d->options) { + audio_process_options (d->name, d->options); + } + } + + printf ("Audio options:\n"); + audio_print_options ("AUDIO", audio_options); + printf ("\n"); + + printf ("Available drivers:\n"); + + for (i = 0; i < ARRAY_SIZE (drvtab); i++) { + struct audio_driver *d = drvtab[i]; + + printf ("Name: %s\n", d->name); + printf ("Description: %s\n", d->descr); + + audio_pp_nb_voices ("playback", d->max_voices_out); + audio_pp_nb_voices ("capture", d->max_voices_in); + + if (d->options) { + printf ("Options:\n"); + audio_print_options (d->name, d->options); + } + else { + printf ("No options\n"); + } + printf ("\n"); + } + + printf ( + "Options are settable through environment variables.\n" + "Example:\n" +#ifdef _WIN32 + " set QEMU_AUDIO_DRV=wav\n" + " set QEMU_WAV_PATH=c:\\tune.wav\n" +#else + " export QEMU_AUDIO_DRV=wav\n" + " export QEMU_WAV_PATH=$HOME/tune.wav\n" + "(for csh replace export with setenv in the above)\n" +#endif + " qemu ...\n\n" + ); +} + +static int audio_driver_init (AudioState *s, struct audio_driver *drv) +{ + if (drv->options) { + audio_process_options (drv->name, drv->options); + } + s->drv_opaque = drv->init (); + + if (s->drv_opaque) { + audio_init_nb_voices_out (drv); + audio_init_nb_voices_in (drv); + s->drv = drv; + return 0; + } + else { + dolog ("Could not init `%s' audio driver\n", drv->name); + return -1; + } +} + +static void audio_vm_change_state_handler (void *opaque, int running, + RunState state) +{ + AudioState *s = opaque; + HWVoiceOut *hwo = NULL; + HWVoiceIn *hwi = NULL; + int op = running ? VOICE_ENABLE : VOICE_DISABLE; + + s->vm_running = running; + while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { + hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out); + } + + while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { + hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); + } + audio_reset_timer (s); +} + +static void audio_atexit (void) +{ + AudioState *s = &glob_audio_state; + HWVoiceOut *hwo = NULL; + HWVoiceIn *hwi = NULL; + + while ((hwo = audio_pcm_hw_find_any_out (hwo))) { + SWVoiceCap *sc; + + if (hwo->enabled) { + hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); + } + hwo->pcm_ops->fini_out (hwo); + + for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) { + CaptureVoiceOut *cap = sc->cap; + struct capture_callback *cb; + + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { + cb->ops.destroy (cb->opaque); + } + } + } + + while ((hwi = audio_pcm_hw_find_any_in (hwi))) { + if (hwi->enabled) { + hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE); + } + hwi->pcm_ops->fini_in (hwi); + } + + if (s->drv) { + s->drv->fini (s->drv_opaque); + } +} + +static const VMStateDescription vmstate_audio = { + .name = "audio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +static void audio_init (void) +{ + size_t i; + int done = 0; + const char *drvname; + VMChangeStateEntry *e; + AudioState *s = &glob_audio_state; + + if (s->drv) { + return; + } + + QLIST_INIT (&s->hw_head_out); + QLIST_INIT (&s->hw_head_in); + QLIST_INIT (&s->cap_head); + atexit (audio_atexit); + + s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s); + if (!s->ts) { + hw_error("Could not create audio timer\n"); + } + + audio_process_options ("AUDIO", audio_options); + + s->nb_hw_voices_out = conf.fixed_out.nb_voices; + s->nb_hw_voices_in = conf.fixed_in.nb_voices; + + if (s->nb_hw_voices_out <= 0) { + dolog ("Bogus number of playback voices %d, setting to 1\n", + s->nb_hw_voices_out); + s->nb_hw_voices_out = 1; + } + + if (s->nb_hw_voices_in <= 0) { + dolog ("Bogus number of capture voices %d, setting to 0\n", + s->nb_hw_voices_in); + s->nb_hw_voices_in = 0; + } + + { + int def; + drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); + } + + if (drvname) { + int found = 0; + + for (i = 0; i < ARRAY_SIZE (drvtab); i++) { + if (!strcmp (drvname, drvtab[i]->name)) { + done = !audio_driver_init (s, drvtab[i]); + found = 1; + break; + } + } + + if (!found) { + dolog ("Unknown audio driver `%s'\n", drvname); + dolog ("Run with -audio-help to list available drivers\n"); + } + } + + if (!done) { + for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) { + if (drvtab[i]->can_be_default) { + done = !audio_driver_init (s, drvtab[i]); + } + } + } + + if (!done) { + done = !audio_driver_init (s, &no_audio_driver); + if (!done) { + hw_error("Could not initialize audio subsystem\n"); + } + else { + dolog ("warning: Using timer based audio emulation\n"); + } + } + + if (conf.period.hertz <= 0) { + if (conf.period.hertz < 0) { + dolog ("warning: Timer period is negative - %d " + "treating as zero\n", + conf.period.hertz); + } + conf.period.ticks = 1; + } else { + conf.period.ticks = + muldiv64 (1, get_ticks_per_sec (), conf.period.hertz); + } + + e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); + if (!e) { + dolog ("warning: Could not register change state handler\n" + "(Audio can continue looping even after stopping the VM)\n"); + } + + QLIST_INIT (&s->card_head); + vmstate_register (NULL, 0, &vmstate_audio, s); +} + +void AUD_register_card (const char *name, QEMUSoundCard *card) +{ + audio_init (); + card->name = g_strdup (name); + memset (&card->entries, 0, sizeof (card->entries)); + QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries); +} + +void AUD_remove_card (QEMUSoundCard *card) +{ + QLIST_REMOVE (card, entries); + g_free (card->name); +} + + +CaptureVoiceOut *AUD_add_capture ( + struct audsettings *as, + struct audio_capture_ops *ops, + void *cb_opaque + ) +{ + AudioState *s = &glob_audio_state; + CaptureVoiceOut *cap; + struct capture_callback *cb; + + if (audio_validate_settings (as)) { + dolog ("Invalid settings were passed when trying to add capture\n"); + audio_print_settings (as); + goto err0; + } + + cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb)); + if (!cb) { + dolog ("Could not allocate capture callback information, size %zu\n", + sizeof (*cb)); + goto err0; + } + cb->ops = *ops; + cb->opaque = cb_opaque; + + cap = audio_pcm_capture_find_specific (as); + if (cap) { + QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); + return cap; + } + else { + HWVoiceOut *hw; + CaptureVoiceOut *cap; + + cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap)); + if (!cap) { + dolog ("Could not allocate capture voice, size %zu\n", + sizeof (*cap)); + goto err1; + } + + hw = &cap->hw; + QLIST_INIT (&hw->sw_head); + QLIST_INIT (&cap->cb_head); + + /* XXX find a more elegant way */ + hw->samples = 4096 * 4; + hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples, + sizeof (struct st_sample)); + if (!hw->mix_buf) { + dolog ("Could not allocate capture mix buffer (%d samples)\n", + hw->samples); + goto err2; + } + + audio_pcm_init_info (&hw->info, as); + + cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!cap->buf) { + dolog ("Could not allocate capture buffer " + "(%d samples, each %d bytes)\n", + hw->samples, 1 << hw->info.shift); + goto err3; + } + + hw->clip = mixeng_clip + [hw->info.nchannels == 2] + [hw->info.sign] + [hw->info.swap_endianness] + [audio_bits_to_index (hw->info.bits)]; + + QLIST_INSERT_HEAD (&s->cap_head, cap, entries); + QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); + + hw = NULL; + while ((hw = audio_pcm_hw_find_any_out (hw))) { + audio_attach_capture (hw); + } + return cap; + + err3: + g_free (cap->hw.mix_buf); + err2: + g_free (cap); + err1: + g_free (cb); + err0: + return NULL; + } +} + +void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) +{ + struct capture_callback *cb; + + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { + if (cb->opaque == cb_opaque) { + cb->ops.destroy (cb_opaque); + QLIST_REMOVE (cb, entries); + g_free (cb); + + if (!cap->cb_head.lh_first) { + SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1; + + while (sw) { + SWVoiceCap *sc = (SWVoiceCap *) sw; +#ifdef DEBUG_CAPTURE + dolog ("freeing %s\n", sw->name); +#endif + + sw1 = sw->entries.le_next; + if (sw->rate) { + st_rate_stop (sw->rate); + sw->rate = NULL; + } + QLIST_REMOVE (sw, entries); + QLIST_REMOVE (sc, entries); + g_free (sc); + sw = sw1; + } + QLIST_REMOVE (cap, entries); + g_free (cap); + } + return; + } + } +} + +void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol) +{ + if (sw) { + HWVoiceOut *hw = sw->hw; + + sw->vol.mute = mute; + sw->vol.l = nominal_volume.l * lvol / 255; + sw->vol.r = nominal_volume.r * rvol / 255; + + if (hw->pcm_ops->ctl_out) { + hw->pcm_ops->ctl_out (hw, VOICE_VOLUME, sw); + } + } +} + +void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol) +{ + if (sw) { + HWVoiceIn *hw = sw->hw; + + sw->vol.mute = mute; + sw->vol.l = nominal_volume.l * lvol / 255; + sw->vol.r = nominal_volume.r * rvol / 255; + + if (hw->pcm_ops->ctl_in) { + hw->pcm_ops->ctl_in (hw, VOICE_VOLUME, sw); + } + } +} diff --git a/audio/audio.h b/audio/audio.h new file mode 100644 index 0000000..e7ea397 --- /dev/null +++ b/audio/audio.h @@ -0,0 +1,166 @@ +/* + * QEMU Audio subsystem header + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_AUDIO_H +#define QEMU_AUDIO_H + +#include "config-host.h" +#include "qemu/queue.h" + +typedef void (*audio_callback_fn) (void *opaque, int avail); + +typedef enum { + AUD_FMT_U8, + AUD_FMT_S8, + AUD_FMT_U16, + AUD_FMT_S16, + AUD_FMT_U32, + AUD_FMT_S32 +} audfmt_e; + +#ifdef HOST_WORDS_BIGENDIAN +#define AUDIO_HOST_ENDIANNESS 1 +#else +#define AUDIO_HOST_ENDIANNESS 0 +#endif + +struct audsettings { + int freq; + int nchannels; + audfmt_e fmt; + int endianness; +}; + +typedef enum { + AUD_CNOTIFY_ENABLE, + AUD_CNOTIFY_DISABLE +} audcnotification_e; + +struct audio_capture_ops { + void (*notify) (void *opaque, audcnotification_e cmd); + void (*capture) (void *opaque, void *buf, int size); + void (*destroy) (void *opaque); +}; + +struct capture_ops { + void (*info) (void *opaque); + void (*destroy) (void *opaque); +}; + +typedef struct CaptureState { + void *opaque; + struct capture_ops ops; + QLIST_ENTRY (CaptureState) entries; +} CaptureState; + +typedef struct SWVoiceOut SWVoiceOut; +typedef struct CaptureVoiceOut CaptureVoiceOut; +typedef struct SWVoiceIn SWVoiceIn; + +typedef struct QEMUSoundCard { + char *name; + QLIST_ENTRY (QEMUSoundCard) entries; +} QEMUSoundCard; + +typedef struct QEMUAudioTimeStamp { + uint64_t old_ts; +} QEMUAudioTimeStamp; + +void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); +void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); + +void AUD_help (void); +void AUD_register_card (const char *name, QEMUSoundCard *card); +void AUD_remove_card (QEMUSoundCard *card); +CaptureVoiceOut *AUD_add_capture ( + struct audsettings *as, + struct audio_capture_ops *ops, + void *opaque + ); +void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque); + +SWVoiceOut *AUD_open_out ( + QEMUSoundCard *card, + SWVoiceOut *sw, + const char *name, + void *callback_opaque, + audio_callback_fn callback_fn, + struct audsettings *settings + ); + +void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); +int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size); +int AUD_get_buffer_size_out (SWVoiceOut *sw); +void AUD_set_active_out (SWVoiceOut *sw, int on); +int AUD_is_active_out (SWVoiceOut *sw); + +void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); +uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); + +void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol); +void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol); + +SWVoiceIn *AUD_open_in ( + QEMUSoundCard *card, + SWVoiceIn *sw, + const char *name, + void *callback_opaque, + audio_callback_fn callback_fn, + struct audsettings *settings + ); + +void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); +int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size); +void AUD_set_active_in (SWVoiceIn *sw, int on); +int AUD_is_active_in (SWVoiceIn *sw); + +void AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts); +uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts); + +static inline void *advance (void *p, int incr) +{ + uint8_t *d = p; + return (d + incr); +} + +#ifdef __GNUC__ +#define audio_MIN(a, b) ( __extension__ ({ \ + __typeof (a) ta = a; \ + __typeof (b) tb = b; \ + ((ta)>(tb)?(tb):(ta)); \ +})) + +#define audio_MAX(a, b) ( __extension__ ({ \ + __typeof (a) ta = a; \ + __typeof (b) tb = b; \ + ((ta)<(tb)?(tb):(ta)); \ +})) +#else +#define audio_MIN(a, b) ((a)>(b)?(b):(a)) +#define audio_MAX(a, b) ((a)<(b)?(b):(a)) +#endif + +int wav_start_capture (CaptureState *s, const char *path, int freq, + int bits, int nchannels); + +#endif /* audio.h */ diff --git a/audio/audio_int.h b/audio/audio_int.h new file mode 100644 index 0000000..fd019a0 --- /dev/null +++ b/audio/audio_int.h @@ -0,0 +1,263 @@ +/* + * QEMU Audio subsystem header + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_AUDIO_INT_H +#define QEMU_AUDIO_INT_H + +#ifdef CONFIG_COREAUDIO +#define FLOAT_MIXENG +/* #define RECIPROCAL */ +#endif +#include "mixeng.h" + +struct audio_pcm_ops; + +typedef enum { + AUD_OPT_INT, + AUD_OPT_FMT, + AUD_OPT_STR, + AUD_OPT_BOOL +} audio_option_tag_e; + +struct audio_option { + const char *name; + audio_option_tag_e tag; + void *valp; + const char *descr; + int *overriddenp; + int overridden; +}; + +struct audio_callback { + void *opaque; + audio_callback_fn fn; +}; + +struct audio_pcm_info { + int bits; + int sign; + int freq; + int nchannels; + int align; + int shift; + int bytes_per_second; + int swap_endianness; +}; + +typedef struct SWVoiceCap SWVoiceCap; + +typedef struct HWVoiceOut { + int enabled; + int poll_mode; + int pending_disable; + struct audio_pcm_info info; + + f_sample *clip; + + int rpos; + uint64_t ts_helper; + + struct st_sample *mix_buf; + + int samples; + QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; + QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; + int ctl_caps; + struct audio_pcm_ops *pcm_ops; + QLIST_ENTRY (HWVoiceOut) entries; +} HWVoiceOut; + +typedef struct HWVoiceIn { + int enabled; + int poll_mode; + struct audio_pcm_info info; + + t_sample *conv; + + int wpos; + int total_samples_captured; + uint64_t ts_helper; + + struct st_sample *conv_buf; + + int samples; + QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; + int ctl_caps; + struct audio_pcm_ops *pcm_ops; + QLIST_ENTRY (HWVoiceIn) entries; +} HWVoiceIn; + +struct SWVoiceOut { + QEMUSoundCard *card; + struct audio_pcm_info info; + t_sample *conv; + int64_t ratio; + struct st_sample *buf; + void *rate; + int total_hw_samples_mixed; + int active; + int empty; + HWVoiceOut *hw; + char *name; + struct mixeng_volume vol; + struct audio_callback callback; + QLIST_ENTRY (SWVoiceOut) entries; +}; + +struct SWVoiceIn { + QEMUSoundCard *card; + int active; + struct audio_pcm_info info; + int64_t ratio; + void *rate; + int total_hw_samples_acquired; + struct st_sample *buf; + f_sample *clip; + HWVoiceIn *hw; + char *name; + struct mixeng_volume vol; + struct audio_callback callback; + QLIST_ENTRY (SWVoiceIn) entries; +}; + +struct audio_driver { + const char *name; + const char *descr; + struct audio_option *options; + void *(*init) (void); + void (*fini) (void *); + struct audio_pcm_ops *pcm_ops; + int can_be_default; + int max_voices_out; + int max_voices_in; + int voice_size_out; + int voice_size_in; + int ctl_caps; +}; + +struct audio_pcm_ops { + int (*init_out)(HWVoiceOut *hw, struct audsettings *as); + void (*fini_out)(HWVoiceOut *hw); + int (*run_out) (HWVoiceOut *hw, int live); + int (*write) (SWVoiceOut *sw, void *buf, int size); + int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); + + int (*init_in) (HWVoiceIn *hw, struct audsettings *as); + void (*fini_in) (HWVoiceIn *hw); + int (*run_in) (HWVoiceIn *hw); + int (*read) (SWVoiceIn *sw, void *buf, int size); + int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); +}; + +struct capture_callback { + struct audio_capture_ops ops; + void *opaque; + QLIST_ENTRY (capture_callback) entries; +}; + +struct CaptureVoiceOut { + HWVoiceOut hw; + void *buf; + QLIST_HEAD (cb_listhead, capture_callback) cb_head; + QLIST_ENTRY (CaptureVoiceOut) entries; +}; + +struct SWVoiceCap { + SWVoiceOut sw; + CaptureVoiceOut *cap; + QLIST_ENTRY (SWVoiceCap) entries; +}; + +struct AudioState { + struct audio_driver *drv; + void *drv_opaque; + + QEMUTimer *ts; + QLIST_HEAD (card_listhead, QEMUSoundCard) card_head; + QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; + QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; + QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head; + int nb_hw_voices_out; + int nb_hw_voices_in; + int vm_running; +}; + +extern struct audio_driver no_audio_driver; +extern struct audio_driver oss_audio_driver; +extern struct audio_driver sdl_audio_driver; +extern struct audio_driver wav_audio_driver; +extern struct audio_driver fmod_audio_driver; +extern struct audio_driver alsa_audio_driver; +extern struct audio_driver coreaudio_audio_driver; +extern struct audio_driver dsound_audio_driver; +extern struct audio_driver esd_audio_driver; +extern struct audio_driver pa_audio_driver; +extern struct audio_driver spice_audio_driver; +extern struct audio_driver winwave_audio_driver; +extern const struct mixeng_volume nominal_volume; + +void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); +void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); + +int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len); +int audio_pcm_hw_get_live_in (HWVoiceIn *hw); + +int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); + +int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, + int live, int pending); + +int audio_bug (const char *funcname, int cond); +void *audio_calloc (const char *funcname, int nmemb, size_t size); + +void audio_run (const char *msg); + +#define VOICE_ENABLE 1 +#define VOICE_DISABLE 2 +#define VOICE_VOLUME 3 + +#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME) + +static inline int audio_ring_dist (int dst, int src, int len) +{ + return (dst >= src) ? (dst - src) : (len - src + dst); +} + +#define dolog(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__) + +#ifdef DEBUG +#define ldebug(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__) +#else +#define ldebug(fmt, ...) (void)0 +#endif + +#define AUDIO_STRINGIFY_(n) #n +#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) + +#if defined _MSC_VER || defined __GNUC__ +#define AUDIO_FUNC __FUNCTION__ +#else +#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__) +#endif + +#endif /* audio_int.h */ diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c new file mode 100644 index 0000000..9a9c306 --- /dev/null +++ b/audio/audio_pt_int.c @@ -0,0 +1,173 @@ +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "audio-pt" + +#include "audio_int.h" +#include "audio_pt_int.h" + +static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err, + const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (pt->drv, fmt, ap); + va_end (ap); + + AUD_log (NULL, "\n"); + AUD_log (pt->drv, "Reason: %s\n", strerror (err)); +} + +int audio_pt_init (struct audio_pt *p, void *(*func) (void *), + void *opaque, const char *drv, const char *cap) +{ + int err, err2; + const char *efunc; + sigset_t set, old_set; + + p->drv = drv; + + err = sigfillset (&set); + if (err) { + logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC); + return -1; + } + + err = pthread_mutex_init (&p->mutex, NULL); + if (err) { + efunc = "pthread_mutex_init"; + goto err0; + } + + err = pthread_cond_init (&p->cond, NULL); + if (err) { + efunc = "pthread_cond_init"; + goto err1; + } + + err = pthread_sigmask (SIG_BLOCK, &set, &old_set); + if (err) { + efunc = "pthread_sigmask"; + goto err2; + } + + err = pthread_create (&p->thread, NULL, func, opaque); + + err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL); + if (err2) { + logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed", + cap, AUDIO_FUNC); + /* We have failed to restore original signal mask, all bets are off, + so terminate the process */ + exit (EXIT_FAILURE); + } + + if (err) { + efunc = "pthread_create"; + goto err2; + } + + return 0; + + err2: + err2 = pthread_cond_destroy (&p->cond); + if (err2) { + logerr (p, err2, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC); + } + + err1: + err2 = pthread_mutex_destroy (&p->mutex); + if (err2) { + logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC); + } + + err0: + logerr (p, err, "%s(%s): %s failed", cap, AUDIO_FUNC, efunc); + return -1; +} + +int audio_pt_fini (struct audio_pt *p, const char *cap) +{ + int err, ret = 0; + + err = pthread_cond_destroy (&p->cond); + if (err) { + logerr (p, err, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC); + ret = -1; + } + + err = pthread_mutex_destroy (&p->mutex); + if (err) { + logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC); + ret = -1; + } + return ret; +} + +int audio_pt_lock (struct audio_pt *p, const char *cap) +{ + int err; + + err = pthread_mutex_lock (&p->mutex); + if (err) { + logerr (p, err, "%s(%s): pthread_mutex_lock failed", cap, AUDIO_FUNC); + return -1; + } + return 0; +} + +int audio_pt_unlock (struct audio_pt *p, const char *cap) +{ + int err; + + err = pthread_mutex_unlock (&p->mutex); + if (err) { + logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC); + return -1; + } + return 0; +} + +int audio_pt_wait (struct audio_pt *p, const char *cap) +{ + int err; + + err = pthread_cond_wait (&p->cond, &p->mutex); + if (err) { + logerr (p, err, "%s(%s): pthread_cond_wait failed", cap, AUDIO_FUNC); + return -1; + } + return 0; +} + +int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap) +{ + int err; + + err = pthread_mutex_unlock (&p->mutex); + if (err) { + logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC); + return -1; + } + err = pthread_cond_signal (&p->cond); + if (err) { + logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_FUNC); + return -1; + } + return 0; +} + +int audio_pt_join (struct audio_pt *p, void **arg, const char *cap) +{ + int err; + void *ret; + + err = pthread_join (p->thread, &ret); + if (err) { + logerr (p, err, "%s(%s): pthread_join failed", cap, AUDIO_FUNC); + return -1; + } + *arg = ret; + return 0; +} diff --git a/audio/audio_pt_int.h b/audio/audio_pt_int.h new file mode 100644 index 0000000..0dfff76 --- /dev/null +++ b/audio/audio_pt_int.h @@ -0,0 +1,22 @@ +#ifndef QEMU_AUDIO_PT_INT_H +#define QEMU_AUDIO_PT_INT_H + +#include + +struct audio_pt { + const char *drv; + pthread_t thread; + pthread_cond_t cond; + pthread_mutex_t mutex; +}; + +int audio_pt_init (struct audio_pt *, void *(*) (void *), void *, + const char *, const char *); +int audio_pt_fini (struct audio_pt *, const char *); +int audio_pt_lock (struct audio_pt *, const char *); +int audio_pt_unlock (struct audio_pt *, const char *); +int audio_pt_wait (struct audio_pt *, const char *); +int audio_pt_unlock_and_signal (struct audio_pt *, const char *); +int audio_pt_join (struct audio_pt *, void **, const char *); + +#endif /* audio_pt_int.h */ diff --git a/audio/audio_template.h b/audio/audio_template.h new file mode 100644 index 0000000..584e536 --- /dev/null +++ b/audio/audio_template.h @@ -0,0 +1,555 @@ +/* + * QEMU Audio subsystem header + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DAC +#define NAME "playback" +#define HWBUF hw->mix_buf +#define TYPE out +#define HW HWVoiceOut +#define SW SWVoiceOut +#else +#define NAME "capture" +#define TYPE in +#define HW HWVoiceIn +#define SW SWVoiceIn +#define HWBUF hw->conv_buf +#endif + +static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv) +{ + AudioState *s = &glob_audio_state; + int max_voices = glue (drv->max_voices_, TYPE); + int voice_size = glue (drv->voice_size_, TYPE); + + if (glue (s->nb_hw_voices_, TYPE) > max_voices) { + if (!max_voices) { +#ifdef DAC + dolog ("Driver `%s' does not support " NAME "\n", drv->name); +#endif + } + else { + dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n", + drv->name, + glue (s->nb_hw_voices_, TYPE), + max_voices); + } + glue (s->nb_hw_voices_, TYPE) = max_voices; + } + + if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) { + dolog ("drv=`%s' voice_size=0 max_voices=%d\n", + drv->name, max_voices); + glue (s->nb_hw_voices_, TYPE) = 0; + } + + if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) { + dolog ("drv=`%s' voice_size=%d max_voices=0\n", + drv->name, voice_size); + } +} + +static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) +{ + g_free (HWBUF); + HWBUF = NULL; +} + +static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) +{ + HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (struct st_sample)); + if (!HWBUF) { + dolog ("Could not allocate " NAME " buffer (%d samples)\n", + hw->samples); + return -1; + } + + return 0; +} + +static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) +{ + g_free (sw->buf); + + if (sw->rate) { + st_rate_stop (sw->rate); + } + + sw->buf = NULL; + sw->rate = NULL; +} + +static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) +{ + int samples; + + samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; + + sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample)); + if (!sw->buf) { + dolog ("Could not allocate buffer for `%s' (%d samples)\n", + SW_NAME (sw), samples); + return -1; + } + +#ifdef DAC + sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq); +#else + sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); +#endif + if (!sw->rate) { + g_free (sw->buf); + sw->buf = NULL; + return -1; + } + return 0; +} + +static int glue (audio_pcm_sw_init_, TYPE) ( + SW *sw, + HW *hw, + const char *name, + struct audsettings *as + ) +{ + int err; + + audio_pcm_init_info (&sw->info, as); + sw->hw = hw; + sw->active = 0; +#ifdef DAC + sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq; + sw->total_hw_samples_mixed = 0; + sw->empty = 1; +#else + sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; +#endif + +#ifdef DAC + sw->conv = mixeng_conv +#else + sw->clip = mixeng_clip +#endif + [sw->info.nchannels == 2] + [sw->info.sign] + [sw->info.swap_endianness] + [audio_bits_to_index (sw->info.bits)]; + + sw->name = g_strdup (name); + err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); + if (err) { + g_free (sw->name); + sw->name = NULL; + } + return err; +} + +static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) +{ + glue (audio_pcm_sw_free_resources_, TYPE) (sw); + g_free (sw->name); + sw->name = NULL; +} + +static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw) +{ + QLIST_INSERT_HEAD (&hw->sw_head, sw, entries); +} + +static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw) +{ + QLIST_REMOVE (sw, entries); +} + +static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) +{ + AudioState *s = &glob_audio_state; + HW *hw = *hwp; + + if (!hw->sw_head.lh_first) { +#ifdef DAC + audio_detach_capture (hw); +#endif + QLIST_REMOVE (hw, entries); + glue (hw->pcm_ops->fini_, TYPE) (hw); + glue (s->nb_hw_voices_, TYPE) += 1; + glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); + g_free (hw); + *hwp = NULL; + } +} + +static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw) +{ + AudioState *s = &glob_audio_state; + return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first; +} + +static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw) +{ + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { + if (hw->enabled) { + return hw; + } + } + return NULL; +} + +static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( + HW *hw, + struct audsettings *as + ) +{ + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { + if (audio_pcm_info_eq (&hw->info, as)) { + return hw; + } + } + return NULL; +} + +static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) +{ + HW *hw; + AudioState *s = &glob_audio_state; + struct audio_driver *drv = s->drv; + + if (!glue (s->nb_hw_voices_, TYPE)) { + return NULL; + } + + if (audio_bug (AUDIO_FUNC, !drv)) { + dolog ("No host audio driver\n"); + return NULL; + } + + if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) { + dolog ("Host audio driver without pcm_ops\n"); + return NULL; + } + + hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE)); + if (!hw) { + dolog ("Can not allocate voice `%s' size %d\n", + drv->name, glue (drv->voice_size_, TYPE)); + return NULL; + } + + hw->pcm_ops = drv->pcm_ops; + hw->ctl_caps = drv->ctl_caps; + + QLIST_INIT (&hw->sw_head); +#ifdef DAC + QLIST_INIT (&hw->cap_head); +#endif + if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { + goto err0; + } + + if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) { + dolog ("hw->samples=%d\n", hw->samples); + goto err1; + } + +#ifdef DAC + hw->clip = mixeng_clip +#else + hw->conv = mixeng_conv +#endif + [hw->info.nchannels == 2] + [hw->info.sign] + [hw->info.swap_endianness] + [audio_bits_to_index (hw->info.bits)]; + + if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) { + goto err1; + } + + QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); + glue (s->nb_hw_voices_, TYPE) -= 1; +#ifdef DAC + audio_attach_capture (hw); +#endif + return hw; + + err1: + glue (hw->pcm_ops->fini_, TYPE) (hw); + err0: + g_free (hw); + return NULL; +} + +static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) +{ + HW *hw; + + if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) { + hw = glue (audio_pcm_hw_add_new_, TYPE) (as); + if (hw) { + return hw; + } + } + + hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as); + if (hw) { + return hw; + } + + hw = glue (audio_pcm_hw_add_new_, TYPE) (as); + if (hw) { + return hw; + } + + return glue (audio_pcm_hw_find_any_, TYPE) (NULL); +} + +static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( + const char *sw_name, + struct audsettings *as + ) +{ + SW *sw; + HW *hw; + struct audsettings hw_as; + + if (glue (conf.fixed_, TYPE).enabled) { + hw_as = glue (conf.fixed_, TYPE).settings; + } + else { + hw_as = *as; + } + + sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw)); + if (!sw) { + dolog ("Could not allocate soft voice `%s' (%zu bytes)\n", + sw_name ? sw_name : "unknown", sizeof (*sw)); + goto err1; + } + + hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as); + if (!hw) { + goto err2; + } + + glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw); + + if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) { + goto err3; + } + + return sw; + +err3: + glue (audio_pcm_hw_del_sw_, TYPE) (sw); + glue (audio_pcm_hw_gc_, TYPE) (&hw); +err2: + g_free (sw); +err1: + return NULL; +} + +static void glue (audio_close_, TYPE) (SW *sw) +{ + glue (audio_pcm_sw_fini_, TYPE) (sw); + glue (audio_pcm_hw_del_sw_, TYPE) (sw); + glue (audio_pcm_hw_gc_, TYPE) (&sw->hw); + g_free (sw); +} + +void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) +{ + if (sw) { + if (audio_bug (AUDIO_FUNC, !card)) { + dolog ("card=%p\n", card); + return; + } + + glue (audio_close_, TYPE) (sw); + } +} + +SW *glue (AUD_open_, TYPE) ( + QEMUSoundCard *card, + SW *sw, + const char *name, + void *callback_opaque , + audio_callback_fn callback_fn, + struct audsettings *as + ) +{ + AudioState *s = &glob_audio_state; +#ifdef DAC + int live = 0; + SW *old_sw = NULL; +#endif + + if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) { + dolog ("card=%p name=%p callback_fn=%p as=%p\n", + card, name, callback_fn, as); + goto fail; + } + + ldebug ("open %s, freq %d, nchannels %d, fmt %d\n", + name, as->freq, as->nchannels, as->fmt); + + if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) { + audio_print_settings (as); + goto fail; + } + + if (audio_bug (AUDIO_FUNC, !s->drv)) { + dolog ("Can not open `%s' (no host audio driver)\n", name); + goto fail; + } + + if (sw && audio_pcm_info_eq (&sw->info, as)) { + return sw; + } + +#ifdef DAC + if (conf.plive && sw && (!sw->active && !sw->empty)) { + live = sw->total_hw_samples_mixed; + +#ifdef DEBUG_PLIVE + dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live); + dolog ("Old %s freq %d, bits %d, channels %d\n", + SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels); + dolog ("New %s freq %d, bits %d, channels %d\n", + name, + as->freq, + (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8, + as->nchannels); +#endif + + if (live) { + old_sw = sw; + old_sw->callback.fn = NULL; + sw = NULL; + } + } +#endif + + if (!glue (conf.fixed_, TYPE).enabled && sw) { + glue (AUD_close_, TYPE) (card, sw); + sw = NULL; + } + + if (sw) { + HW *hw = sw->hw; + + if (!hw) { + dolog ("Internal logic error voice `%s' has no hardware store\n", + SW_NAME (sw)); + goto fail; + } + + glue (audio_pcm_sw_fini_, TYPE) (sw); + if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) { + goto fail; + } + } + else { + sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as); + if (!sw) { + dolog ("Failed to create voice `%s'\n", name); + return NULL; + } + } + + sw->card = card; + sw->vol = nominal_volume; + sw->callback.fn = callback_fn; + sw->callback.opaque = callback_opaque; + +#ifdef DAC + if (live) { + int mixed = + (live << old_sw->info.shift) + * old_sw->info.bytes_per_second + / sw->info.bytes_per_second; + +#ifdef DEBUG_PLIVE + dolog ("Silence will be mixed %d\n", mixed); +#endif + sw->total_hw_samples_mixed += mixed; + } +#endif + +#ifdef DEBUG_AUDIO + dolog ("%s\n", name); + audio_pcm_print_info ("hw", &sw->hw->info); + audio_pcm_print_info ("sw", &sw->info); +#endif + + return sw; + + fail: + glue (AUD_close_, TYPE) (card, sw); + return NULL; +} + +int glue (AUD_is_active_, TYPE) (SW *sw) +{ + return sw ? sw->active : 0; +} + +void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) +{ + if (!sw) { + return; + } + + ts->old_ts = sw->hw->ts_helper; +} + +uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) +{ + uint64_t delta, cur_ts, old_ts; + + if (!sw) { + return 0; + } + + cur_ts = sw->hw->ts_helper; + old_ts = ts->old_ts; + /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */ + + if (cur_ts >= old_ts) { + delta = cur_ts - old_ts; + } + else { + delta = UINT64_MAX - old_ts + cur_ts; + } + + if (!delta) { + return 0; + } + + return muldiv64 (delta, sw->hw->info.freq, 1000000); +} + +#undef TYPE +#undef HW +#undef SW +#undef HWBUF +#undef NAME diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c new file mode 100644 index 0000000..e132405 --- /dev/null +++ b/audio/audio_win_int.c @@ -0,0 +1,107 @@ +/* public domain */ + +#include "qemu-common.h" + +#define AUDIO_CAP "win-int" +#include +#include + +#include "audio.h" +#include "audio_int.h" +#include "audio_win_int.h" + +int waveformat_from_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as) +{ + memset (wfx, 0, sizeof (*wfx)); + + wfx->wFormatTag = WAVE_FORMAT_PCM; + wfx->nChannels = as->nchannels; + wfx->nSamplesPerSec = as->freq; + wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); + wfx->nBlockAlign = 1 << (as->nchannels == 2); + wfx->cbSize = 0; + + switch (as->fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + wfx->wBitsPerSample = 8; + break; + + case AUD_FMT_S16: + case AUD_FMT_U16: + wfx->wBitsPerSample = 16; + wfx->nAvgBytesPerSec <<= 1; + wfx->nBlockAlign <<= 1; + break; + + case AUD_FMT_S32: + case AUD_FMT_U32: + wfx->wBitsPerSample = 32; + wfx->nAvgBytesPerSec <<= 2; + wfx->nBlockAlign <<= 2; + break; + + default: + dolog ("Internal logic error: Bad audio format %d\n", as->freq); + return -1; + } + + return 0; +} + +int waveformat_to_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as) +{ + if (wfx->wFormatTag != WAVE_FORMAT_PCM) { + dolog ("Invalid wave format, tag is not PCM, but %d\n", + wfx->wFormatTag); + return -1; + } + + if (!wfx->nSamplesPerSec) { + dolog ("Invalid wave format, frequency is zero\n"); + return -1; + } + as->freq = wfx->nSamplesPerSec; + + switch (wfx->nChannels) { + case 1: + as->nchannels = 1; + break; + + case 2: + as->nchannels = 2; + break; + + default: + dolog ( + "Invalid wave format, number of channels is not 1 or 2, but %d\n", + wfx->nChannels + ); + return -1; + } + + switch (wfx->wBitsPerSample) { + case 8: + as->fmt = AUD_FMT_U8; + break; + + case 16: + as->fmt = AUD_FMT_S16; + break; + + case 32: + as->fmt = AUD_FMT_S32; + break; + + default: + dolog ("Invalid wave format, bits per sample is not " + "8, 16 or 32, but %d\n", + wfx->wBitsPerSample); + return -1; + } + + return 0; +} + diff --git a/audio/audio_win_int.h b/audio/audio_win_int.h new file mode 100644 index 0000000..fa5b3cb --- /dev/null +++ b/audio/audio_win_int.h @@ -0,0 +1,10 @@ +#ifndef AUDIO_WIN_INT_H +#define AUDIO_WIN_INT_H + +int waveformat_from_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as); + +int waveformat_to_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as); + +#endif /* AUDIO_WIN_INT_H */ diff --git a/audio/coreaudio.c b/audio/coreaudio.c new file mode 100644 index 0000000..5964c62 --- /dev/null +++ b/audio/coreaudio.c @@ -0,0 +1,549 @@ +/* + * QEMU OS X CoreAudio audio driver + * + * Copyright (c) 2005 Mike Kronenberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include /* strerror */ +#include /* pthread_X */ + +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "coreaudio" +#include "audio_int.h" + +struct { + int buffer_frames; + int nbuffers; + int isAtexit; +} conf = { + .buffer_frames = 512, + .nbuffers = 4, + .isAtexit = 0 +}; + +typedef struct coreaudioVoiceOut { + HWVoiceOut hw; + pthread_mutex_t mutex; + int isAtexit; + AudioDeviceID outputDeviceID; + UInt32 audioDevicePropertyBufferFrameSize; + AudioStreamBasicDescription outputStreamBasicDescription; + int live; + int decr; + int rpos; +} coreaudioVoiceOut; + +static void coreaudio_logstatus (OSStatus status) +{ + const char *str = "BUG"; + + switch(status) { + case kAudioHardwareNoError: + str = "kAudioHardwareNoError"; + break; + + case kAudioHardwareNotRunningError: + str = "kAudioHardwareNotRunningError"; + break; + + case kAudioHardwareUnspecifiedError: + str = "kAudioHardwareUnspecifiedError"; + break; + + case kAudioHardwareUnknownPropertyError: + str = "kAudioHardwareUnknownPropertyError"; + break; + + case kAudioHardwareBadPropertySizeError: + str = "kAudioHardwareBadPropertySizeError"; + break; + + case kAudioHardwareIllegalOperationError: + str = "kAudioHardwareIllegalOperationError"; + break; + + case kAudioHardwareBadDeviceError: + str = "kAudioHardwareBadDeviceError"; + break; + + case kAudioHardwareBadStreamError: + str = "kAudioHardwareBadStreamError"; + break; + + case kAudioHardwareUnsupportedOperationError: + str = "kAudioHardwareUnsupportedOperationError"; + break; + + case kAudioDeviceUnsupportedFormatError: + str = "kAudioDeviceUnsupportedFormatError"; + break; + + case kAudioDevicePermissionsError: + str = "kAudioDevicePermissionsError"; + break; + + default: + AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status); + return; + } + + AUD_log (AUDIO_CAP, "Reason: %s\n", str); +} + +static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( + OSStatus status, + const char *fmt, + ... + ) +{ + va_list ap; + + va_start (ap, fmt); + AUD_log (AUDIO_CAP, fmt, ap); + va_end (ap); + + coreaudio_logstatus (status); +} + +static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( + OSStatus status, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + coreaudio_logstatus (status); +} + +static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) +{ + OSStatus status; + UInt32 result = 0; + UInt32 propertySize = sizeof(outputDeviceID); + status = AudioDeviceGetProperty( + outputDeviceID, 0, 0, + kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); + if (status != kAudioHardwareNoError) { + coreaudio_logerr(status, + "Could not determine whether Device is playing\n"); + } + return result; +} + +static void coreaudio_atexit (void) +{ + conf.isAtexit = 1; +} + +static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) +{ + int err; + + err = pthread_mutex_lock (&core->mutex); + if (err) { + dolog ("Could not lock voice for %s\nReason: %s\n", + fn_name, strerror (err)); + return -1; + } + return 0; +} + +static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) +{ + int err; + + err = pthread_mutex_unlock (&core->mutex); + if (err) { + dolog ("Could not unlock voice for %s\nReason: %s\n", + fn_name, strerror (err)); + return -1; + } + return 0; +} + +static int coreaudio_run_out (HWVoiceOut *hw, int live) +{ + int decr; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + if (coreaudio_lock (core, "coreaudio_run_out")) { + return 0; + } + + if (core->decr > live) { + ldebug ("core->decr %d live %d core->live %d\n", + core->decr, + live, + core->live); + } + + decr = audio_MIN (core->decr, live); + core->decr -= decr; + + core->live = live - decr; + hw->rpos = core->rpos; + + coreaudio_unlock (core, "coreaudio_run_out"); + return decr; +} + +/* callback to feed audiooutput buffer */ +static OSStatus audioDeviceIOProc( + AudioDeviceID inDevice, + const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, + const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, + const AudioTimeStamp* inOutputTime, + void* hwptr) +{ + UInt32 frame, frameCount; + float *out = outOutputData->mBuffers[0].mData; + HWVoiceOut *hw = hwptr; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; + int rpos, live; + struct st_sample *src; +#ifndef FLOAT_MIXENG +#ifdef RECIPROCAL + const float scale = 1.f / UINT_MAX; +#else + const float scale = UINT_MAX; +#endif +#endif + + if (coreaudio_lock (core, "audioDeviceIOProc")) { + inInputTime = 0; + return 0; + } + + frameCount = core->audioDevicePropertyBufferFrameSize; + live = core->live; + + /* if there are not enough samples, set signal and return */ + if (live < frameCount) { + inInputTime = 0; + coreaudio_unlock (core, "audioDeviceIOProc(empty)"); + return 0; + } + + rpos = core->rpos; + src = hw->mix_buf + rpos; + + /* fill buffer */ + for (frame = 0; frame < frameCount; frame++) { +#ifdef FLOAT_MIXENG + *out++ = src[frame].l; /* left channel */ + *out++ = src[frame].r; /* right channel */ +#else +#ifdef RECIPROCAL + *out++ = src[frame].l * scale; /* left channel */ + *out++ = src[frame].r * scale; /* right channel */ +#else + *out++ = src[frame].l / scale; /* left channel */ + *out++ = src[frame].r / scale; /* right channel */ +#endif +#endif + } + + rpos = (rpos + frameCount) % hw->samples; + core->decr += frameCount; + core->rpos = rpos; + + coreaudio_unlock (core, "audioDeviceIOProc"); + return 0; +} + +static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + OSStatus status; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + UInt32 propertySize; + int err; + const char *typ = "playback"; + AudioValueRange frameRange; + + /* create mutex */ + err = pthread_mutex_init(&core->mutex, NULL); + if (err) { + dolog("Could not create mutex\nReason: %s\n", strerror (err)); + return -1; + } + + audio_pcm_init_info (&hw->info, as); + + /* open default output device */ + propertySize = sizeof(core->outputDeviceID); + status = AudioHardwareGetProperty( + kAudioHardwarePropertyDefaultOutputDevice, + &propertySize, + &core->outputDeviceID); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not get default output Device\n"); + return -1; + } + if (core->outputDeviceID == kAudioDeviceUnknown) { + dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); + return -1; + } + + /* get minimum and maximum buffer frame sizes */ + propertySize = sizeof(frameRange); + status = AudioDeviceGetProperty( + core->outputDeviceID, + 0, + 0, + kAudioDevicePropertyBufferFrameSizeRange, + &propertySize, + &frameRange); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not get device buffer frame range\n"); + return -1; + } + + if (frameRange.mMinimum > conf.buffer_frames) { + core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; + dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); + } + else if (frameRange.mMaximum < conf.buffer_frames) { + core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; + dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); + } + else { + core->audioDevicePropertyBufferFrameSize = conf.buffer_frames; + } + + /* set Buffer Frame Size */ + propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); + status = AudioDeviceSetProperty( + core->outputDeviceID, + NULL, + 0, + false, + kAudioDevicePropertyBufferFrameSize, + propertySize, + &core->audioDevicePropertyBufferFrameSize); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not set device buffer frame size %" PRIu32 "\n", + (uint32_t)core->audioDevicePropertyBufferFrameSize); + return -1; + } + + /* get Buffer Frame Size */ + propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); + status = AudioDeviceGetProperty( + core->outputDeviceID, + 0, + false, + kAudioDevicePropertyBufferFrameSize, + &propertySize, + &core->audioDevicePropertyBufferFrameSize); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not get device buffer frame size\n"); + return -1; + } + hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize; + + /* get StreamFormat */ + propertySize = sizeof(core->outputStreamBasicDescription); + status = AudioDeviceGetProperty( + core->outputDeviceID, + 0, + false, + kAudioDevicePropertyStreamFormat, + &propertySize, + &core->outputStreamBasicDescription); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not get Device Stream properties\n"); + core->outputDeviceID = kAudioDeviceUnknown; + return -1; + } + + /* set Samplerate */ + core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; + propertySize = sizeof(core->outputStreamBasicDescription); + status = AudioDeviceSetProperty( + core->outputDeviceID, + 0, + 0, + 0, + kAudioDevicePropertyStreamFormat, + propertySize, + &core->outputStreamBasicDescription); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", + as->freq); + core->outputDeviceID = kAudioDeviceUnknown; + return -1; + } + + /* set Callback */ + status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); + core->outputDeviceID = kAudioDeviceUnknown; + return -1; + } + + /* start Playback */ + if (!isPlaying(core->outputDeviceID)) { + status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, "Could not start playback\n"); + AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); + core->outputDeviceID = kAudioDeviceUnknown; + return -1; + } + } + + return 0; +} + +static void coreaudio_fini_out (HWVoiceOut *hw) +{ + OSStatus status; + int err; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + if (!conf.isAtexit) { + /* stop playback */ + if (isPlaying(core->outputDeviceID)) { + status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not stop playback\n"); + } + } + + /* remove callback */ + status = AudioDeviceRemoveIOProc(core->outputDeviceID, + audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not remove IOProc\n"); + } + } + core->outputDeviceID = kAudioDeviceUnknown; + + /* destroy mutex */ + err = pthread_mutex_destroy(&core->mutex); + if (err) { + dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); + } +} + +static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + OSStatus status; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + switch (cmd) { + case VOICE_ENABLE: + /* start playback */ + if (!isPlaying(core->outputDeviceID)) { + status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not resume playback\n"); + } + } + break; + + case VOICE_DISABLE: + /* stop playback */ + if (!conf.isAtexit) { + if (isPlaying(core->outputDeviceID)) { + status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not pause playback\n"); + } + } + } + break; + } + return 0; +} + +static void *coreaudio_audio_init (void) +{ + atexit(coreaudio_atexit); + return &coreaudio_audio_init; +} + +static void coreaudio_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_option coreaudio_options[] = { + { + .name = "BUFFER_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.buffer_frames, + .descr = "Size of the buffer in frames" + }, + { + .name = "BUFFER_COUNT", + .tag = AUD_OPT_INT, + .valp = &conf.nbuffers, + .descr = "Number of buffers" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops coreaudio_pcm_ops = { + .init_out = coreaudio_init_out, + .fini_out = coreaudio_fini_out, + .run_out = coreaudio_run_out, + .write = coreaudio_write, + .ctl_out = coreaudio_ctl_out +}; + +struct audio_driver coreaudio_audio_driver = { + .name = "coreaudio", + .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", + .options = coreaudio_options, + .init = coreaudio_audio_init, + .fini = coreaudio_audio_fini, + .pcm_ops = &coreaudio_pcm_ops, + .can_be_default = 1, + .max_voices_out = 1, + .max_voices_in = 0, + .voice_size_out = sizeof (coreaudioVoiceOut), + .voice_size_in = 0 +}; diff --git a/audio/dsound_template.h b/audio/dsound_template.h new file mode 100644 index 0000000..8b37d16 --- /dev/null +++ b/audio/dsound_template.h @@ -0,0 +1,293 @@ +/* + * QEMU DirectSound audio driver header + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifdef DSBTYPE_IN +#define NAME "capture buffer" +#define NAME2 "DirectSoundCapture" +#define TYPE in +#define IFACE IDirectSoundCaptureBuffer +#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER +#define FIELD dsound_capture_buffer +#define FIELD2 dsound_capture +#else +#define NAME "playback buffer" +#define NAME2 "DirectSound" +#define TYPE out +#define IFACE IDirectSoundBuffer +#define BUFPTR LPDIRECTSOUNDBUFFER +#define FIELD dsound_buffer +#define FIELD2 dsound +#endif + +static int glue (dsound_unlock_, TYPE) ( + BUFPTR buf, + LPVOID p1, + LPVOID p2, + DWORD blen1, + DWORD blen2 + ) +{ + HRESULT hr; + + hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not unlock " NAME "\n"); + return -1; + } + + return 0; +} + +static int glue (dsound_lock_, TYPE) ( + BUFPTR buf, + struct audio_pcm_info *info, + DWORD pos, + DWORD len, + LPVOID *p1p, + LPVOID *p2p, + DWORD *blen1p, + DWORD *blen2p, + int entire + ) +{ + HRESULT hr; + int i; + LPVOID p1 = NULL, p2 = NULL; + DWORD blen1 = 0, blen2 = 0; + DWORD flag; + +#ifdef DSBTYPE_IN + flag = entire ? DSCBLOCK_ENTIREBUFFER : 0; +#else + flag = entire ? DSBLOCK_ENTIREBUFFER : 0; +#endif + for (i = 0; i < conf.lock_retries; ++i) { + hr = glue (IFACE, _Lock) ( + buf, + pos, + len, + &p1, + &blen1, + &p2, + &blen2, + flag + ); + + if (FAILED (hr)) { +#ifndef DSBTYPE_IN + if (hr == DSERR_BUFFERLOST) { + if (glue (dsound_restore_, TYPE) (buf)) { + dsound_logerr (hr, "Could not lock " NAME "\n"); + goto fail; + } + continue; + } +#endif + dsound_logerr (hr, "Could not lock " NAME "\n"); + goto fail; + } + + break; + } + + if (i == conf.lock_retries) { + dolog ("%d attempts to lock " NAME " failed\n", i); + goto fail; + } + + if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { + dolog ("DirectSound returned misaligned buffer %ld %ld\n", + blen1, blen2); + glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2); + goto fail; + } + + if (!p1 && blen1) { + dolog ("warning: !p1 && blen1=%ld\n", blen1); + blen1 = 0; + } + + if (!p2 && blen2) { + dolog ("warning: !p2 && blen2=%ld\n", blen2); + blen2 = 0; + } + + *p1p = p1; + *p2p = p2; + *blen1p = blen1; + *blen2p = blen2; + return 0; + + fail: + *p1p = NULL - 1; + *p2p = NULL - 1; + *blen1p = -1; + *blen2p = -1; + return -1; +} + +#ifdef DSBTYPE_IN +static void dsound_fini_in (HWVoiceIn *hw) +#else +static void dsound_fini_out (HWVoiceOut *hw) +#endif +{ + HRESULT hr; +#ifdef DSBTYPE_IN + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; +#else + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; +#endif + + if (ds->FIELD) { + hr = glue (IFACE, _Stop) (ds->FIELD); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not stop " NAME "\n"); + } + + hr = glue (IFACE, _Release) (ds->FIELD); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release " NAME "\n"); + } + ds->FIELD = NULL; + } +} + +#ifdef DSBTYPE_IN +static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as) +#else +static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as) +#endif +{ + int err; + HRESULT hr; + dsound *s = &glob_dsound; + WAVEFORMATEX wfx; + struct audsettings obt_as; +#ifdef DSBTYPE_IN + const char *typ = "ADC"; + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; + DSCBUFFERDESC bd; + DSCBCAPS bc; +#else + const char *typ = "DAC"; + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; + DSBUFFERDESC bd; + DSBCAPS bc; +#endif + + if (!s->FIELD2) { + dolog ("Attempt to initialize voice without " NAME2 " object\n"); + return -1; + } + + err = waveformat_from_audio_settings (&wfx, as); + if (err) { + return -1; + } + + memset (&bd, 0, sizeof (bd)); + bd.dwSize = sizeof (bd); + bd.lpwfxFormat = &wfx; +#ifdef DSBTYPE_IN + bd.dwBufferBytes = conf.bufsize_in; + hr = IDirectSoundCapture_CreateCaptureBuffer ( + s->dsound_capture, + &bd, + &ds->dsound_capture_buffer, + NULL + ); +#else + bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; + bd.dwBufferBytes = conf.bufsize_out; + hr = IDirectSound_CreateSoundBuffer ( + s->dsound, + &bd, + &ds->dsound_buffer, + NULL + ); +#endif + + if (FAILED (hr)) { + dsound_logerr2 (hr, typ, "Could not create " NAME "\n"); + return -1; + } + + hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL); + if (FAILED (hr)) { + dsound_logerr2 (hr, typ, "Could not get " NAME " format\n"); + goto fail0; + } + +#ifdef DEBUG_DSOUND + dolog (NAME "\n"); + print_wave_format (&wfx); +#endif + + memset (&bc, 0, sizeof (bc)); + bc.dwSize = sizeof (bc); + + hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc); + if (FAILED (hr)) { + dsound_logerr2 (hr, typ, "Could not get " NAME " format\n"); + goto fail0; + } + + err = waveformat_to_audio_settings (&wfx, &obt_as); + if (err) { + goto fail0; + } + + ds->first_time = 1; + obt_as.endianness = 0; + audio_pcm_init_info (&hw->info, &obt_as); + + if (bc.dwBufferBytes & hw->info.align) { + dolog ( + "GetCaps returned misaligned buffer size %ld, alignment %d\n", + bc.dwBufferBytes, hw->info.align + 1 + ); + } + hw->samples = bc.dwBufferBytes >> hw->info.shift; + +#ifdef DEBUG_DSOUND + dolog ("caps %ld, desc %ld\n", + bc.dwBufferBytes, bd.dwBufferBytes); + + dolog ("bufsize %d, freq %d, chan %d, fmt %d\n", + hw->bufsize, settings.freq, settings.nchannels, settings.fmt); +#endif + return 0; + + fail0: + glue (dsound_fini_, TYPE) (hw); + return -1; +} + +#undef NAME +#undef NAME2 +#undef TYPE +#undef IFACE +#undef BUFPTR +#undef FIELD +#undef FIELD2 diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c new file mode 100644 index 0000000..e2d89fd --- /dev/null +++ b/audio/dsoundaudio.c @@ -0,0 +1,1030 @@ +/* + * QEMU DirectSound audio driver + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation + */ + +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "dsound" +#include "audio_int.h" + +#include +#include +#include +#include + +#include "audio_win_int.h" + +/* #define DEBUG_DSOUND */ + +static struct { + int lock_retries; + int restore_retries; + int getstatus_retries; + int set_primary; + int bufsize_in; + int bufsize_out; + struct audsettings settings; + int latency_millis; +} conf = { + .lock_retries = 1, + .restore_retries = 1, + .getstatus_retries = 1, + .set_primary = 0, + .bufsize_in = 16384, + .bufsize_out = 16384, + .settings.freq = 44100, + .settings.nchannels = 2, + .settings.fmt = AUD_FMT_S16, + .latency_millis = 10 +}; + +typedef struct { + LPDIRECTSOUND dsound; + LPDIRECTSOUNDCAPTURE dsound_capture; + LPDIRECTSOUNDBUFFER dsound_primary_buffer; + struct audsettings settings; +} dsound; + +static dsound glob_dsound; + +typedef struct { + HWVoiceOut hw; + LPDIRECTSOUNDBUFFER dsound_buffer; + DWORD old_pos; + int first_time; +#ifdef DEBUG_DSOUND + DWORD old_ppos; + DWORD played; + DWORD mixed; +#endif +} DSoundVoiceOut; + +typedef struct { + HWVoiceIn hw; + int first_time; + LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; +} DSoundVoiceIn; + +static void dsound_log_hresult (HRESULT hr) +{ + const char *str = "BUG"; + + switch (hr) { + case DS_OK: + str = "The method succeeded"; + break; +#ifdef DS_NO_VIRTUALIZATION + case DS_NO_VIRTUALIZATION: + str = "The buffer was created, but another 3D algorithm was substituted"; + break; +#endif +#ifdef DS_INCOMPLETE + case DS_INCOMPLETE: + str = "The method succeeded, but not all the optional effects were obtained"; + break; +#endif +#ifdef DSERR_ACCESSDENIED + case DSERR_ACCESSDENIED: + str = "The request failed because access was denied"; + break; +#endif +#ifdef DSERR_ALLOCATED + case DSERR_ALLOCATED: + str = "The request failed because resources, such as a priority level, were already in use by another caller"; + break; +#endif +#ifdef DSERR_ALREADYINITIALIZED + case DSERR_ALREADYINITIALIZED: + str = "The object is already initialized"; + break; +#endif +#ifdef DSERR_BADFORMAT + case DSERR_BADFORMAT: + str = "The specified wave format is not supported"; + break; +#endif +#ifdef DSERR_BADSENDBUFFERGUID + case DSERR_BADSENDBUFFERGUID: + str = "The GUID specified in an audiopath file does not match a valid mix-in buffer"; + break; +#endif +#ifdef DSERR_BUFFERLOST + case DSERR_BUFFERLOST: + str = "The buffer memory has been lost and must be restored"; + break; +#endif +#ifdef DSERR_BUFFERTOOSMALL + case DSERR_BUFFERTOOSMALL: + str = "The buffer size is not great enough to enable effects processing"; + break; +#endif +#ifdef DSERR_CONTROLUNAVAIL + case DSERR_CONTROLUNAVAIL: + str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC"; + break; +#endif +#ifdef DSERR_DS8_REQUIRED + case DSERR_DS8_REQUIRED: + str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface"; + break; +#endif +#ifdef DSERR_FXUNAVAILABLE + case DSERR_FXUNAVAILABLE: + str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software"; + break; +#endif +#ifdef DSERR_GENERIC + case DSERR_GENERIC : + str = "An undetermined error occurred inside the DirectSound subsystem"; + break; +#endif +#ifdef DSERR_INVALIDCALL + case DSERR_INVALIDCALL: + str = "This function is not valid for the current state of this object"; + break; +#endif +#ifdef DSERR_INVALIDPARAM + case DSERR_INVALIDPARAM: + str = "An invalid parameter was passed to the returning function"; + break; +#endif +#ifdef DSERR_NOAGGREGATION + case DSERR_NOAGGREGATION: + str = "The object does not support aggregation"; + break; +#endif +#ifdef DSERR_NODRIVER + case DSERR_NODRIVER: + str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID"; + break; +#endif +#ifdef DSERR_NOINTERFACE + case DSERR_NOINTERFACE: + str = "The requested COM interface is not available"; + break; +#endif +#ifdef DSERR_OBJECTNOTFOUND + case DSERR_OBJECTNOTFOUND: + str = "The requested object was not found"; + break; +#endif +#ifdef DSERR_OTHERAPPHASPRIO + case DSERR_OTHERAPPHASPRIO: + str = "Another application has a higher priority level, preventing this call from succeeding"; + break; +#endif +#ifdef DSERR_OUTOFMEMORY + case DSERR_OUTOFMEMORY: + str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request"; + break; +#endif +#ifdef DSERR_PRIOLEVELNEEDED + case DSERR_PRIOLEVELNEEDED: + str = "A cooperative level of DSSCL_PRIORITY or higher is required"; + break; +#endif +#ifdef DSERR_SENDLOOP + case DSERR_SENDLOOP: + str = "A circular loop of send effects was detected"; + break; +#endif +#ifdef DSERR_UNINITIALIZED + case DSERR_UNINITIALIZED: + str = "The Initialize method has not been called or has not been called successfully before other methods were called"; + break; +#endif +#ifdef DSERR_UNSUPPORTED + case DSERR_UNSUPPORTED: + str = "The function called is not supported at this time"; + break; +#endif + default: + AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr); + return; + } + + AUD_log (AUDIO_CAP, "Reason: %s\n", str); +} + +static void GCC_FMT_ATTR (2, 3) dsound_logerr ( + HRESULT hr, + const char *fmt, + ... + ) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + dsound_log_hresult (hr); +} + +static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( + HRESULT hr, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + dsound_log_hresult (hr); +} + +static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis) +{ + return (millis * info->bytes_per_second) / 1000; +} + +#ifdef DEBUG_DSOUND +static void print_wave_format (WAVEFORMATEX *wfx) +{ + dolog ("tag = %d\n", wfx->wFormatTag); + dolog ("nChannels = %d\n", wfx->nChannels); + dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec); + dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec); + dolog ("nBlockAlign = %d\n", wfx->nBlockAlign); + dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample); + dolog ("cbSize = %d\n", wfx->cbSize); +} +#endif + +static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) +{ + HRESULT hr; + int i; + + for (i = 0; i < conf.restore_retries; ++i) { + hr = IDirectSoundBuffer_Restore (dsb); + + switch (hr) { + case DS_OK: + return 0; + + case DSERR_BUFFERLOST: + continue; + + default: + dsound_logerr (hr, "Could not restore playback buffer\n"); + return -1; + } + } + + dolog ("%d attempts to restore playback buffer failed\n", i); + return -1; +} + +#include "dsound_template.h" +#define DSBTYPE_IN +#include "dsound_template.h" +#undef DSBTYPE_IN + +static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp) +{ + HRESULT hr; + int i; + + for (i = 0; i < conf.getstatus_retries; ++i) { + hr = IDirectSoundBuffer_GetStatus (dsb, statusp); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get playback buffer status\n"); + return -1; + } + + if (*statusp & DSERR_BUFFERLOST) { + if (dsound_restore_out (dsb)) { + return -1; + } + continue; + } + break; + } + + return 0; +} + +static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb, + DWORD *statusp) +{ + HRESULT hr; + + hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get capture buffer status\n"); + return -1; + } + + return 0; +} + +static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) +{ + int src_len1 = dst_len; + int src_len2 = 0; + int pos = hw->rpos + dst_len; + struct st_sample *src1 = hw->mix_buf + hw->rpos; + struct st_sample *src2 = NULL; + + if (pos > hw->samples) { + src_len1 = hw->samples - hw->rpos; + src2 = hw->mix_buf; + src_len2 = dst_len - src_len1; + pos = src_len2; + } + + if (src_len1) { + hw->clip (dst, src1, src_len1); + } + + if (src_len2) { + dst = advance (dst, src_len1 << hw->info.shift); + hw->clip (dst, src2, src_len2); + } + + hw->rpos = pos % hw->samples; +} + +static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb) +{ + int err; + LPVOID p1, p2; + DWORD blen1, blen2, len1, len2; + + err = dsound_lock_out ( + dsb, + &hw->info, + 0, + hw->samples << hw->info.shift, + &p1, &p2, + &blen1, &blen2, + 1 + ); + if (err) { + return; + } + + len1 = blen1 >> hw->info.shift; + len2 = blen2 >> hw->info.shift; + +#ifdef DEBUG_DSOUND + dolog ("clear %p,%ld,%ld %p,%ld,%ld\n", + p1, blen1, len1, + p2, blen2, len2); +#endif + + if (p1 && len1) { + audio_pcm_info_clear_buf (&hw->info, p1, len1); + } + + if (p2 && len2) { + audio_pcm_info_clear_buf (&hw->info, p2, len2); + } + + dsound_unlock_out (dsb, p1, p2, blen1, blen2); +} + +static void dsound_close (dsound *s) +{ + HRESULT hr; + + if (s->dsound_primary_buffer) { + hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release primary buffer\n"); + } + s->dsound_primary_buffer = NULL; + } +} + +static int dsound_open (dsound *s) +{ + int err; + HRESULT hr; + WAVEFORMATEX wfx; + DSBUFFERDESC dsbd; + HWND hwnd; + + hwnd = GetForegroundWindow (); + hr = IDirectSound_SetCooperativeLevel ( + s->dsound, + hwnd, + DSSCL_PRIORITY + ); + + if (FAILED (hr)) { + dsound_logerr (hr, "Could not set cooperative level for window %p\n", + hwnd); + return -1; + } + + if (!conf.set_primary) { + return 0; + } + + err = waveformat_from_audio_settings (&wfx, &conf.settings); + if (err) { + return -1; + } + + memset (&dsbd, 0, sizeof (dsbd)); + dsbd.dwSize = sizeof (dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = NULL; + + hr = IDirectSound_CreateSoundBuffer ( + s->dsound, + &dsbd, + &s->dsound_primary_buffer, + NULL + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not create primary playback buffer\n"); + return -1; + } + + hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not set primary playback buffer format\n"); + } + + hr = IDirectSoundBuffer_GetFormat ( + s->dsound_primary_buffer, + &wfx, + sizeof (wfx), + NULL + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get primary playback buffer format\n"); + goto fail0; + } + +#ifdef DEBUG_DSOUND + dolog ("Primary\n"); + print_wave_format (&wfx); +#endif + + err = waveformat_to_audio_settings (&wfx, &s->settings); + if (err) { + goto fail0; + } + + return 0; + + fail0: + dsound_close (s); + return -1; +} + +static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + HRESULT hr; + DWORD status; + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; + LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; + + if (!dsb) { + dolog ("Attempt to control voice without a buffer\n"); + return 0; + } + + switch (cmd) { + case VOICE_ENABLE: + if (dsound_get_status_out (dsb, &status)) { + return -1; + } + + if (status & DSBSTATUS_PLAYING) { + dolog ("warning: Voice is already playing\n"); + return 0; + } + + dsound_clear_sample (hw, dsb); + + hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not start playing buffer\n"); + return -1; + } + break; + + case VOICE_DISABLE: + if (dsound_get_status_out (dsb, &status)) { + return -1; + } + + if (status & DSBSTATUS_PLAYING) { + hr = IDirectSoundBuffer_Stop (dsb); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not stop playing buffer\n"); + return -1; + } + } + else { + dolog ("warning: Voice is not playing\n"); + } + break; + } + return 0; +} + +static int dsound_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int dsound_run_out (HWVoiceOut *hw, int live) +{ + int err; + HRESULT hr; + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; + LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; + int len, hwshift; + DWORD blen1, blen2; + DWORD len1, len2; + DWORD decr; + DWORD wpos, ppos, old_pos; + LPVOID p1, p2; + int bufsize; + + if (!dsb) { + dolog ("Attempt to run empty with playback buffer\n"); + return 0; + } + + hwshift = hw->info.shift; + bufsize = hw->samples << hwshift; + + hr = IDirectSoundBuffer_GetCurrentPosition ( + dsb, + &ppos, + ds->first_time ? &wpos : NULL + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get playback buffer position\n"); + return 0; + } + + len = live << hwshift; + + if (ds->first_time) { + if (conf.latency_millis) { + DWORD cur_blat; + + cur_blat = audio_ring_dist (wpos, ppos, bufsize); + ds->first_time = 0; + old_pos = wpos; + old_pos += + millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat; + old_pos %= bufsize; + old_pos &= ~hw->info.align; + } + else { + old_pos = wpos; + } +#ifdef DEBUG_DSOUND + ds->played = 0; + ds->mixed = 0; +#endif + } + else { + if (ds->old_pos == ppos) { +#ifdef DEBUG_DSOUND + dolog ("old_pos == ppos\n"); +#endif + return 0; + } + +#ifdef DEBUG_DSOUND + ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize); +#endif + old_pos = ds->old_pos; + } + + if ((old_pos < ppos) && ((old_pos + len) > ppos)) { + len = ppos - old_pos; + } + else { + if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) { + len = bufsize - old_pos + ppos; + } + } + + if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) { + dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n", + len, bufsize, old_pos, ppos); + return 0; + } + + len &= ~hw->info.align; + if (!len) { + return 0; + } + +#ifdef DEBUG_DSOUND + ds->old_ppos = ppos; +#endif + err = dsound_lock_out ( + dsb, + &hw->info, + old_pos, + len, + &p1, &p2, + &blen1, &blen2, + 0 + ); + if (err) { + return 0; + } + + len1 = blen1 >> hwshift; + len2 = blen2 >> hwshift; + decr = len1 + len2; + + if (p1 && len1) { + dsound_write_sample (hw, p1, len1); + } + + if (p2 && len2) { + dsound_write_sample (hw, p2, len2); + } + + dsound_unlock_out (dsb, p1, p2, blen1, blen2); + ds->old_pos = (old_pos + (decr << hwshift)) % bufsize; + +#ifdef DEBUG_DSOUND + ds->mixed += decr << hwshift; + + dolog ("played %lu mixed %lu diff %ld sec %f\n", + ds->played, + ds->mixed, + ds->mixed - ds->played, + abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second); +#endif + return decr; +} + +static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + HRESULT hr; + DWORD status; + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; + LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; + + if (!dscb) { + dolog ("Attempt to control capture voice without a buffer\n"); + return -1; + } + + switch (cmd) { + case VOICE_ENABLE: + if (dsound_get_status_in (dscb, &status)) { + return -1; + } + + if (status & DSCBSTATUS_CAPTURING) { + dolog ("warning: Voice is already capturing\n"); + return 0; + } + + /* clear ?? */ + + hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not start capturing\n"); + return -1; + } + break; + + case VOICE_DISABLE: + if (dsound_get_status_in (dscb, &status)) { + return -1; + } + + if (status & DSCBSTATUS_CAPTURING) { + hr = IDirectSoundCaptureBuffer_Stop (dscb); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not stop capturing\n"); + return -1; + } + } + else { + dolog ("warning: Voice is not capturing\n"); + } + break; + } + return 0; +} + +static int dsound_read (SWVoiceIn *sw, void *buf, int len) +{ + return audio_pcm_sw_read (sw, buf, len); +} + +static int dsound_run_in (HWVoiceIn *hw) +{ + int err; + HRESULT hr; + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; + LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; + int live, len, dead; + DWORD blen1, blen2; + DWORD len1, len2; + DWORD decr; + DWORD cpos, rpos; + LPVOID p1, p2; + int hwshift; + + if (!dscb) { + dolog ("Attempt to run without capture buffer\n"); + return 0; + } + + hwshift = hw->info.shift; + + live = audio_pcm_hw_get_live_in (hw); + dead = hw->samples - live; + if (!dead) { + return 0; + } + + hr = IDirectSoundCaptureBuffer_GetCurrentPosition ( + dscb, + &cpos, + ds->first_time ? &rpos : NULL + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get capture buffer position\n"); + return 0; + } + + if (ds->first_time) { + ds->first_time = 0; + if (rpos & hw->info.align) { + ldebug ("warning: Misaligned capture read position %ld(%d)\n", + rpos, hw->info.align); + } + hw->wpos = rpos >> hwshift; + } + + if (cpos & hw->info.align) { + ldebug ("warning: Misaligned capture position %ld(%d)\n", + cpos, hw->info.align); + } + cpos >>= hwshift; + + len = audio_ring_dist (cpos, hw->wpos, hw->samples); + if (!len) { + return 0; + } + len = audio_MIN (len, dead); + + err = dsound_lock_in ( + dscb, + &hw->info, + hw->wpos << hwshift, + len << hwshift, + &p1, + &p2, + &blen1, + &blen2, + 0 + ); + if (err) { + return 0; + } + + len1 = blen1 >> hwshift; + len2 = blen2 >> hwshift; + decr = len1 + len2; + + if (p1 && len1) { + hw->conv (hw->conv_buf + hw->wpos, p1, len1); + } + + if (p2 && len2) { + hw->conv (hw->conv_buf, p2, len2); + } + + dsound_unlock_in (dscb, p1, p2, blen1, blen2); + hw->wpos = (hw->wpos + decr) % hw->samples; + return decr; +} + +static void dsound_audio_fini (void *opaque) +{ + HRESULT hr; + dsound *s = opaque; + + if (!s->dsound) { + return; + } + + hr = IDirectSound_Release (s->dsound); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release DirectSound\n"); + } + s->dsound = NULL; + + if (!s->dsound_capture) { + return; + } + + hr = IDirectSoundCapture_Release (s->dsound_capture); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release DirectSoundCapture\n"); + } + s->dsound_capture = NULL; +} + +static void *dsound_audio_init (void) +{ + int err; + HRESULT hr; + dsound *s = &glob_dsound; + + hr = CoInitialize (NULL); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not initialize COM\n"); + return NULL; + } + + hr = CoCreateInstance ( + &CLSID_DirectSound, + NULL, + CLSCTX_ALL, + &IID_IDirectSound, + (void **) &s->dsound + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not create DirectSound instance\n"); + return NULL; + } + + hr = IDirectSound_Initialize (s->dsound, NULL); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not initialize DirectSound\n"); + + hr = IDirectSound_Release (s->dsound); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release DirectSound\n"); + } + s->dsound = NULL; + return NULL; + } + + hr = CoCreateInstance ( + &CLSID_DirectSoundCapture, + NULL, + CLSCTX_ALL, + &IID_IDirectSoundCapture, + (void **) &s->dsound_capture + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); + } + else { + hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); + + hr = IDirectSoundCapture_Release (s->dsound_capture); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release DirectSoundCapture\n"); + } + s->dsound_capture = NULL; + } + } + + err = dsound_open (s); + if (err) { + dsound_audio_fini (s); + return NULL; + } + + return s; +} + +static struct audio_option dsound_options[] = { + { + .name = "LOCK_RETRIES", + .tag = AUD_OPT_INT, + .valp = &conf.lock_retries, + .descr = "Number of times to attempt locking the buffer" + }, + { + .name = "RESTOURE_RETRIES", + .tag = AUD_OPT_INT, + .valp = &conf.restore_retries, + .descr = "Number of times to attempt restoring the buffer" + }, + { + .name = "GETSTATUS_RETRIES", + .tag = AUD_OPT_INT, + .valp = &conf.getstatus_retries, + .descr = "Number of times to attempt getting status of the buffer" + }, + { + .name = "SET_PRIMARY", + .tag = AUD_OPT_BOOL, + .valp = &conf.set_primary, + .descr = "Set the parameters of primary buffer" + }, + { + .name = "LATENCY_MILLIS", + .tag = AUD_OPT_INT, + .valp = &conf.latency_millis, + .descr = "(undocumented)" + }, + { + .name = "PRIMARY_FREQ", + .tag = AUD_OPT_INT, + .valp = &conf.settings.freq, + .descr = "Primary buffer frequency" + }, + { + .name = "PRIMARY_CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.settings.nchannels, + .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)" + }, + { + .name = "PRIMARY_FMT", + .tag = AUD_OPT_FMT, + .valp = &conf.settings.fmt, + .descr = "Primary buffer format" + }, + { + .name = "BUFSIZE_OUT", + .tag = AUD_OPT_INT, + .valp = &conf.bufsize_out, + .descr = "(undocumented)" + }, + { + .name = "BUFSIZE_IN", + .tag = AUD_OPT_INT, + .valp = &conf.bufsize_in, + .descr = "(undocumented)" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops dsound_pcm_ops = { + .init_out = dsound_init_out, + .fini_out = dsound_fini_out, + .run_out = dsound_run_out, + .write = dsound_write, + .ctl_out = dsound_ctl_out, + + .init_in = dsound_init_in, + .fini_in = dsound_fini_in, + .run_in = dsound_run_in, + .read = dsound_read, + .ctl_in = dsound_ctl_in +}; + +struct audio_driver dsound_audio_driver = { + .name = "dsound", + .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", + .options = dsound_options, + .init = dsound_audio_init, + .fini = dsound_audio_fini, + .pcm_ops = &dsound_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = 1, + .voice_size_out = sizeof (DSoundVoiceOut), + .voice_size_in = sizeof (DSoundVoiceIn) +}; diff --git a/audio/esdaudio.c b/audio/esdaudio.c new file mode 100644 index 0000000..eea9cce --- /dev/null +++ b/audio/esdaudio.c @@ -0,0 +1,557 @@ +/* + * QEMU ESD audio driver + * + * Copyright (c) 2006 Frederick Reeve (brushed up by malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "esd" +#include "audio_int.h" +#include "audio_pt_int.h" + +typedef struct { + HWVoiceOut hw; + int done; + int live; + int decr; + int rpos; + void *pcm_buf; + int fd; + struct audio_pt pt; +} ESDVoiceOut; + +typedef struct { + HWVoiceIn hw; + int done; + int dead; + int incr; + int wpos; + void *pcm_buf; + int fd; + struct audio_pt pt; +} ESDVoiceIn; + +static struct { + int samples; + int divisor; + char *dac_host; + char *adc_host; +} conf = { + .samples = 1024, + .divisor = 2, +}; + +static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); +} + +/* playback */ +static void *qesd_thread_out (void *arg) +{ + ESDVoiceOut *esd = arg; + HWVoiceOut *hw = &esd->hw; + int threshold; + + threshold = conf.divisor ? hw->samples / conf.divisor : 0; + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + for (;;) { + int decr, to_mix, rpos; + + for (;;) { + if (esd->done) { + goto exit; + } + + if (esd->live > threshold) { + break; + } + + if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { + goto exit; + } + } + + decr = to_mix = esd->live; + rpos = hw->rpos; + + if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + while (to_mix) { + ssize_t written; + int chunk = audio_MIN (to_mix, hw->samples - rpos); + struct st_sample *src = hw->mix_buf + rpos; + + hw->clip (esd->pcm_buf, src, chunk); + + again: + written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift); + if (written == -1) { + if (errno == EINTR || errno == EAGAIN) { + goto again; + } + qesd_logerr (errno, "write failed\n"); + return NULL; + } + + if (written != chunk << hw->info.shift) { + int wsamples = written >> hw->info.shift; + int wbytes = wsamples << hw->info.shift; + if (wbytes != written) { + dolog ("warning: Misaligned write %d (requested %zd), " + "alignment %d\n", + wbytes, written, hw->info.align + 1); + } + to_mix -= wsamples; + rpos = (rpos + wsamples) % hw->samples; + break; + } + + rpos = (rpos + chunk) % hw->samples; + to_mix -= chunk; + } + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + esd->rpos = rpos; + esd->live -= decr; + esd->decr += decr; + } + + exit: + audio_pt_unlock (&esd->pt, AUDIO_FUNC); + return NULL; +} + +static int qesd_run_out (HWVoiceOut *hw, int live) +{ + int decr; + ESDVoiceOut *esd = (ESDVoiceOut *) hw; + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return 0; + } + + decr = audio_MIN (live, esd->decr); + esd->decr -= decr; + esd->live = live - decr; + hw->rpos = esd->rpos; + if (esd->live > 0) { + audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); + } + else { + audio_pt_unlock (&esd->pt, AUDIO_FUNC); + } + return decr; +} + +static int qesd_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + ESDVoiceOut *esd = (ESDVoiceOut *) hw; + struct audsettings obt_as = *as; + int esdfmt = ESD_STREAM | ESD_PLAY; + + esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; + switch (as->fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + esdfmt |= ESD_BITS8; + obt_as.fmt = AUD_FMT_U8; + break; + + case AUD_FMT_S32: + case AUD_FMT_U32: + dolog ("Will use 16 instead of 32 bit samples\n"); + /* fall through */ + case AUD_FMT_S16: + case AUD_FMT_U16: + deffmt: + esdfmt |= ESD_BITS16; + obt_as.fmt = AUD_FMT_S16; + break; + + default: + dolog ("Internal logic error: Bad audio format %d\n", as->fmt); + goto deffmt; + + } + obt_as.endianness = AUDIO_HOST_ENDIANNESS; + + audio_pcm_init_info (&hw->info, &obt_as); + + hw->samples = conf.samples; + esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!esd->pcm_buf) { + dolog ("Could not allocate buffer (%d bytes)\n", + hw->samples << hw->info.shift); + return -1; + } + + esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); + if (esd->fd < 0) { + qesd_logerr (errno, "esd_play_stream failed\n"); + goto fail1; + } + + if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { + goto fail2; + } + + return 0; + + fail2: + if (close (esd->fd)) { + qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", + AUDIO_FUNC, esd->fd); + } + esd->fd = -1; + + fail1: + g_free (esd->pcm_buf); + esd->pcm_buf = NULL; + return -1; +} + +static void qesd_fini_out (HWVoiceOut *hw) +{ + void *ret; + ESDVoiceOut *esd = (ESDVoiceOut *) hw; + + audio_pt_lock (&esd->pt, AUDIO_FUNC); + esd->done = 1; + audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); + audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); + + if (esd->fd >= 0) { + if (close (esd->fd)) { + qesd_logerr (errno, "failed to close esd socket\n"); + } + esd->fd = -1; + } + + audio_pt_fini (&esd->pt, AUDIO_FUNC); + + g_free (esd->pcm_buf); + esd->pcm_buf = NULL; +} + +static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +/* capture */ +static void *qesd_thread_in (void *arg) +{ + ESDVoiceIn *esd = arg; + HWVoiceIn *hw = &esd->hw; + int threshold; + + threshold = conf.divisor ? hw->samples / conf.divisor : 0; + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + for (;;) { + int incr, to_grab, wpos; + + for (;;) { + if (esd->done) { + goto exit; + } + + if (esd->dead > threshold) { + break; + } + + if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { + goto exit; + } + } + + incr = to_grab = esd->dead; + wpos = hw->wpos; + + if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + while (to_grab) { + ssize_t nread; + int chunk = audio_MIN (to_grab, hw->samples - wpos); + void *buf = advance (esd->pcm_buf, wpos); + + again: + nread = read (esd->fd, buf, chunk << hw->info.shift); + if (nread == -1) { + if (errno == EINTR || errno == EAGAIN) { + goto again; + } + qesd_logerr (errno, "read failed\n"); + return NULL; + } + + if (nread != chunk << hw->info.shift) { + int rsamples = nread >> hw->info.shift; + int rbytes = rsamples << hw->info.shift; + if (rbytes != nread) { + dolog ("warning: Misaligned write %d (requested %zd), " + "alignment %d\n", + rbytes, nread, hw->info.align + 1); + } + to_grab -= rsamples; + wpos = (wpos + rsamples) % hw->samples; + break; + } + + hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift); + wpos = (wpos + chunk) % hw->samples; + to_grab -= chunk; + } + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + esd->wpos = wpos; + esd->dead -= incr; + esd->incr += incr; + } + + exit: + audio_pt_unlock (&esd->pt, AUDIO_FUNC); + return NULL; +} + +static int qesd_run_in (HWVoiceIn *hw) +{ + int live, incr, dead; + ESDVoiceIn *esd = (ESDVoiceIn *) hw; + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return 0; + } + + live = audio_pcm_hw_get_live_in (hw); + dead = hw->samples - live; + incr = audio_MIN (dead, esd->incr); + esd->incr -= incr; + esd->dead = dead - incr; + hw->wpos = esd->wpos; + if (esd->dead > 0) { + audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); + } + else { + audio_pt_unlock (&esd->pt, AUDIO_FUNC); + } + return incr; +} + +static int qesd_read (SWVoiceIn *sw, void *buf, int len) +{ + return audio_pcm_sw_read (sw, buf, len); +} + +static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) +{ + ESDVoiceIn *esd = (ESDVoiceIn *) hw; + struct audsettings obt_as = *as; + int esdfmt = ESD_STREAM | ESD_RECORD; + + esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; + switch (as->fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + esdfmt |= ESD_BITS8; + obt_as.fmt = AUD_FMT_U8; + break; + + case AUD_FMT_S16: + case AUD_FMT_U16: + esdfmt |= ESD_BITS16; + obt_as.fmt = AUD_FMT_S16; + break; + + case AUD_FMT_S32: + case AUD_FMT_U32: + dolog ("Will use 16 instead of 32 bit samples\n"); + esdfmt |= ESD_BITS16; + obt_as.fmt = AUD_FMT_S16; + break; + } + obt_as.endianness = AUDIO_HOST_ENDIANNESS; + + audio_pcm_init_info (&hw->info, &obt_as); + + hw->samples = conf.samples; + esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!esd->pcm_buf) { + dolog ("Could not allocate buffer (%d bytes)\n", + hw->samples << hw->info.shift); + return -1; + } + + esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); + if (esd->fd < 0) { + qesd_logerr (errno, "esd_record_stream failed\n"); + goto fail1; + } + + if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { + goto fail2; + } + + return 0; + + fail2: + if (close (esd->fd)) { + qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", + AUDIO_FUNC, esd->fd); + } + esd->fd = -1; + + fail1: + g_free (esd->pcm_buf); + esd->pcm_buf = NULL; + return -1; +} + +static void qesd_fini_in (HWVoiceIn *hw) +{ + void *ret; + ESDVoiceIn *esd = (ESDVoiceIn *) hw; + + audio_pt_lock (&esd->pt, AUDIO_FUNC); + esd->done = 1; + audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); + audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); + + if (esd->fd >= 0) { + if (close (esd->fd)) { + qesd_logerr (errno, "failed to close esd socket\n"); + } + esd->fd = -1; + } + + audio_pt_fini (&esd->pt, AUDIO_FUNC); + + g_free (esd->pcm_buf); + esd->pcm_buf = NULL; +} + +static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +/* common */ +static void *qesd_audio_init (void) +{ + return &conf; +} + +static void qesd_audio_fini (void *opaque) +{ + (void) opaque; + ldebug ("esd_fini"); +} + +struct audio_option qesd_options[] = { + { + .name = "SAMPLES", + .tag = AUD_OPT_INT, + .valp = &conf.samples, + .descr = "buffer size in samples" + }, + { + .name = "DIVISOR", + .tag = AUD_OPT_INT, + .valp = &conf.divisor, + .descr = "threshold divisor" + }, + { + .name = "DAC_HOST", + .tag = AUD_OPT_STR, + .valp = &conf.dac_host, + .descr = "playback host" + }, + { + .name = "ADC_HOST", + .tag = AUD_OPT_STR, + .valp = &conf.adc_host, + .descr = "capture host" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops qesd_pcm_ops = { + .init_out = qesd_init_out, + .fini_out = qesd_fini_out, + .run_out = qesd_run_out, + .write = qesd_write, + .ctl_out = qesd_ctl_out, + + .init_in = qesd_init_in, + .fini_in = qesd_fini_in, + .run_in = qesd_run_in, + .read = qesd_read, + .ctl_in = qesd_ctl_in, +}; + +struct audio_driver esd_audio_driver = { + .name = "esd", + .descr = "http://en.wikipedia.org/wiki/Esound", + .options = qesd_options, + .init = qesd_audio_init, + .fini = qesd_audio_fini, + .pcm_ops = &qesd_pcm_ops, + .can_be_default = 0, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (ESDVoiceOut), + .voice_size_in = sizeof (ESDVoiceIn) +}; diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c new file mode 100644 index 0000000..fabf84d --- /dev/null +++ b/audio/fmodaudio.c @@ -0,0 +1,685 @@ +/* + * QEMU FMOD audio driver + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "fmod" +#include "audio_int.h" + +typedef struct FMODVoiceOut { + HWVoiceOut hw; + unsigned int old_pos; + FSOUND_SAMPLE *fmod_sample; + int channel; +} FMODVoiceOut; + +typedef struct FMODVoiceIn { + HWVoiceIn hw; + FSOUND_SAMPLE *fmod_sample; +} FMODVoiceIn; + +static struct { + const char *drvname; + int nb_samples; + int freq; + int nb_channels; + int bufsize; + int broken_adc; +} conf = { + .nb_samples = 2048 * 2, + .freq = 44100, + .nb_channels = 2, +}; + +static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", + FMOD_ErrorString (FSOUND_GetError ())); +} + +static void GCC_FMT_ATTR (2, 3) fmod_logerr2 ( + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", + FMOD_ErrorString (FSOUND_GetError ())); +} + +static int fmod_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static void fmod_clear_sample (FMODVoiceOut *fmd) +{ + HWVoiceOut *hw = &fmd->hw; + int status; + void *p1 = 0, *p2 = 0; + unsigned int len1 = 0, len2 = 0; + + status = FSOUND_Sample_Lock ( + fmd->fmod_sample, + 0, + hw->samples << hw->info.shift, + &p1, + &p2, + &len1, + &len2 + ); + + if (!status) { + fmod_logerr ("Failed to lock sample\n"); + return; + } + + if ((len1 & hw->info.align) || (len2 & hw->info.align)) { + dolog ("Lock returned misaligned length %d, %d, alignment %d\n", + len1, len2, hw->info.align + 1); + goto fail; + } + + if ((len1 + len2) - (hw->samples << hw->info.shift)) { + dolog ("Lock returned incomplete length %d, %d\n", + len1 + len2, hw->samples << hw->info.shift); + goto fail; + } + + audio_pcm_info_clear_buf (&hw->info, p1, hw->samples); + + fail: + status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); + if (!status) { + fmod_logerr ("Failed to unlock sample\n"); + } +} + +static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) +{ + int src_len1 = dst_len; + int src_len2 = 0; + int pos = hw->rpos + dst_len; + struct st_sample *src1 = hw->mix_buf + hw->rpos; + struct st_sample *src2 = NULL; + + if (pos > hw->samples) { + src_len1 = hw->samples - hw->rpos; + src2 = hw->mix_buf; + src_len2 = dst_len - src_len1; + pos = src_len2; + } + + if (src_len1) { + hw->clip (dst, src1, src_len1); + } + + if (src_len2) { + dst = advance (dst, src_len1 << hw->info.shift); + hw->clip (dst, src2, src_len2); + } + + hw->rpos = pos % hw->samples; +} + +static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, + unsigned int blen1, unsigned int blen2) +{ + int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2); + if (!status) { + fmod_logerr ("Failed to unlock sample\n"); + return -1; + } + return 0; +} + +static int fmod_lock_sample ( + FSOUND_SAMPLE *sample, + struct audio_pcm_info *info, + int pos, + int len, + void **p1, + void **p2, + unsigned int *blen1, + unsigned int *blen2 + ) +{ + int status; + + status = FSOUND_Sample_Lock ( + sample, + pos << info->shift, + len << info->shift, + p1, + p2, + blen1, + blen2 + ); + + if (!status) { + fmod_logerr ("Failed to lock sample\n"); + return -1; + } + + if ((*blen1 & info->align) || (*blen2 & info->align)) { + dolog ("Lock returned misaligned length %d, %d, alignment %d\n", + *blen1, *blen2, info->align + 1); + + fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); + + *p1 = NULL - 1; + *p2 = NULL - 1; + *blen1 = ~0U; + *blen2 = ~0U; + return -1; + } + + if (!*p1 && *blen1) { + dolog ("warning: !p1 && blen1=%d\n", *blen1); + *blen1 = 0; + } + + if (!p2 && *blen2) { + dolog ("warning: !p2 && blen2=%d\n", *blen2); + *blen2 = 0; + } + + return 0; +} + +static int fmod_run_out (HWVoiceOut *hw, int live) +{ + FMODVoiceOut *fmd = (FMODVoiceOut *) hw; + int decr; + void *p1 = 0, *p2 = 0; + unsigned int blen1 = 0, blen2 = 0; + unsigned int len1 = 0, len2 = 0; + + if (!hw->pending_disable) { + return 0; + } + + decr = live; + + if (fmd->channel >= 0) { + int len = decr; + int old_pos = fmd->old_pos; + int ppos = FSOUND_GetCurrentPosition (fmd->channel); + + if (ppos == old_pos || !ppos) { + return 0; + } + + if ((old_pos < ppos) && ((old_pos + len) > ppos)) { + len = ppos - old_pos; + } + else { + if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) { + len = hw->samples - old_pos + ppos; + } + } + decr = len; + + if (audio_bug (AUDIO_FUNC, decr < 0)) { + dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n", + decr, live, ppos, old_pos, len); + return 0; + } + } + + + if (!decr) { + return 0; + } + + if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, + fmd->old_pos, decr, + &p1, &p2, + &blen1, &blen2)) { + return 0; + } + + len1 = blen1 >> hw->info.shift; + len2 = blen2 >> hw->info.shift; + ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); + decr = len1 + len2; + + if (p1 && len1) { + fmod_write_sample (hw, p1, len1); + } + + if (p2 && len2) { + fmod_write_sample (hw, p2, len2); + } + + fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); + + fmd->old_pos = (fmd->old_pos + decr) % hw->samples; + return decr; +} + +static int aud_to_fmodfmt (audfmt_e fmt, int stereo) +{ + int mode = FSOUND_LOOP_NORMAL; + + switch (fmt) { + case AUD_FMT_S8: + mode |= FSOUND_SIGNED | FSOUND_8BITS; + break; + + case AUD_FMT_U8: + mode |= FSOUND_UNSIGNED | FSOUND_8BITS; + break; + + case AUD_FMT_S16: + mode |= FSOUND_SIGNED | FSOUND_16BITS; + break; + + case AUD_FMT_U16: + mode |= FSOUND_UNSIGNED | FSOUND_16BITS; + break; + + default: + dolog ("Internal logic error: Bad audio format %d\n", fmt); +#ifdef DEBUG_FMOD + abort (); +#endif + mode |= FSOUND_8BITS; + } + mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; + return mode; +} + +static void fmod_fini_out (HWVoiceOut *hw) +{ + FMODVoiceOut *fmd = (FMODVoiceOut *) hw; + + if (fmd->fmod_sample) { + FSOUND_Sample_Free (fmd->fmod_sample); + fmd->fmod_sample = 0; + + if (fmd->channel >= 0) { + FSOUND_StopSound (fmd->channel); + } + } +} + +static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + int mode, channel; + FMODVoiceOut *fmd = (FMODVoiceOut *) hw; + struct audsettings obt_as = *as; + + mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); + fmd->fmod_sample = FSOUND_Sample_Alloc ( + FSOUND_FREE, /* index */ + conf.nb_samples, /* length */ + mode, /* mode */ + as->freq, /* freq */ + 255, /* volume */ + 128, /* pan */ + 255 /* priority */ + ); + + if (!fmd->fmod_sample) { + fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); + return -1; + } + + channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); + if (channel < 0) { + fmod_logerr2 ("DAC", "Failed to start playing sound\n"); + FSOUND_Sample_Free (fmd->fmod_sample); + return -1; + } + fmd->channel = channel; + + /* FMOD always operates on little endian frames? */ + obt_as.endianness = 0; + audio_pcm_init_info (&hw->info, &obt_as); + hw->samples = conf.nb_samples; + return 0; +} + +static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + int status; + FMODVoiceOut *fmd = (FMODVoiceOut *) hw; + + switch (cmd) { + case VOICE_ENABLE: + fmod_clear_sample (fmd); + status = FSOUND_SetPaused (fmd->channel, 0); + if (!status) { + fmod_logerr ("Failed to resume channel %d\n", fmd->channel); + } + break; + + case VOICE_DISABLE: + status = FSOUND_SetPaused (fmd->channel, 1); + if (!status) { + fmod_logerr ("Failed to pause channel %d\n", fmd->channel); + } + break; + } + return 0; +} + +static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as) +{ + int mode; + FMODVoiceIn *fmd = (FMODVoiceIn *) hw; + struct audsettings obt_as = *as; + + if (conf.broken_adc) { + return -1; + } + + mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); + fmd->fmod_sample = FSOUND_Sample_Alloc ( + FSOUND_FREE, /* index */ + conf.nb_samples, /* length */ + mode, /* mode */ + as->freq, /* freq */ + 255, /* volume */ + 128, /* pan */ + 255 /* priority */ + ); + + if (!fmd->fmod_sample) { + fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); + return -1; + } + + /* FMOD always operates on little endian frames? */ + obt_as.endianness = 0; + audio_pcm_init_info (&hw->info, &obt_as); + hw->samples = conf.nb_samples; + return 0; +} + +static void fmod_fini_in (HWVoiceIn *hw) +{ + FMODVoiceIn *fmd = (FMODVoiceIn *) hw; + + if (fmd->fmod_sample) { + FSOUND_Record_Stop (); + FSOUND_Sample_Free (fmd->fmod_sample); + fmd->fmod_sample = 0; + } +} + +static int fmod_run_in (HWVoiceIn *hw) +{ + FMODVoiceIn *fmd = (FMODVoiceIn *) hw; + int hwshift = hw->info.shift; + int live, dead, new_pos, len; + unsigned int blen1 = 0, blen2 = 0; + unsigned int len1, len2; + unsigned int decr; + void *p1, *p2; + + live = audio_pcm_hw_get_live_in (hw); + dead = hw->samples - live; + if (!dead) { + return 0; + } + + new_pos = FSOUND_Record_GetPosition (); + if (new_pos < 0) { + fmod_logerr ("Could not get recording position\n"); + return 0; + } + + len = audio_ring_dist (new_pos, hw->wpos, hw->samples); + if (!len) { + return 0; + } + len = audio_MIN (len, dead); + + if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, + hw->wpos, len, + &p1, &p2, + &blen1, &blen2)) { + return 0; + } + + len1 = blen1 >> hwshift; + len2 = blen2 >> hwshift; + decr = len1 + len2; + + if (p1 && blen1) { + hw->conv (hw->conv_buf + hw->wpos, p1, len1); + } + if (p2 && len2) { + hw->conv (hw->conv_buf, p2, len2); + } + + fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); + hw->wpos = (hw->wpos + decr) % hw->samples; + return decr; +} + +static struct { + const char *name; + int type; +} drvtab[] = { + { .name = "none", .type = FSOUND_OUTPUT_NOSOUND }, +#ifdef _WIN32 + { .name = "winmm", .type = FSOUND_OUTPUT_WINMM }, + { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND }, + { .name = "a3d", .type = FSOUND_OUTPUT_A3D }, + { .name = "asio", .type = FSOUND_OUTPUT_ASIO }, +#endif +#ifdef __linux__ + { .name = "oss", .type = FSOUND_OUTPUT_OSS }, + { .name = "alsa", .type = FSOUND_OUTPUT_ALSA }, + { .name = "esd", .type = FSOUND_OUTPUT_ESD }, +#endif +#ifdef __APPLE__ + { .name = "mac", .type = FSOUND_OUTPUT_MAC }, +#endif +#if 0 + { .name = "xbox", .type = FSOUND_OUTPUT_XBOX }, + { .name = "ps2", .type = FSOUND_OUTPUT_PS2 }, + { .name = "gcube", .type = FSOUND_OUTPUT_GC }, +#endif + { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME } +}; + +static void *fmod_audio_init (void) +{ + size_t i; + double ver; + int status; + int output_type = -1; + const char *drv = conf.drvname; + + ver = FSOUND_GetVersion (); + if (ver < FMOD_VERSION) { + dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); + return NULL; + } + +#ifdef __linux__ + if (ver < 3.75) { + dolog ("FMOD before 3.75 has bug preventing ADC from working\n" + "ADC will be disabled.\n"); + conf.broken_adc = 1; + } +#endif + + if (drv) { + int found = 0; + for (i = 0; i < ARRAY_SIZE (drvtab); i++) { + if (!strcmp (drv, drvtab[i].name)) { + output_type = drvtab[i].type; + found = 1; + break; + } + } + if (!found) { + dolog ("Unknown FMOD driver `%s'\n", drv); + dolog ("Valid drivers:\n"); + for (i = 0; i < ARRAY_SIZE (drvtab); i++) { + dolog (" %s\n", drvtab[i].name); + } + } + } + + if (output_type != -1) { + status = FSOUND_SetOutput (output_type); + if (!status) { + fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type); + return NULL; + } + } + + if (conf.bufsize) { + status = FSOUND_SetBufferSize (conf.bufsize); + if (!status) { + fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize); + } + } + + status = FSOUND_Init (conf.freq, conf.nb_channels, 0); + if (!status) { + fmod_logerr ("FSOUND_Init failed\n"); + return NULL; + } + + return &conf; +} + +static int fmod_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + int status; + FMODVoiceIn *fmd = (FMODVoiceIn *) hw; + + switch (cmd) { + case VOICE_ENABLE: + status = FSOUND_Record_StartSample (fmd->fmod_sample, 1); + if (!status) { + fmod_logerr ("Failed to start recording\n"); + } + break; + + case VOICE_DISABLE: + status = FSOUND_Record_Stop (); + if (!status) { + fmod_logerr ("Failed to stop recording\n"); + } + break; + } + return 0; +} + +static void fmod_audio_fini (void *opaque) +{ + (void) opaque; + FSOUND_Close (); +} + +static struct audio_option fmod_options[] = { + { + .name = "DRV", + .tag = AUD_OPT_STR, + .valp = &conf.drvname, + .descr = "FMOD driver" + }, + { + .name = "FREQ", + .tag = AUD_OPT_INT, + .valp = &conf.freq, + .descr = "Default frequency" + }, + { + .name = "SAMPLES", + .tag = AUD_OPT_INT, + .valp = &conf.nb_samples, + .descr = "Buffer size in samples" + }, + { + .name = "CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.nb_channels, + .descr = "Number of default channels (1 - mono, 2 - stereo)" + }, + { + .name = "BUFSIZE", + .tag = AUD_OPT_INT, + .valp = &conf.bufsize, + .descr = "(undocumented)" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops fmod_pcm_ops = { + .init_out = fmod_init_out, + .fini_out = fmod_fini_out, + .run_out = fmod_run_out, + .write = fmod_write, + .ctl_out = fmod_ctl_out, + + .init_in = fmod_init_in, + .fini_in = fmod_fini_in, + .run_in = fmod_run_in, + .read = fmod_read, + .ctl_in = fmod_ctl_in +}; + +struct audio_driver fmod_audio_driver = { + .name = "fmod", + .descr = "FMOD 3.xx http://www.fmod.org", + .options = fmod_options, + .init = fmod_audio_init, + .fini = fmod_audio_fini, + .pcm_ops = &fmod_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (FMODVoiceOut), + .voice_size_in = sizeof (FMODVoiceIn) +}; diff --git a/audio/mixeng.c b/audio/mixeng.c new file mode 100644 index 0000000..0e4976f --- /dev/null +++ b/audio/mixeng.c @@ -0,0 +1,366 @@ +/* + * QEMU Mixing engine + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * Copyright (c) 1998 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "mixeng" +#include "audio_int.h" + +/* 8 bit */ +#define ENDIAN_CONVERSION natural +#define ENDIAN_CONVERT(v) (v) + +/* Signed 8 bit */ +#define BSIZE 8 +#define ITYPE int +#define IN_MIN SCHAR_MIN +#define IN_MAX SCHAR_MAX +#define SIGNED +#define SHIFT 8 +#include "mixeng_template.h" +#undef SIGNED +#undef IN_MAX +#undef IN_MIN +#undef BSIZE +#undef ITYPE +#undef SHIFT + +/* Unsigned 8 bit */ +#define BSIZE 8 +#define ITYPE uint +#define IN_MIN 0 +#define IN_MAX UCHAR_MAX +#define SHIFT 8 +#include "mixeng_template.h" +#undef IN_MAX +#undef IN_MIN +#undef BSIZE +#undef ITYPE +#undef SHIFT + +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION + +/* Signed 16 bit */ +#define BSIZE 16 +#define ITYPE int +#define IN_MIN SHRT_MIN +#define IN_MAX SHRT_MAX +#define SIGNED +#define SHIFT 16 +#define ENDIAN_CONVERSION natural +#define ENDIAN_CONVERT(v) (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#define ENDIAN_CONVERSION swap +#define ENDIAN_CONVERT(v) bswap16 (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#undef SIGNED +#undef IN_MAX +#undef IN_MIN +#undef BSIZE +#undef ITYPE +#undef SHIFT + +/* Unsigned 16 bit */ +#define BSIZE 16 +#define ITYPE uint +#define IN_MIN 0 +#define IN_MAX USHRT_MAX +#define SHIFT 16 +#define ENDIAN_CONVERSION natural +#define ENDIAN_CONVERT(v) (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#define ENDIAN_CONVERSION swap +#define ENDIAN_CONVERT(v) bswap16 (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#undef IN_MAX +#undef IN_MIN +#undef BSIZE +#undef ITYPE +#undef SHIFT + +/* Signed 32 bit */ +#define BSIZE 32 +#define ITYPE int +#define IN_MIN INT32_MIN +#define IN_MAX INT32_MAX +#define SIGNED +#define SHIFT 32 +#define ENDIAN_CONVERSION natural +#define ENDIAN_CONVERT(v) (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#define ENDIAN_CONVERSION swap +#define ENDIAN_CONVERT(v) bswap32 (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#undef SIGNED +#undef IN_MAX +#undef IN_MIN +#undef BSIZE +#undef ITYPE +#undef SHIFT + +/* Unsigned 32 bit */ +#define BSIZE 32 +#define ITYPE uint +#define IN_MIN 0 +#define IN_MAX UINT32_MAX +#define SHIFT 32 +#define ENDIAN_CONVERSION natural +#define ENDIAN_CONVERT(v) (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#define ENDIAN_CONVERSION swap +#define ENDIAN_CONVERT(v) bswap32 (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#undef IN_MAX +#undef IN_MIN +#undef BSIZE +#undef ITYPE +#undef SHIFT + +t_sample *mixeng_conv[2][2][2][3] = { + { + { + { + conv_natural_uint8_t_to_mono, + conv_natural_uint16_t_to_mono, + conv_natural_uint32_t_to_mono + }, + { + conv_natural_uint8_t_to_mono, + conv_swap_uint16_t_to_mono, + conv_swap_uint32_t_to_mono, + } + }, + { + { + conv_natural_int8_t_to_mono, + conv_natural_int16_t_to_mono, + conv_natural_int32_t_to_mono + }, + { + conv_natural_int8_t_to_mono, + conv_swap_int16_t_to_mono, + conv_swap_int32_t_to_mono + } + } + }, + { + { + { + conv_natural_uint8_t_to_stereo, + conv_natural_uint16_t_to_stereo, + conv_natural_uint32_t_to_stereo + }, + { + conv_natural_uint8_t_to_stereo, + conv_swap_uint16_t_to_stereo, + conv_swap_uint32_t_to_stereo + } + }, + { + { + conv_natural_int8_t_to_stereo, + conv_natural_int16_t_to_stereo, + conv_natural_int32_t_to_stereo + }, + { + conv_natural_int8_t_to_stereo, + conv_swap_int16_t_to_stereo, + conv_swap_int32_t_to_stereo, + } + } + } +}; + +f_sample *mixeng_clip[2][2][2][3] = { + { + { + { + clip_natural_uint8_t_from_mono, + clip_natural_uint16_t_from_mono, + clip_natural_uint32_t_from_mono + }, + { + clip_natural_uint8_t_from_mono, + clip_swap_uint16_t_from_mono, + clip_swap_uint32_t_from_mono + } + }, + { + { + clip_natural_int8_t_from_mono, + clip_natural_int16_t_from_mono, + clip_natural_int32_t_from_mono + }, + { + clip_natural_int8_t_from_mono, + clip_swap_int16_t_from_mono, + clip_swap_int32_t_from_mono + } + } + }, + { + { + { + clip_natural_uint8_t_from_stereo, + clip_natural_uint16_t_from_stereo, + clip_natural_uint32_t_from_stereo + }, + { + clip_natural_uint8_t_from_stereo, + clip_swap_uint16_t_from_stereo, + clip_swap_uint32_t_from_stereo + } + }, + { + { + clip_natural_int8_t_from_stereo, + clip_natural_int16_t_from_stereo, + clip_natural_int32_t_from_stereo + }, + { + clip_natural_int8_t_from_stereo, + clip_swap_int16_t_from_stereo, + clip_swap_int32_t_from_stereo + } + } + } +}; + +/* + * August 21, 1998 + * Copyright 1998 Fabrice Bellard. + * + * [Rewrote completly the code of Lance Norskog And Sundry + * Contributors with a more efficient algorithm.] + * + * This source code is freely redistributable and may be used for + * any purpose. This copyright notice must be maintained. + * Lance Norskog And Sundry Contributors are not responsible for + * the consequences of using this software. + */ + +/* + * Sound Tools rate change effect file. + */ +/* + * Linear Interpolation. + * + * The use of fractional increment allows us to use no buffer. It + * avoid the problems at the end of the buffer we had with the old + * method which stored a possibly big buffer of size + * lcm(in_rate,out_rate). + * + * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If + * the input & output frequencies are equal, a delay of one sample is + * introduced. Limited to processing 32-bit count worth of samples. + * + * 1 << FRAC_BITS evaluating to zero in several places. Changed with + * an (unsigned long) cast to make it safe. MarkMLl 2/1/99 + */ + +/* Private data */ +struct rate { + uint64_t opos; + uint64_t opos_inc; + uint32_t ipos; /* position in the input stream (integer) */ + struct st_sample ilast; /* last sample in the input stream */ +}; + +/* + * Prepare processing. + */ +void *st_rate_start (int inrate, int outrate) +{ + struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate)); + + if (!rate) { + dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate)); + return NULL; + } + + rate->opos = 0; + + /* increment */ + rate->opos_inc = ((uint64_t) inrate << 32) / outrate; + + rate->ipos = 0; + rate->ilast.l = 0; + rate->ilast.r = 0; + return rate; +} + +#define NAME st_rate_flow_mix +#define OP(a, b) a += b +#include "rate_template.h" + +#define NAME st_rate_flow +#define OP(a, b) a = b +#include "rate_template.h" + +void st_rate_stop (void *opaque) +{ + g_free (opaque); +} + +void mixeng_clear (struct st_sample *buf, int len) +{ + memset (buf, 0, len * sizeof (struct st_sample)); +} + +void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) +{ + if (vol->mute) { + mixeng_clear (buf, len); + return; + } + + while (len--) { +#ifdef FLOAT_MIXENG + buf->l = buf->l * vol->l; + buf->r = buf->r * vol->r; +#else + buf->l = (buf->l * vol->l) >> 32; + buf->r = (buf->r * vol->r) >> 32; +#endif + buf += 1; + } +} diff --git a/audio/mixeng.h b/audio/mixeng.h new file mode 100644 index 0000000..9de443b --- /dev/null +++ b/audio/mixeng.h @@ -0,0 +1,51 @@ +/* + * QEMU Mixing engine header + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_MIXENG_H +#define QEMU_MIXENG_H + +#ifdef FLOAT_MIXENG +typedef float mixeng_real; +struct mixeng_volume { int mute; mixeng_real r; mixeng_real l; }; +struct st_sample { mixeng_real l; mixeng_real r; }; +#else +struct mixeng_volume { int mute; int64_t r; int64_t l; }; +struct st_sample { int64_t l; int64_t r; }; +#endif + +typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); +typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); + +extern t_sample *mixeng_conv[2][2][2][3]; +extern f_sample *mixeng_clip[2][2][2][3]; + +void *st_rate_start (int inrate, int outrate); +void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, + int *isamp, int *osamp); +void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, + int *isamp, int *osamp); +void st_rate_stop (void *opaque); +void mixeng_clear (struct st_sample *buf, int len); +void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); + +#endif /* mixeng.h */ diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h new file mode 100644 index 0000000..77cc89b --- /dev/null +++ b/audio/mixeng_template.h @@ -0,0 +1,154 @@ +/* + * QEMU Mixing engine + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Tusen tack till Mike Nordell + * dec++'ified by Dscho + */ + +#ifndef SIGNED +#define HALF (IN_MAX >> 1) +#endif + +#define ET glue (ENDIAN_CONVERSION, glue (glue (glue (_, ITYPE), BSIZE), _t)) +#define IN_T glue (glue (ITYPE, BSIZE), _t) + +#ifdef FLOAT_MIXENG +static inline mixeng_real glue (conv_, ET) (IN_T v) +{ + IN_T nv = ENDIAN_CONVERT (v); + +#ifdef RECIPROCAL +#ifdef SIGNED + return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN)); +#else + return (nv - HALF) * (1.f / (mixeng_real) IN_MAX); +#endif +#else /* !RECIPROCAL */ +#ifdef SIGNED + return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN); +#else + return (nv - HALF) / (mixeng_real) IN_MAX; +#endif +#endif +} + +static inline IN_T glue (clip_, ET) (mixeng_real v) +{ + if (v >= 0.5) { + return IN_MAX; + } + else if (v < -0.5) { + return IN_MIN; + } + +#ifdef SIGNED + return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN))); +#else + return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF)); +#endif +} + +#else /* !FLOAT_MIXENG */ + +static inline int64_t glue (conv_, ET) (IN_T v) +{ + IN_T nv = ENDIAN_CONVERT (v); +#ifdef SIGNED + return ((int64_t) nv) << (32 - SHIFT); +#else + return ((int64_t) nv - HALF) << (32 - SHIFT); +#endif +} + +static inline IN_T glue (clip_, ET) (int64_t v) +{ + if (v >= 0x7f000000) { + return IN_MAX; + } + else if (v < -2147483648LL) { + return IN_MIN; + } + +#ifdef SIGNED + return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT))); +#else + return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF)); +#endif +} +#endif + +static void glue (glue (conv_, ET), _to_stereo) + (struct st_sample *dst, const void *src, int samples) +{ + struct st_sample *out = dst; + IN_T *in = (IN_T *) src; + + while (samples--) { + out->l = glue (conv_, ET) (*in++); + out->r = glue (conv_, ET) (*in++); + out += 1; + } +} + +static void glue (glue (conv_, ET), _to_mono) + (struct st_sample *dst, const void *src, int samples) +{ + struct st_sample *out = dst; + IN_T *in = (IN_T *) src; + + while (samples--) { + out->l = glue (conv_, ET) (in[0]); + out->r = out->l; + out += 1; + in += 1; + } +} + +static void glue (glue (clip_, ET), _from_stereo) + (void *dst, const struct st_sample *src, int samples) +{ + const struct st_sample *in = src; + IN_T *out = (IN_T *) dst; + while (samples--) { + *out++ = glue (clip_, ET) (in->l); + *out++ = glue (clip_, ET) (in->r); + in += 1; + } +} + +static void glue (glue (clip_, ET), _from_mono) + (void *dst, const struct st_sample *src, int samples) +{ + const struct st_sample *in = src; + IN_T *out = (IN_T *) dst; + while (samples--) { + *out++ = glue (clip_, ET) (in->l + in->r); + in += 1; + } +} + +#undef ET +#undef HALF +#undef IN_T diff --git a/audio/noaudio.c b/audio/noaudio.c new file mode 100644 index 0000000..cb38662 --- /dev/null +++ b/audio/noaudio.c @@ -0,0 +1,173 @@ +/* + * QEMU Timer based audio emulation + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "audio.h" +#include "qemu/timer.h" + +#define AUDIO_CAP "noaudio" +#include "audio_int.h" + +typedef struct NoVoiceOut { + HWVoiceOut hw; + int64_t old_ticks; +} NoVoiceOut; + +typedef struct NoVoiceIn { + HWVoiceIn hw; + int64_t old_ticks; +} NoVoiceIn; + +static int no_run_out (HWVoiceOut *hw, int live) +{ + NoVoiceOut *no = (NoVoiceOut *) hw; + int decr, samples; + int64_t now; + int64_t ticks; + int64_t bytes; + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ticks = now - no->old_ticks; + bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); + bytes = audio_MIN (bytes, INT_MAX); + samples = bytes >> hw->info.shift; + + no->old_ticks = now; + decr = audio_MIN (live, samples); + hw->rpos = (hw->rpos + decr) % hw->samples; + return decr; +} + +static int no_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int no_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + audio_pcm_init_info (&hw->info, as); + hw->samples = 1024; + return 0; +} + +static void no_fini_out (HWVoiceOut *hw) +{ + (void) hw; +} + +static int no_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +static int no_init_in (HWVoiceIn *hw, struct audsettings *as) +{ + audio_pcm_init_info (&hw->info, as); + hw->samples = 1024; + return 0; +} + +static void no_fini_in (HWVoiceIn *hw) +{ + (void) hw; +} + +static int no_run_in (HWVoiceIn *hw) +{ + NoVoiceIn *no = (NoVoiceIn *) hw; + int live = audio_pcm_hw_get_live_in (hw); + int dead = hw->samples - live; + int samples = 0; + + if (dead) { + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t ticks = now - no->old_ticks; + int64_t bytes = + muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); + + no->old_ticks = now; + bytes = audio_MIN (bytes, INT_MAX); + samples = bytes >> hw->info.shift; + samples = audio_MIN (samples, dead); + } + return samples; +} + +static int no_read (SWVoiceIn *sw, void *buf, int size) +{ + /* use custom code here instead of audio_pcm_sw_read() to avoid + * useless resampling/mixing */ + int samples = size >> sw->info.shift; + int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; + int to_clear = audio_MIN (samples, total); + sw->total_hw_samples_acquired += total; + audio_pcm_info_clear_buf (&sw->info, buf, to_clear); + return to_clear << sw->info.shift; +} + +static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +static void *no_audio_init (void) +{ + return &no_audio_init; +} + +static void no_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_pcm_ops no_pcm_ops = { + .init_out = no_init_out, + .fini_out = no_fini_out, + .run_out = no_run_out, + .write = no_write, + .ctl_out = no_ctl_out, + + .init_in = no_init_in, + .fini_in = no_fini_in, + .run_in = no_run_in, + .read = no_read, + .ctl_in = no_ctl_in +}; + +struct audio_driver no_audio_driver = { + .name = "none", + .descr = "Timer based audio emulation", + .options = NULL, + .init = no_audio_init, + .fini = no_audio_fini, + .pcm_ops = &no_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (NoVoiceOut), + .voice_size_in = sizeof (NoVoiceIn) +}; diff --git a/audio/ossaudio.c b/audio/ossaudio.c new file mode 100644 index 0000000..4db2ca6 --- /dev/null +++ b/audio/ossaudio.c @@ -0,0 +1,942 @@ +/* + * QEMU OSS audio driver + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include "qemu-common.h" +#include "qemu/main-loop.h" +#include "qemu/host-utils.h" +#include "audio.h" + +#define AUDIO_CAP "oss" +#include "audio_int.h" + +#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY +#define USE_DSP_POLICY +#endif + +typedef struct OSSVoiceOut { + HWVoiceOut hw; + void *pcm_buf; + int fd; + int wpos; + int nfrags; + int fragsize; + int mmapped; + int pending; +} OSSVoiceOut; + +typedef struct OSSVoiceIn { + HWVoiceIn hw; + void *pcm_buf; + int fd; + int nfrags; + int fragsize; +} OSSVoiceIn; + +static struct { + int try_mmap; + int nfrags; + int fragsize; + const char *devpath_out; + const char *devpath_in; + int debug; + int exclusive; + int policy; +} conf = { + .try_mmap = 0, + .nfrags = 4, + .fragsize = 4096, + .devpath_out = "/dev/dsp", + .devpath_in = "/dev/dsp", + .debug = 0, + .exclusive = 0, + .policy = 5 +}; + +struct oss_params { + int freq; + audfmt_e fmt; + int nchannels; + int nfrags; + int fragsize; +}; + +static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); +} + +static void GCC_FMT_ATTR (3, 4) oss_logerr2 ( + int err, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); +} + +static void oss_anal_close (int *fdp) +{ + int err; + + qemu_set_fd_handler (*fdp, NULL, NULL, NULL); + err = close (*fdp); + if (err) { + oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp); + } + *fdp = -1; +} + +static void oss_helper_poll_out (void *opaque) +{ + (void) opaque; + audio_run ("oss_poll_out"); +} + +static void oss_helper_poll_in (void *opaque) +{ + (void) opaque; + audio_run ("oss_poll_in"); +} + +static int oss_poll_out (HWVoiceOut *hw) +{ + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + + return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); +} + +static int oss_poll_in (HWVoiceIn *hw) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + + return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); +} + +static int oss_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int aud_to_ossfmt (audfmt_e fmt, int endianness) +{ + switch (fmt) { + case AUD_FMT_S8: + return AFMT_S8; + + case AUD_FMT_U8: + return AFMT_U8; + + case AUD_FMT_S16: + if (endianness) { + return AFMT_S16_BE; + } + else { + return AFMT_S16_LE; + } + + case AUD_FMT_U16: + if (endianness) { + return AFMT_U16_BE; + } + else { + return AFMT_U16_LE; + } + + default: + dolog ("Internal logic error: Bad audio format %d\n", fmt); +#ifdef DEBUG_AUDIO + abort (); +#endif + return AFMT_U8; + } +} + +static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness) +{ + switch (ossfmt) { + case AFMT_S8: + *endianness = 0; + *fmt = AUD_FMT_S8; + break; + + case AFMT_U8: + *endianness = 0; + *fmt = AUD_FMT_U8; + break; + + case AFMT_S16_LE: + *endianness = 0; + *fmt = AUD_FMT_S16; + break; + + case AFMT_U16_LE: + *endianness = 0; + *fmt = AUD_FMT_U16; + break; + + case AFMT_S16_BE: + *endianness = 1; + *fmt = AUD_FMT_S16; + break; + + case AFMT_U16_BE: + *endianness = 1; + *fmt = AUD_FMT_U16; + break; + + default: + dolog ("Unrecognized audio format %d\n", ossfmt); + return -1; + } + + return 0; +} + +#if defined DEBUG_MISMATCHES || defined DEBUG +static void oss_dump_info (struct oss_params *req, struct oss_params *obt) +{ + dolog ("parameter | requested value | obtained value\n"); + dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); + dolog ("channels | %10d | %10d\n", + req->nchannels, obt->nchannels); + dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); + dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags); + dolog ("fragsize | %10d | %10d\n", + req->fragsize, obt->fragsize); +} +#endif + +#ifdef USE_DSP_POLICY +static int oss_get_version (int fd, int *version, const char *typ) +{ + if (ioctl (fd, OSS_GETVERSION, &version)) { +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + /* + * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION + * since 7.x, but currently only on the mixer device (or in + * the Linuxolator), and in the native version that part of + * the code is in fact never reached so the ioctl fails anyway. + * Until this is fixed, just check the errno and if its what + * FreeBSD's sound drivers return atm assume they are new enough. + */ + if (errno == EINVAL) { + *version = 0x040000; + return 0; + } +#endif + oss_logerr2 (errno, typ, "Failed to get OSS version\n"); + return -1; + } + return 0; +} +#endif + +static int oss_open (int in, struct oss_params *req, + struct oss_params *obt, int *pfd) +{ + int fd; + int oflags = conf.exclusive ? O_EXCL : 0; + audio_buf_info abinfo; + int fmt, freq, nchannels; + int setfragment = 1; + const char *dspname = in ? conf.devpath_in : conf.devpath_out; + const char *typ = in ? "ADC" : "DAC"; + + /* Kludge needed to have working mmap on Linux */ + oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); + + fd = open (dspname, oflags | O_NONBLOCK); + if (-1 == fd) { + oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname); + return -1; + } + + freq = req->freq; + nchannels = req->nchannels; + fmt = req->fmt; + + if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { + oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); + goto err; + } + + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) { + oss_logerr2 (errno, typ, "Failed to set number of channels %d\n", + req->nchannels); + goto err; + } + + if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) { + oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq); + goto err; + } + + if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) { + oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n"); + goto err; + } + +#ifdef USE_DSP_POLICY + if (conf.policy >= 0) { + int version; + + if (!oss_get_version (fd, &version, typ)) { + if (conf.debug) { + dolog ("OSS version = %#x\n", version); + } + + if (version >= 0x040000) { + int policy = conf.policy; + if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { + oss_logerr2 (errno, typ, + "Failed to set timing policy to %d\n", + conf.policy); + goto err; + } + setfragment = 0; + } + } + } +#endif + + if (setfragment) { + int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize); + if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { + oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n", + req->nfrags, req->fragsize); + goto err; + } + } + + if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) { + oss_logerr2 (errno, typ, "Failed to get buffer length\n"); + goto err; + } + + if (!abinfo.fragstotal || !abinfo.fragsize) { + AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n", + abinfo.fragstotal, abinfo.fragsize, typ); + goto err; + } + + obt->fmt = fmt; + obt->nchannels = nchannels; + obt->freq = freq; + obt->nfrags = abinfo.fragstotal; + obt->fragsize = abinfo.fragsize; + *pfd = fd; + +#ifdef DEBUG_MISMATCHES + if ((req->fmt != obt->fmt) || + (req->nchannels != obt->nchannels) || + (req->freq != obt->freq) || + (req->fragsize != obt->fragsize) || + (req->nfrags != obt->nfrags)) { + dolog ("Audio parameters mismatch\n"); + oss_dump_info (req, obt); + } +#endif + +#ifdef DEBUG + oss_dump_info (req, obt); +#endif + return 0; + + err: + oss_anal_close (&fd); + return -1; +} + +static void oss_write_pending (OSSVoiceOut *oss) +{ + HWVoiceOut *hw = &oss->hw; + + if (oss->mmapped) { + return; + } + + while (oss->pending) { + int samples_written; + ssize_t bytes_written; + int samples_till_end = hw->samples - oss->wpos; + int samples_to_write = audio_MIN (oss->pending, samples_till_end); + int bytes_to_write = samples_to_write << hw->info.shift; + void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift); + + bytes_written = write (oss->fd, pcm, bytes_to_write); + if (bytes_written < 0) { + if (errno != EAGAIN) { + oss_logerr (errno, "failed to write %d bytes\n", + bytes_to_write); + } + break; + } + + if (bytes_written & hw->info.align) { + dolog ("misaligned write asked for %d, but got %zd\n", + bytes_to_write, bytes_written); + return; + } + + samples_written = bytes_written >> hw->info.shift; + oss->pending -= samples_written; + oss->wpos = (oss->wpos + samples_written) % hw->samples; + if (bytes_written - bytes_to_write) { + break; + } + } +} + +static int oss_run_out (HWVoiceOut *hw, int live) +{ + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + int err, decr; + struct audio_buf_info abinfo; + struct count_info cntinfo; + int bufsize; + + bufsize = hw->samples << hw->info.shift; + + if (oss->mmapped) { + int bytes, pos; + + err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); + if (err < 0) { + oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n"); + return 0; + } + + pos = hw->rpos << hw->info.shift; + bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize); + decr = audio_MIN (bytes >> hw->info.shift, live); + } + else { + err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); + if (err < 0) { + oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n"); + return 0; + } + + if (abinfo.bytes > bufsize) { + if (conf.debug) { + dolog ("warning: Invalid available size, size=%d bufsize=%d\n" + "please report your OS/audio hw to av1474@comtv.ru\n", + abinfo.bytes, bufsize); + } + abinfo.bytes = bufsize; + } + + if (abinfo.bytes < 0) { + if (conf.debug) { + dolog ("warning: Invalid available size, size=%d bufsize=%d\n", + abinfo.bytes, bufsize); + } + return 0; + } + + decr = audio_MIN (abinfo.bytes >> hw->info.shift, live); + if (!decr) { + return 0; + } + } + + decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending); + oss->pending += decr; + oss_write_pending (oss); + + return decr; +} + +static void oss_fini_out (HWVoiceOut *hw) +{ + int err; + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + + ldebug ("oss_fini\n"); + oss_anal_close (&oss->fd); + + if (oss->pcm_buf) { + if (oss->mmapped) { + err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); + if (err) { + oss_logerr (errno, "Failed to unmap buffer %p, size %d\n", + oss->pcm_buf, hw->samples << hw->info.shift); + } + } + else { + g_free (oss->pcm_buf); + } + oss->pcm_buf = NULL; + } +} + +static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + struct oss_params req, obt; + int endianness; + int err; + int fd; + audfmt_e effective_fmt; + struct audsettings obt_as; + + oss->fd = -1; + + req.fmt = aud_to_ossfmt (as->fmt, as->endianness); + req.freq = as->freq; + req.nchannels = as->nchannels; + req.fragsize = conf.fragsize; + req.nfrags = conf.nfrags; + + if (oss_open (0, &req, &obt, &fd)) { + return -1; + } + + err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); + if (err) { + oss_anal_close (&fd); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.nchannels; + obt_as.fmt = effective_fmt; + obt_as.endianness = endianness; + + audio_pcm_init_info (&hw->info, &obt_as); + oss->nfrags = obt.nfrags; + oss->fragsize = obt.fragsize; + + if (obt.nfrags * obt.fragsize & hw->info.align) { + dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n", + obt.nfrags * obt.fragsize, hw->info.align + 1); + } + + hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; + + oss->mmapped = 0; + if (conf.try_mmap) { + oss->pcm_buf = mmap ( + NULL, + hw->samples << hw->info.shift, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + 0 + ); + if (oss->pcm_buf == MAP_FAILED) { + oss_logerr (errno, "Failed to map %d bytes of DAC\n", + hw->samples << hw->info.shift); + } + else { + int err; + int trig = 0; + if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); + } + else { + trig = PCM_ENABLE_OUTPUT; + if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr ( + errno, + "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" + ); + } + else { + oss->mmapped = 1; + } + } + + if (!oss->mmapped) { + err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); + if (err) { + oss_logerr (errno, "Failed to unmap buffer %p size %d\n", + oss->pcm_buf, hw->samples << hw->info.shift); + } + } + } + } + + if (!oss->mmapped) { + oss->pcm_buf = audio_calloc ( + AUDIO_FUNC, + hw->samples, + 1 << hw->info.shift + ); + if (!oss->pcm_buf) { + dolog ( + "Could not allocate DAC buffer (%d samples, each %d bytes)\n", + hw->samples, + 1 << hw->info.shift + ); + oss_anal_close (&fd); + return -1; + } + } + + oss->fd = fd; + return 0; +} + +static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + int trig; + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + + switch (cmd) { + case VOICE_ENABLE: + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + ldebug ("enabling voice\n"); + if (poll_mode && oss_poll_out (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + + if (!oss->mmapped) { + return 0; + } + + audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples); + trig = PCM_ENABLE_OUTPUT; + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr ( + errno, + "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" + ); + return -1; + } + } + break; + + case VOICE_DISABLE: + if (hw->poll_mode) { + qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); + hw->poll_mode = 0; + } + + if (!oss->mmapped) { + return 0; + } + + ldebug ("disabling voice\n"); + trig = 0; + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); + return -1; + } + break; + } + return 0; +} + +static int oss_init_in (HWVoiceIn *hw, struct audsettings *as) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + struct oss_params req, obt; + int endianness; + int err; + int fd; + audfmt_e effective_fmt; + struct audsettings obt_as; + + oss->fd = -1; + + req.fmt = aud_to_ossfmt (as->fmt, as->endianness); + req.freq = as->freq; + req.nchannels = as->nchannels; + req.fragsize = conf.fragsize; + req.nfrags = conf.nfrags; + if (oss_open (1, &req, &obt, &fd)) { + return -1; + } + + err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); + if (err) { + oss_anal_close (&fd); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.nchannels; + obt_as.fmt = effective_fmt; + obt_as.endianness = endianness; + + audio_pcm_init_info (&hw->info, &obt_as); + oss->nfrags = obt.nfrags; + oss->fragsize = obt.fragsize; + + if (obt.nfrags * obt.fragsize & hw->info.align) { + dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n", + obt.nfrags * obt.fragsize, hw->info.align + 1); + } + + hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; + oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!oss->pcm_buf) { + dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", + hw->samples, 1 << hw->info.shift); + oss_anal_close (&fd); + return -1; + } + + oss->fd = fd; + return 0; +} + +static void oss_fini_in (HWVoiceIn *hw) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + + oss_anal_close (&oss->fd); + + g_free(oss->pcm_buf); + oss->pcm_buf = NULL; +} + +static int oss_run_in (HWVoiceIn *hw) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + int hwshift = hw->info.shift; + int i; + int live = audio_pcm_hw_get_live_in (hw); + int dead = hw->samples - live; + size_t read_samples = 0; + struct { + int add; + int len; + } bufs[2] = { + { .add = hw->wpos, .len = 0 }, + { .add = 0, .len = 0 } + }; + + if (!dead) { + return 0; + } + + if (hw->wpos + dead > hw->samples) { + bufs[0].len = (hw->samples - hw->wpos) << hwshift; + bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift; + } + else { + bufs[0].len = dead << hwshift; + } + + for (i = 0; i < 2; ++i) { + ssize_t nread; + + if (bufs[i].len) { + void *p = advance (oss->pcm_buf, bufs[i].add << hwshift); + nread = read (oss->fd, p, bufs[i].len); + + if (nread > 0) { + if (nread & hw->info.align) { + dolog ("warning: Misaligned read %zd (requested %d), " + "alignment %d\n", nread, bufs[i].add << hwshift, + hw->info.align + 1); + } + read_samples += nread >> hwshift; + hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift); + } + + if (bufs[i].len - nread) { + if (nread == -1) { + switch (errno) { + case EINTR: + case EAGAIN: + break; + default: + oss_logerr ( + errno, + "Failed to read %d bytes of audio (to %p)\n", + bufs[i].len, p + ); + break; + } + } + break; + } + } + } + + hw->wpos = (hw->wpos + read_samples) % hw->samples; + return read_samples; +} + +static int oss_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + + switch (cmd) { + case VOICE_ENABLE: + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + if (poll_mode && oss_poll_in (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + } + break; + + case VOICE_DISABLE: + if (hw->poll_mode) { + hw->poll_mode = 0; + qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); + } + break; + } + return 0; +} + +static void *oss_audio_init (void) +{ + if (access(conf.devpath_in, R_OK | W_OK) < 0 || + access(conf.devpath_out, R_OK | W_OK) < 0) { + return NULL; + } + return &conf; +} + +static void oss_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_option oss_options[] = { + { + .name = "FRAGSIZE", + .tag = AUD_OPT_INT, + .valp = &conf.fragsize, + .descr = "Fragment size in bytes" + }, + { + .name = "NFRAGS", + .tag = AUD_OPT_INT, + .valp = &conf.nfrags, + .descr = "Number of fragments" + }, + { + .name = "MMAP", + .tag = AUD_OPT_BOOL, + .valp = &conf.try_mmap, + .descr = "Try using memory mapped access" + }, + { + .name = "DAC_DEV", + .tag = AUD_OPT_STR, + .valp = &conf.devpath_out, + .descr = "Path to DAC device" + }, + { + .name = "ADC_DEV", + .tag = AUD_OPT_STR, + .valp = &conf.devpath_in, + .descr = "Path to ADC device" + }, + { + .name = "EXCLUSIVE", + .tag = AUD_OPT_BOOL, + .valp = &conf.exclusive, + .descr = "Open device in exclusive mode (vmix wont work)" + }, +#ifdef USE_DSP_POLICY + { + .name = "POLICY", + .tag = AUD_OPT_INT, + .valp = &conf.policy, + .descr = "Set the timing policy of the device, -1 to use fragment mode", + }, +#endif + { + .name = "DEBUG", + .tag = AUD_OPT_BOOL, + .valp = &conf.debug, + .descr = "Turn on some debugging messages" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops oss_pcm_ops = { + .init_out = oss_init_out, + .fini_out = oss_fini_out, + .run_out = oss_run_out, + .write = oss_write, + .ctl_out = oss_ctl_out, + + .init_in = oss_init_in, + .fini_in = oss_fini_in, + .run_in = oss_run_in, + .read = oss_read, + .ctl_in = oss_ctl_in +}; + +struct audio_driver oss_audio_driver = { + .name = "oss", + .descr = "OSS http://www.opensound.com", + .options = oss_options, + .init = oss_audio_init, + .fini = oss_audio_fini, + .pcm_ops = &oss_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (OSSVoiceOut), + .voice_size_in = sizeof (OSSVoiceIn) +}; diff --git a/audio/paaudio.c b/audio/paaudio.c new file mode 100644 index 0000000..90ff245 --- /dev/null +++ b/audio/paaudio.c @@ -0,0 +1,942 @@ +/* public domain */ +#include "qemu-common.h" +#include "audio.h" + +#include + +#define AUDIO_CAP "pulseaudio" +#include "audio_int.h" +#include "audio_pt_int.h" + +typedef struct { + HWVoiceOut hw; + int done; + int live; + int decr; + int rpos; + pa_stream *stream; + void *pcm_buf; + struct audio_pt pt; +} PAVoiceOut; + +typedef struct { + HWVoiceIn hw; + int done; + int dead; + int incr; + int wpos; + pa_stream *stream; + void *pcm_buf; + struct audio_pt pt; + const void *read_data; + size_t read_index, read_length; +} PAVoiceIn; + +typedef struct { + int samples; + char *server; + char *sink; + char *source; + pa_threaded_mainloop *mainloop; + pa_context *context; +} paaudio; + +static paaudio glob_paaudio = { + .samples = 4096, +}; + +static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err)); +} + +#ifndef PA_CONTEXT_IS_GOOD +static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) +{ + return + x == PA_CONTEXT_CONNECTING || + x == PA_CONTEXT_AUTHORIZING || + x == PA_CONTEXT_SETTING_NAME || + x == PA_CONTEXT_READY; +} +#endif + +#ifndef PA_STREAM_IS_GOOD +static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) +{ + return + x == PA_STREAM_CREATING || + x == PA_STREAM_READY; +} +#endif + +#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \ + do { \ + if (!(expression)) { \ + if (rerror) { \ + *(rerror) = pa_context_errno ((c)->context); \ + } \ + goto label; \ + } \ + } while (0); + +#define CHECK_DEAD_GOTO(c, stream, rerror, label) \ + do { \ + if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \ + !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \ + if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \ + ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \ + if (rerror) { \ + *(rerror) = pa_context_errno ((c)->context); \ + } \ + } else { \ + if (rerror) { \ + *(rerror) = PA_ERR_BADSTATE; \ + } \ + } \ + goto label; \ + } \ + } while (0); + +static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) +{ + paaudio *g = &glob_paaudio; + + pa_threaded_mainloop_lock (g->mainloop); + + CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); + + while (length > 0) { + size_t l; + + while (!p->read_data) { + int r; + + r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); + CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); + + if (!p->read_data) { + pa_threaded_mainloop_wait (g->mainloop); + CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); + } else { + p->read_index = 0; + } + } + + l = p->read_length < length ? p->read_length : length; + memcpy (data, (const uint8_t *) p->read_data+p->read_index, l); + + data = (uint8_t *) data + l; + length -= l; + + p->read_index += l; + p->read_length -= l; + + if (!p->read_length) { + int r; + + r = pa_stream_drop (p->stream); + p->read_data = NULL; + p->read_length = 0; + p->read_index = 0; + + CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); + } + } + + pa_threaded_mainloop_unlock (g->mainloop); + return 0; + +unlock_and_fail: + pa_threaded_mainloop_unlock (g->mainloop); + return -1; +} + +static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) +{ + paaudio *g = &glob_paaudio; + + pa_threaded_mainloop_lock (g->mainloop); + + CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); + + while (length > 0) { + size_t l; + int r; + + while (!(l = pa_stream_writable_size (p->stream))) { + pa_threaded_mainloop_wait (g->mainloop); + CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); + } + + CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); + + if (l > length) { + l = length; + } + + r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); + CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); + + data = (const uint8_t *) data + l; + length -= l; + } + + pa_threaded_mainloop_unlock (g->mainloop); + return 0; + +unlock_and_fail: + pa_threaded_mainloop_unlock (g->mainloop); + return -1; +} + +static void *qpa_thread_out (void *arg) +{ + PAVoiceOut *pa = arg; + HWVoiceOut *hw = &pa->hw; + + if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { + return NULL; + } + + for (;;) { + int decr, to_mix, rpos; + + for (;;) { + if (pa->done) { + goto exit; + } + + if (pa->live > 0) { + break; + } + + if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { + goto exit; + } + } + + decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2); + rpos = pa->rpos; + + if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { + return NULL; + } + + while (to_mix) { + int error; + int chunk = audio_MIN (to_mix, hw->samples - rpos); + struct st_sample *src = hw->mix_buf + rpos; + + hw->clip (pa->pcm_buf, src, chunk); + + if (qpa_simple_write (pa, pa->pcm_buf, + chunk << hw->info.shift, &error) < 0) { + qpa_logerr (error, "pa_simple_write failed\n"); + return NULL; + } + + rpos = (rpos + chunk) % hw->samples; + to_mix -= chunk; + } + + if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { + return NULL; + } + + pa->rpos = rpos; + pa->live -= decr; + pa->decr += decr; + } + + exit: + audio_pt_unlock (&pa->pt, AUDIO_FUNC); + return NULL; +} + +static int qpa_run_out (HWVoiceOut *hw, int live) +{ + int decr; + PAVoiceOut *pa = (PAVoiceOut *) hw; + + if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { + return 0; + } + + decr = audio_MIN (live, pa->decr); + pa->decr -= decr; + pa->live = live - decr; + hw->rpos = pa->rpos; + if (pa->live > 0) { + audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); + } + else { + audio_pt_unlock (&pa->pt, AUDIO_FUNC); + } + return decr; +} + +static int qpa_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +/* capture */ +static void *qpa_thread_in (void *arg) +{ + PAVoiceIn *pa = arg; + HWVoiceIn *hw = &pa->hw; + + if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { + return NULL; + } + + for (;;) { + int incr, to_grab, wpos; + + for (;;) { + if (pa->done) { + goto exit; + } + + if (pa->dead > 0) { + break; + } + + if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { + goto exit; + } + } + + incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2); + wpos = pa->wpos; + + if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { + return NULL; + } + + while (to_grab) { + int error; + int chunk = audio_MIN (to_grab, hw->samples - wpos); + void *buf = advance (pa->pcm_buf, wpos); + + if (qpa_simple_read (pa, buf, + chunk << hw->info.shift, &error) < 0) { + qpa_logerr (error, "pa_simple_read failed\n"); + return NULL; + } + + hw->conv (hw->conv_buf + wpos, buf, chunk); + wpos = (wpos + chunk) % hw->samples; + to_grab -= chunk; + } + + if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { + return NULL; + } + + pa->wpos = wpos; + pa->dead -= incr; + pa->incr += incr; + } + + exit: + audio_pt_unlock (&pa->pt, AUDIO_FUNC); + return NULL; +} + +static int qpa_run_in (HWVoiceIn *hw) +{ + int live, incr, dead; + PAVoiceIn *pa = (PAVoiceIn *) hw; + + if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { + return 0; + } + + live = audio_pcm_hw_get_live_in (hw); + dead = hw->samples - live; + incr = audio_MIN (dead, pa->incr); + pa->incr -= incr; + pa->dead = dead - incr; + hw->wpos = pa->wpos; + if (pa->dead > 0) { + audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); + } + else { + audio_pt_unlock (&pa->pt, AUDIO_FUNC); + } + return incr; +} + +static int qpa_read (SWVoiceIn *sw, void *buf, int len) +{ + return audio_pcm_sw_read (sw, buf, len); +} + +static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) +{ + int format; + + switch (afmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + format = PA_SAMPLE_U8; + break; + case AUD_FMT_S16: + case AUD_FMT_U16: + format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; + break; + case AUD_FMT_S32: + case AUD_FMT_U32: + format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; + break; + default: + dolog ("Internal logic error: Bad audio format %d\n", afmt); + format = PA_SAMPLE_U8; + break; + } + return format; +} + +static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) +{ + switch (fmt) { + case PA_SAMPLE_U8: + return AUD_FMT_U8; + case PA_SAMPLE_S16BE: + *endianness = 1; + return AUD_FMT_S16; + case PA_SAMPLE_S16LE: + *endianness = 0; + return AUD_FMT_S16; + case PA_SAMPLE_S32BE: + *endianness = 1; + return AUD_FMT_S32; + case PA_SAMPLE_S32LE: + *endianness = 0; + return AUD_FMT_S32; + default: + dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); + return AUD_FMT_U8; + } +} + +static void context_state_cb (pa_context *c, void *userdata) +{ + paaudio *g = &glob_paaudio; + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal (g->mainloop, 0); + break; + + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + } +} + +static void stream_state_cb (pa_stream *s, void * userdata) +{ + paaudio *g = &glob_paaudio; + + switch (pa_stream_get_state (s)) { + + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + pa_threaded_mainloop_signal (g->mainloop, 0); + break; + + case PA_STREAM_UNCONNECTED: + case PA_STREAM_CREATING: + break; + } +} + +static void stream_request_cb (pa_stream *s, size_t length, void *userdata) +{ + paaudio *g = &glob_paaudio; + + pa_threaded_mainloop_signal (g->mainloop, 0); +} + +static pa_stream *qpa_simple_new ( + const char *server, + const char *name, + pa_stream_direction_t dir, + const char *dev, + const char *stream_name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_buffer_attr *attr, + int *rerror) +{ + paaudio *g = &glob_paaudio; + int r; + pa_stream *stream; + + pa_threaded_mainloop_lock (g->mainloop); + + stream = pa_stream_new (g->context, name, ss, map); + if (!stream) { + goto fail; + } + + pa_stream_set_state_callback (stream, stream_state_cb, g); + pa_stream_set_read_callback (stream, stream_request_cb, g); + pa_stream_set_write_callback (stream, stream_request_cb, g); + + if (dir == PA_STREAM_PLAYBACK) { + r = pa_stream_connect_playback (stream, dev, attr, + PA_STREAM_INTERPOLATE_TIMING +#ifdef PA_STREAM_ADJUST_LATENCY + |PA_STREAM_ADJUST_LATENCY +#endif + |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); + } else { + r = pa_stream_connect_record (stream, dev, attr, + PA_STREAM_INTERPOLATE_TIMING +#ifdef PA_STREAM_ADJUST_LATENCY + |PA_STREAM_ADJUST_LATENCY +#endif + |PA_STREAM_AUTO_TIMING_UPDATE); + } + + if (r < 0) { + goto fail; + } + + pa_threaded_mainloop_unlock (g->mainloop); + + return stream; + +fail: + pa_threaded_mainloop_unlock (g->mainloop); + + if (stream) { + pa_stream_unref (stream); + } + + *rerror = pa_context_errno (g->context); + + return NULL; +} + +static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + int error; + static pa_sample_spec ss; + static pa_buffer_attr ba; + struct audsettings obt_as = *as; + PAVoiceOut *pa = (PAVoiceOut *) hw; + + ss.format = audfmt_to_pa (as->fmt, as->endianness); + ss.channels = as->nchannels; + ss.rate = as->freq; + + /* + * qemu audio tick runs at 100 Hz (by default), so processing + * data chunks worth 10 ms of sound should be a good fit. + */ + ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); + ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); + ba.maxlength = -1; + ba.prebuf = -1; + + obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); + + pa->stream = qpa_simple_new ( + glob_paaudio.server, + "qemu", + PA_STREAM_PLAYBACK, + glob_paaudio.sink, + "pcm.playback", + &ss, + NULL, /* channel map */ + &ba, /* buffering attributes */ + &error + ); + if (!pa->stream) { + qpa_logerr (error, "pa_simple_new for playback failed\n"); + goto fail1; + } + + audio_pcm_init_info (&hw->info, &obt_as); + hw->samples = glob_paaudio.samples; + pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + pa->rpos = hw->rpos; + if (!pa->pcm_buf) { + dolog ("Could not allocate buffer (%d bytes)\n", + hw->samples << hw->info.shift); + goto fail2; + } + + if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { + goto fail3; + } + + return 0; + + fail3: + g_free (pa->pcm_buf); + pa->pcm_buf = NULL; + fail2: + if (pa->stream) { + pa_stream_unref (pa->stream); + pa->stream = NULL; + } + fail1: + return -1; +} + +static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) +{ + int error; + static pa_sample_spec ss; + struct audsettings obt_as = *as; + PAVoiceIn *pa = (PAVoiceIn *) hw; + + ss.format = audfmt_to_pa (as->fmt, as->endianness); + ss.channels = as->nchannels; + ss.rate = as->freq; + + obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); + + pa->stream = qpa_simple_new ( + glob_paaudio.server, + "qemu", + PA_STREAM_RECORD, + glob_paaudio.source, + "pcm.capture", + &ss, + NULL, /* channel map */ + NULL, /* buffering attributes */ + &error + ); + if (!pa->stream) { + qpa_logerr (error, "pa_simple_new for capture failed\n"); + goto fail1; + } + + audio_pcm_init_info (&hw->info, &obt_as); + hw->samples = glob_paaudio.samples; + pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + pa->wpos = hw->wpos; + if (!pa->pcm_buf) { + dolog ("Could not allocate buffer (%d bytes)\n", + hw->samples << hw->info.shift); + goto fail2; + } + + if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { + goto fail3; + } + + return 0; + + fail3: + g_free (pa->pcm_buf); + pa->pcm_buf = NULL; + fail2: + if (pa->stream) { + pa_stream_unref (pa->stream); + pa->stream = NULL; + } + fail1: + return -1; +} + +static void qpa_fini_out (HWVoiceOut *hw) +{ + void *ret; + PAVoiceOut *pa = (PAVoiceOut *) hw; + + audio_pt_lock (&pa->pt, AUDIO_FUNC); + pa->done = 1; + audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); + audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); + + if (pa->stream) { + pa_stream_unref (pa->stream); + pa->stream = NULL; + } + + audio_pt_fini (&pa->pt, AUDIO_FUNC); + g_free (pa->pcm_buf); + pa->pcm_buf = NULL; +} + +static void qpa_fini_in (HWVoiceIn *hw) +{ + void *ret; + PAVoiceIn *pa = (PAVoiceIn *) hw; + + audio_pt_lock (&pa->pt, AUDIO_FUNC); + pa->done = 1; + audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); + audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); + + if (pa->stream) { + pa_stream_unref (pa->stream); + pa->stream = NULL; + } + + audio_pt_fini (&pa->pt, AUDIO_FUNC); + g_free (pa->pcm_buf); + pa->pcm_buf = NULL; +} + +static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + PAVoiceOut *pa = (PAVoiceOut *) hw; + pa_operation *op; + pa_cvolume v; + paaudio *g = &glob_paaudio; + +#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */ + pa_cvolume_init (&v); /* function is present in 0.9.13+ */ +#endif + + switch (cmd) { + case VOICE_VOLUME: + { + SWVoiceOut *sw; + va_list ap; + + va_start (ap, cmd); + sw = va_arg (ap, SWVoiceOut *); + va_end (ap); + + v.channels = 2; + v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; + v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; + + pa_threaded_mainloop_lock (g->mainloop); + + op = pa_context_set_sink_input_volume (g->context, + pa_stream_get_index (pa->stream), + &v, NULL, NULL); + if (!op) + qpa_logerr (pa_context_errno (g->context), + "set_sink_input_volume() failed\n"); + else + pa_operation_unref (op); + + op = pa_context_set_sink_input_mute (g->context, + pa_stream_get_index (pa->stream), + sw->vol.mute, NULL, NULL); + if (!op) { + qpa_logerr (pa_context_errno (g->context), + "set_sink_input_mute() failed\n"); + } else { + pa_operation_unref (op); + } + + pa_threaded_mainloop_unlock (g->mainloop); + } + } + return 0; +} + +static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + PAVoiceIn *pa = (PAVoiceIn *) hw; + pa_operation *op; + pa_cvolume v; + paaudio *g = &glob_paaudio; + +#ifdef PA_CHECK_VERSION + pa_cvolume_init (&v); +#endif + + switch (cmd) { + case VOICE_VOLUME: + { + SWVoiceIn *sw; + va_list ap; + + va_start (ap, cmd); + sw = va_arg (ap, SWVoiceIn *); + va_end (ap); + + v.channels = 2; + v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; + v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; + + pa_threaded_mainloop_lock (g->mainloop); + + /* FIXME: use the upcoming "set_source_output_{volume,mute}" */ + op = pa_context_set_source_volume_by_index (g->context, + pa_stream_get_device_index (pa->stream), + &v, NULL, NULL); + if (!op) { + qpa_logerr (pa_context_errno (g->context), + "set_source_volume() failed\n"); + } else { + pa_operation_unref(op); + } + + op = pa_context_set_source_mute_by_index (g->context, + pa_stream_get_index (pa->stream), + sw->vol.mute, NULL, NULL); + if (!op) { + qpa_logerr (pa_context_errno (g->context), + "set_source_mute() failed\n"); + } else { + pa_operation_unref (op); + } + + pa_threaded_mainloop_unlock (g->mainloop); + } + } + return 0; +} + +/* common */ +static void *qpa_audio_init (void) +{ + paaudio *g = &glob_paaudio; + + g->mainloop = pa_threaded_mainloop_new (); + if (!g->mainloop) { + goto fail; + } + + g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server); + if (!g->context) { + goto fail; + } + + pa_context_set_state_callback (g->context, context_state_cb, g); + + if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) { + qpa_logerr (pa_context_errno (g->context), + "pa_context_connect() failed\n"); + goto fail; + } + + pa_threaded_mainloop_lock (g->mainloop); + + if (pa_threaded_mainloop_start (g->mainloop) < 0) { + goto unlock_and_fail; + } + + for (;;) { + pa_context_state_t state; + + state = pa_context_get_state (g->context); + + if (state == PA_CONTEXT_READY) { + break; + } + + if (!PA_CONTEXT_IS_GOOD (state)) { + qpa_logerr (pa_context_errno (g->context), + "Wrong context state\n"); + goto unlock_and_fail; + } + + /* Wait until the context is ready */ + pa_threaded_mainloop_wait (g->mainloop); + } + + pa_threaded_mainloop_unlock (g->mainloop); + + return &glob_paaudio; + +unlock_and_fail: + pa_threaded_mainloop_unlock (g->mainloop); +fail: + AUD_log (AUDIO_CAP, "Failed to initialize PA context"); + return NULL; +} + +static void qpa_audio_fini (void *opaque) +{ + paaudio *g = opaque; + + if (g->mainloop) { + pa_threaded_mainloop_stop (g->mainloop); + } + + if (g->context) { + pa_context_disconnect (g->context); + pa_context_unref (g->context); + g->context = NULL; + } + + if (g->mainloop) { + pa_threaded_mainloop_free (g->mainloop); + } + + g->mainloop = NULL; +} + +struct audio_option qpa_options[] = { + { + .name = "SAMPLES", + .tag = AUD_OPT_INT, + .valp = &glob_paaudio.samples, + .descr = "buffer size in samples" + }, + { + .name = "SERVER", + .tag = AUD_OPT_STR, + .valp = &glob_paaudio.server, + .descr = "server address" + }, + { + .name = "SINK", + .tag = AUD_OPT_STR, + .valp = &glob_paaudio.sink, + .descr = "sink device name" + }, + { + .name = "SOURCE", + .tag = AUD_OPT_STR, + .valp = &glob_paaudio.source, + .descr = "source device name" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops qpa_pcm_ops = { + .init_out = qpa_init_out, + .fini_out = qpa_fini_out, + .run_out = qpa_run_out, + .write = qpa_write, + .ctl_out = qpa_ctl_out, + + .init_in = qpa_init_in, + .fini_in = qpa_fini_in, + .run_in = qpa_run_in, + .read = qpa_read, + .ctl_in = qpa_ctl_in +}; + +struct audio_driver pa_audio_driver = { + .name = "pa", + .descr = "http://www.pulseaudio.org/", + .options = qpa_options, + .init = qpa_audio_init, + .fini = qpa_audio_fini, + .pcm_ops = &qpa_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (PAVoiceOut), + .voice_size_in = sizeof (PAVoiceIn), + .ctl_caps = VOICE_VOLUME_CAP +}; diff --git a/audio/rate_template.h b/audio/rate_template.h new file mode 100644 index 0000000..bd4b1c7 --- /dev/null +++ b/audio/rate_template.h @@ -0,0 +1,111 @@ +/* + * QEMU Mixing engine + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * Copyright (c) 1998 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Processed signed long samples from ibuf to obuf. + * Return number of samples processed. + */ +void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, + int *isamp, int *osamp) +{ + struct rate *rate = opaque; + struct st_sample *istart, *iend; + struct st_sample *ostart, *oend; + struct st_sample ilast, icur, out; +#ifdef FLOAT_MIXENG + mixeng_real t; +#else + int64_t t; +#endif + + ilast = rate->ilast; + + istart = ibuf; + iend = ibuf + *isamp; + + ostart = obuf; + oend = obuf + *osamp; + + if (rate->opos_inc == (1ULL + UINT_MAX)) { + int i, n = *isamp > *osamp ? *osamp : *isamp; + for (i = 0; i < n; i++) { + OP (obuf[i].l, ibuf[i].l); + OP (obuf[i].r, ibuf[i].r); + } + *isamp = n; + *osamp = n; + return; + } + + while (obuf < oend) { + + /* Safety catch to make sure we have input samples. */ + if (ibuf >= iend) { + break; + } + + /* read as many input samples so that ipos > opos */ + + while (rate->ipos <= (rate->opos >> 32)) { + ilast = *ibuf++; + rate->ipos++; + /* See if we finished the input buffer yet */ + if (ibuf >= iend) { + goto the_end; + } + } + + icur = *ibuf; + + /* interpolate */ +#ifdef FLOAT_MIXENG +#ifdef RECIPROCAL + t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX); +#else + t = (rate->opos & UINT_MAX) / (mixeng_real) UINT_MAX; +#endif + out.l = (ilast.l * (1.0 - t)) + icur.l * t; + out.r = (ilast.r * (1.0 - t)) + icur.r * t; +#else + t = rate->opos & 0xffffffff; + out.l = (ilast.l * ((int64_t) UINT_MAX - t) + icur.l * t) >> 32; + out.r = (ilast.r * ((int64_t) UINT_MAX - t) + icur.r * t) >> 32; +#endif + + /* output sample & increment position */ + OP (obuf->l, out.l); + OP (obuf->r, out.r); + obuf += 1; + rate->opos += rate->opos_inc; + } + +the_end: + *isamp = ibuf - istart; + *osamp = obuf - ostart; + rate->ilast = ilast; +} + +#undef NAME +#undef OP diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c new file mode 100644 index 0000000..d24daa5 --- /dev/null +++ b/audio/sdlaudio.c @@ -0,0 +1,458 @@ +/* + * QEMU SDL audio driver + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include "qemu-common.h" +#include "audio.h" + +#ifndef _WIN32 +#ifdef __sun__ +#define _POSIX_PTHREAD_SEMANTICS 1 +#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) +#include +#endif +#endif + +#define AUDIO_CAP "sdl" +#include "audio_int.h" + +typedef struct SDLVoiceOut { + HWVoiceOut hw; + int live; + int rpos; + int decr; +} SDLVoiceOut; + +static struct { + int nb_samples; +} conf = { + .nb_samples = 1024 +}; + +static struct SDLAudioState { + int exit; + SDL_mutex *mutex; + SDL_sem *sem; + int initialized; +} glob_sdl; +typedef struct SDLAudioState SDLAudioState; + +static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); +} + +static int sdl_lock (SDLAudioState *s, const char *forfn) +{ + if (SDL_LockMutex (s->mutex)) { + sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); + return -1; + } + return 0; +} + +static int sdl_unlock (SDLAudioState *s, const char *forfn) +{ + if (SDL_UnlockMutex (s->mutex)) { + sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); + return -1; + } + return 0; +} + +static int sdl_post (SDLAudioState *s, const char *forfn) +{ + if (SDL_SemPost (s->sem)) { + sdl_logerr ("SDL_SemPost for %s failed\n", forfn); + return -1; + } + return 0; +} + +static int sdl_wait (SDLAudioState *s, const char *forfn) +{ + if (SDL_SemWait (s->sem)) { + sdl_logerr ("SDL_SemWait for %s failed\n", forfn); + return -1; + } + return 0; +} + +static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) +{ + if (sdl_unlock (s, forfn)) { + return -1; + } + + return sdl_post (s, forfn); +} + +static int aud_to_sdlfmt (audfmt_e fmt) +{ + switch (fmt) { + case AUD_FMT_S8: + return AUDIO_S8; + + case AUD_FMT_U8: + return AUDIO_U8; + + case AUD_FMT_S16: + return AUDIO_S16LSB; + + case AUD_FMT_U16: + return AUDIO_U16LSB; + + default: + dolog ("Internal logic error: Bad audio format %d\n", fmt); +#ifdef DEBUG_AUDIO + abort (); +#endif + return AUDIO_U8; + } +} + +static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) +{ + switch (sdlfmt) { + case AUDIO_S8: + *endianness = 0; + *fmt = AUD_FMT_S8; + break; + + case AUDIO_U8: + *endianness = 0; + *fmt = AUD_FMT_U8; + break; + + case AUDIO_S16LSB: + *endianness = 0; + *fmt = AUD_FMT_S16; + break; + + case AUDIO_U16LSB: + *endianness = 0; + *fmt = AUD_FMT_U16; + break; + + case AUDIO_S16MSB: + *endianness = 1; + *fmt = AUD_FMT_S16; + break; + + case AUDIO_U16MSB: + *endianness = 1; + *fmt = AUD_FMT_U16; + break; + + default: + dolog ("Unrecognized SDL audio format %d\n", sdlfmt); + return -1; + } + + return 0; +} + +static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) +{ + int status; +#ifndef _WIN32 + int err; + sigset_t new, old; + + /* Make sure potential threads created by SDL don't hog signals. */ + err = sigfillset (&new); + if (err) { + dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); + return -1; + } + err = pthread_sigmask (SIG_BLOCK, &new, &old); + if (err) { + dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); + return -1; + } +#endif + + status = SDL_OpenAudio (req, obt); + if (status) { + sdl_logerr ("SDL_OpenAudio failed\n"); + } + +#ifndef _WIN32 + err = pthread_sigmask (SIG_SETMASK, &old, NULL); + if (err) { + dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", + strerror (errno)); + /* We have failed to restore original signal mask, all bets are off, + so exit the process */ + exit (EXIT_FAILURE); + } +#endif + return status; +} + +static void sdl_close (SDLAudioState *s) +{ + if (s->initialized) { + sdl_lock (s, "sdl_close"); + s->exit = 1; + sdl_unlock_and_post (s, "sdl_close"); + SDL_PauseAudio (1); + SDL_CloseAudio (); + s->initialized = 0; + } +} + +static void sdl_callback (void *opaque, Uint8 *buf, int len) +{ + SDLVoiceOut *sdl = opaque; + SDLAudioState *s = &glob_sdl; + HWVoiceOut *hw = &sdl->hw; + int samples = len >> hw->info.shift; + + if (s->exit) { + return; + } + + while (samples) { + int to_mix, decr; + + /* dolog ("in callback samples=%d\n", samples); */ + sdl_wait (s, "sdl_callback"); + if (s->exit) { + return; + } + + if (sdl_lock (s, "sdl_callback")) { + return; + } + + if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { + dolog ("sdl->live=%d hw->samples=%d\n", + sdl->live, hw->samples); + return; + } + + if (!sdl->live) { + goto again; + } + + /* dolog ("in callback live=%d\n", live); */ + to_mix = audio_MIN (samples, sdl->live); + decr = to_mix; + while (to_mix) { + int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); + struct st_sample *src = hw->mix_buf + hw->rpos; + + /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ + hw->clip (buf, src, chunk); + sdl->rpos = (sdl->rpos + chunk) % hw->samples; + to_mix -= chunk; + buf += chunk << hw->info.shift; + } + samples -= decr; + sdl->live -= decr; + sdl->decr += decr; + + again: + if (sdl_unlock (s, "sdl_callback")) { + return; + } + } + /* dolog ("done len=%d\n", len); */ +} + +static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int sdl_run_out (HWVoiceOut *hw, int live) +{ + int decr; + SDLVoiceOut *sdl = (SDLVoiceOut *) hw; + SDLAudioState *s = &glob_sdl; + + if (sdl_lock (s, "sdl_run_out")) { + return 0; + } + + if (sdl->decr > live) { + ldebug ("sdl->decr %d live %d sdl->live %d\n", + sdl->decr, + live, + sdl->live); + } + + decr = audio_MIN (sdl->decr, live); + sdl->decr -= decr; + + sdl->live = live - decr; + hw->rpos = sdl->rpos; + + if (sdl->live > 0) { + sdl_unlock_and_post (s, "sdl_run_out"); + } + else { + sdl_unlock (s, "sdl_run_out"); + } + return decr; +} + +static void sdl_fini_out (HWVoiceOut *hw) +{ + (void) hw; + + sdl_close (&glob_sdl); +} + +static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + SDLVoiceOut *sdl = (SDLVoiceOut *) hw; + SDLAudioState *s = &glob_sdl; + SDL_AudioSpec req, obt; + int endianness; + int err; + audfmt_e effective_fmt; + struct audsettings obt_as; + + req.freq = as->freq; + req.format = aud_to_sdlfmt (as->fmt); + req.channels = as->nchannels; + req.samples = conf.nb_samples; + req.callback = sdl_callback; + req.userdata = sdl; + + if (sdl_open (&req, &obt)) { + return -1; + } + + err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); + if (err) { + sdl_close (s); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.channels; + obt_as.fmt = effective_fmt; + obt_as.endianness = endianness; + + audio_pcm_init_info (&hw->info, &obt_as); + hw->samples = obt.samples; + + s->initialized = 1; + s->exit = 0; + SDL_PauseAudio (0); + return 0; +} + +static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + (void) hw; + + switch (cmd) { + case VOICE_ENABLE: + SDL_PauseAudio (0); + break; + + case VOICE_DISABLE: + SDL_PauseAudio (1); + break; + } + return 0; +} + +static void *sdl_audio_init (void) +{ + SDLAudioState *s = &glob_sdl; + + if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { + sdl_logerr ("SDL failed to initialize audio subsystem\n"); + return NULL; + } + + s->mutex = SDL_CreateMutex (); + if (!s->mutex) { + sdl_logerr ("Failed to create SDL mutex\n"); + SDL_QuitSubSystem (SDL_INIT_AUDIO); + return NULL; + } + + s->sem = SDL_CreateSemaphore (0); + if (!s->sem) { + sdl_logerr ("Failed to create SDL semaphore\n"); + SDL_DestroyMutex (s->mutex); + SDL_QuitSubSystem (SDL_INIT_AUDIO); + return NULL; + } + + return s; +} + +static void sdl_audio_fini (void *opaque) +{ + SDLAudioState *s = opaque; + sdl_close (s); + SDL_DestroySemaphore (s->sem); + SDL_DestroyMutex (s->mutex); + SDL_QuitSubSystem (SDL_INIT_AUDIO); +} + +static struct audio_option sdl_options[] = { + { + .name = "SAMPLES", + .tag = AUD_OPT_INT, + .valp = &conf.nb_samples, + .descr = "Size of SDL buffer in samples" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops sdl_pcm_ops = { + .init_out = sdl_init_out, + .fini_out = sdl_fini_out, + .run_out = sdl_run_out, + .write = sdl_write_out, + .ctl_out = sdl_ctl_out, +}; + +struct audio_driver sdl_audio_driver = { + .name = "sdl", + .descr = "SDL http://www.libsdl.org", + .options = sdl_options, + .init = sdl_audio_init, + .fini = sdl_audio_fini, + .pcm_ops = &sdl_pcm_ops, + .can_be_default = 1, + .max_voices_out = 1, + .max_voices_in = 0, + .voice_size_out = sizeof (SDLVoiceOut), + .voice_size_in = 0 +}; diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c new file mode 100644 index 0000000..7b79bed --- /dev/null +++ b/audio/spiceaudio.c @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * maintained by Gerd Hoffmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "hw/hw.h" +#include "qemu/timer.h" +#include "ui/qemu-spice.h" + +#define AUDIO_CAP "spice" +#include "audio.h" +#include "audio_int.h" + +#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 +#define LINE_OUT_SAMPLES (480 * 4) +#else +#define LINE_OUT_SAMPLES (256 * 4) +#endif + +#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 +#define LINE_IN_SAMPLES (480 * 4) +#else +#define LINE_IN_SAMPLES (256 * 4) +#endif + +typedef struct SpiceRateCtl { + int64_t start_ticks; + int64_t bytes_sent; +} SpiceRateCtl; + +typedef struct SpiceVoiceOut { + HWVoiceOut hw; + SpicePlaybackInstance sin; + SpiceRateCtl rate; + int active; + uint32_t *frame; + uint32_t *fpos; + uint32_t fsize; +} SpiceVoiceOut; + +typedef struct SpiceVoiceIn { + HWVoiceIn hw; + SpiceRecordInstance sin; + SpiceRateCtl rate; + int active; + uint32_t samples[LINE_IN_SAMPLES]; +} SpiceVoiceIn; + +static const SpicePlaybackInterface playback_sif = { + .base.type = SPICE_INTERFACE_PLAYBACK, + .base.description = "playback", + .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR, + .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR, +}; + +static const SpiceRecordInterface record_sif = { + .base.type = SPICE_INTERFACE_RECORD, + .base.description = "record", + .base.major_version = SPICE_INTERFACE_RECORD_MAJOR, + .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, +}; + +static void *spice_audio_init (void) +{ + if (!using_spice) { + return NULL; + } + return &spice_audio_init; +} + +static void spice_audio_fini (void *opaque) +{ + /* nothing */ +} + +static void rate_start (SpiceRateCtl *rate) +{ + memset (rate, 0, sizeof (*rate)); + rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) +{ + int64_t now; + int64_t ticks; + int64_t bytes; + int64_t samples; + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ticks = now - rate->start_ticks; + bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ()); + samples = (bytes - rate->bytes_sent) >> info->shift; + if (samples < 0 || samples > 65536) { + error_report("Resetting rate control (%" PRId64 " samples)", samples); + rate_start (rate); + samples = 0; + } + rate->bytes_sent += samples << info->shift; + return samples; +} + +/* playback */ + +static int line_out_init (HWVoiceOut *hw, struct audsettings *as) +{ + SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); + struct audsettings settings; + +#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 + settings.freq = spice_server_get_best_playback_rate(NULL); +#else + settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ; +#endif + settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN; + settings.fmt = AUD_FMT_S16; + settings.endianness = AUDIO_HOST_ENDIANNESS; + + audio_pcm_init_info (&hw->info, &settings); + hw->samples = LINE_OUT_SAMPLES; + out->active = 0; + + out->sin.base.sif = &playback_sif.base; + qemu_spice_add_interface (&out->sin.base); +#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 + spice_server_set_playback_rate(&out->sin, settings.freq); +#endif + return 0; +} + +static void line_out_fini (HWVoiceOut *hw) +{ + SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); + + spice_server_remove_interface (&out->sin.base); +} + +static int line_out_run (HWVoiceOut *hw, int live) +{ + SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); + int rpos, decr; + int samples; + + if (!live) { + return 0; + } + + decr = rate_get_samples (&hw->info, &out->rate); + decr = audio_MIN (live, decr); + + samples = decr; + rpos = hw->rpos; + while (samples) { + int left_till_end_samples = hw->samples - rpos; + int len = audio_MIN (samples, left_till_end_samples); + + if (!out->frame) { + spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize); + out->fpos = out->frame; + } + if (out->frame) { + len = audio_MIN (len, out->fsize); + hw->clip (out->fpos, hw->mix_buf + rpos, len); + out->fsize -= len; + out->fpos += len; + if (out->fsize == 0) { + spice_server_playback_put_samples (&out->sin, out->frame); + out->frame = out->fpos = NULL; + } + } + rpos = (rpos + len) % hw->samples; + samples -= len; + } + hw->rpos = rpos; + return decr; +} + +static int line_out_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) +{ + SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); + + switch (cmd) { + case VOICE_ENABLE: + if (out->active) { + break; + } + out->active = 1; + rate_start (&out->rate); + spice_server_playback_start (&out->sin); + break; + case VOICE_DISABLE: + if (!out->active) { + break; + } + out->active = 0; + if (out->frame) { + memset (out->fpos, 0, out->fsize << 2); + spice_server_playback_put_samples (&out->sin, out->frame); + out->frame = out->fpos = NULL; + } + spice_server_playback_stop (&out->sin); + break; + case VOICE_VOLUME: + { +#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) + SWVoiceOut *sw; + va_list ap; + uint16_t vol[2]; + + va_start (ap, cmd); + sw = va_arg (ap, SWVoiceOut *); + va_end (ap); + + vol[0] = sw->vol.l / ((1ULL << 16) + 1); + vol[1] = sw->vol.r / ((1ULL << 16) + 1); + spice_server_playback_set_volume (&out->sin, 2, vol); + spice_server_playback_set_mute (&out->sin, sw->vol.mute); +#endif + break; + } + } + + return 0; +} + +/* record */ + +static int line_in_init (HWVoiceIn *hw, struct audsettings *as) +{ + SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); + struct audsettings settings; + +#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 + settings.freq = spice_server_get_best_record_rate(NULL); +#else + settings.freq = SPICE_INTERFACE_RECORD_FREQ; +#endif + settings.nchannels = SPICE_INTERFACE_RECORD_CHAN; + settings.fmt = AUD_FMT_S16; + settings.endianness = AUDIO_HOST_ENDIANNESS; + + audio_pcm_init_info (&hw->info, &settings); + hw->samples = LINE_IN_SAMPLES; + in->active = 0; + + in->sin.base.sif = &record_sif.base; + qemu_spice_add_interface (&in->sin.base); +#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 + spice_server_set_record_rate(&in->sin, settings.freq); +#endif + return 0; +} + +static void line_in_fini (HWVoiceIn *hw) +{ + SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); + + spice_server_remove_interface (&in->sin.base); +} + +static int line_in_run (HWVoiceIn *hw) +{ + SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); + int num_samples; + int ready; + int len[2]; + uint64_t delta_samp; + const uint32_t *samples; + + if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) { + return 0; + } + + delta_samp = rate_get_samples (&hw->info, &in->rate); + num_samples = audio_MIN (num_samples, delta_samp); + + ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples); + samples = in->samples; + if (ready == 0) { + static const uint32_t silence[LINE_IN_SAMPLES]; + samples = silence; + ready = LINE_IN_SAMPLES; + } + + num_samples = audio_MIN (ready, num_samples); + + if (hw->wpos + num_samples > hw->samples) { + len[0] = hw->samples - hw->wpos; + len[1] = num_samples - len[0]; + } else { + len[0] = num_samples; + len[1] = 0; + } + + hw->conv (hw->conv_buf + hw->wpos, samples, len[0]); + + if (len[1]) { + hw->conv (hw->conv_buf, samples + len[0], len[1]); + } + + hw->wpos = (hw->wpos + num_samples) % hw->samples; + + return num_samples; +} + +static int line_in_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) +{ + SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); + + switch (cmd) { + case VOICE_ENABLE: + if (in->active) { + break; + } + in->active = 1; + rate_start (&in->rate); + spice_server_record_start (&in->sin); + break; + case VOICE_DISABLE: + if (!in->active) { + break; + } + in->active = 0; + spice_server_record_stop (&in->sin); + break; + case VOICE_VOLUME: + { +#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) + SWVoiceIn *sw; + va_list ap; + uint16_t vol[2]; + + va_start (ap, cmd); + sw = va_arg (ap, SWVoiceIn *); + va_end (ap); + + vol[0] = sw->vol.l / ((1ULL << 16) + 1); + vol[1] = sw->vol.r / ((1ULL << 16) + 1); + spice_server_record_set_volume (&in->sin, 2, vol); + spice_server_record_set_mute (&in->sin, sw->vol.mute); +#endif + break; + } + } + + return 0; +} + +static struct audio_option audio_options[] = { + { /* end of list */ }, +}; + +static struct audio_pcm_ops audio_callbacks = { + .init_out = line_out_init, + .fini_out = line_out_fini, + .run_out = line_out_run, + .write = line_out_write, + .ctl_out = line_out_ctl, + + .init_in = line_in_init, + .fini_in = line_in_fini, + .run_in = line_in_run, + .read = line_in_read, + .ctl_in = line_in_ctl, +}; + +struct audio_driver spice_audio_driver = { + .name = "spice", + .descr = "spice audio driver", + .options = audio_options, + .init = spice_audio_init, + .fini = spice_audio_fini, + .pcm_ops = &audio_callbacks, + .max_voices_out = 1, + .max_voices_in = 1, + .voice_size_out = sizeof (SpiceVoiceOut), + .voice_size_in = sizeof (SpiceVoiceIn), +#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) + .ctl_caps = VOICE_VOLUME_CAP +#endif +}; + +void qemu_spice_audio_init (void) +{ + spice_audio_driver.can_be_default = 1; +} diff --git a/audio/wavaudio.c b/audio/wavaudio.c new file mode 100644 index 0000000..6846a1a --- /dev/null +++ b/audio/wavaudio.c @@ -0,0 +1,288 @@ +/* + * QEMU WAV audio driver + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "qemu/timer.h" +#include "audio.h" + +#define AUDIO_CAP "wav" +#include "audio_int.h" + +typedef struct WAVVoiceOut { + HWVoiceOut hw; + FILE *f; + int64_t old_ticks; + void *pcm_buf; + int total_samples; +} WAVVoiceOut; + +static struct { + struct audsettings settings; + const char *wav_path; +} conf = { + .settings.freq = 44100, + .settings.nchannels = 2, + .settings.fmt = AUD_FMT_S16, + .wav_path = "qemu.wav" +}; + +static int wav_run_out (HWVoiceOut *hw, int live) +{ + WAVVoiceOut *wav = (WAVVoiceOut *) hw; + int rpos, decr, samples; + uint8_t *dst; + struct st_sample *src; + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t ticks = now - wav->old_ticks; + int64_t bytes = + muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); + + if (bytes > INT_MAX) { + samples = INT_MAX >> hw->info.shift; + } + else { + samples = bytes >> hw->info.shift; + } + + wav->old_ticks = now; + decr = audio_MIN (live, samples); + samples = decr; + rpos = hw->rpos; + while (samples) { + int left_till_end_samples = hw->samples - rpos; + int convert_samples = audio_MIN (samples, left_till_end_samples); + + src = hw->mix_buf + rpos; + dst = advance (wav->pcm_buf, rpos << hw->info.shift); + + hw->clip (dst, src, convert_samples); + if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) { + dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n", + convert_samples << hw->info.shift, strerror (errno)); + } + + rpos = (rpos + convert_samples) % hw->samples; + samples -= convert_samples; + wav->total_samples += convert_samples; + } + + hw->rpos = rpos; + return decr; +} + +static int wav_write_out (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +/* VICE code: Store number as little endian. */ +static void le_store (uint8_t *buf, uint32_t val, int len) +{ + int i; + for (i = 0; i < len; i++) { + buf[i] = (uint8_t) (val & 0xff); + val >>= 8; + } +} + +static int wav_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + WAVVoiceOut *wav = (WAVVoiceOut *) hw; + int bits16 = 0, stereo = 0; + uint8_t hdr[] = { + 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, + 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, + 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 + }; + struct audsettings wav_as = conf.settings; + + (void) as; + + stereo = wav_as.nchannels == 2; + switch (wav_as.fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + bits16 = 0; + break; + + case AUD_FMT_S16: + case AUD_FMT_U16: + bits16 = 1; + break; + + case AUD_FMT_S32: + case AUD_FMT_U32: + dolog ("WAVE files can not handle 32bit formats\n"); + return -1; + } + + hdr[34] = bits16 ? 0x10 : 0x08; + + wav_as.endianness = 0; + audio_pcm_init_info (&hw->info, &wav_as); + + hw->samples = 1024; + wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!wav->pcm_buf) { + dolog ("Could not allocate buffer (%d bytes)\n", + hw->samples << hw->info.shift); + return -1; + } + + le_store (hdr + 22, hw->info.nchannels, 2); + le_store (hdr + 24, hw->info.freq, 4); + le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); + le_store (hdr + 32, 1 << (bits16 + stereo), 2); + + wav->f = fopen (conf.wav_path, "wb"); + if (!wav->f) { + dolog ("Failed to open wave file `%s'\nReason: %s\n", + conf.wav_path, strerror (errno)); + g_free (wav->pcm_buf); + wav->pcm_buf = NULL; + return -1; + } + + if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { + dolog ("wav_init_out: failed to write header\nReason: %s\n", + strerror(errno)); + return -1; + } + return 0; +} + +static void wav_fini_out (HWVoiceOut *hw) +{ + WAVVoiceOut *wav = (WAVVoiceOut *) hw; + uint8_t rlen[4]; + uint8_t dlen[4]; + uint32_t datalen = wav->total_samples << hw->info.shift; + uint32_t rifflen = datalen + 36; + + if (!wav->f) { + return; + } + + le_store (rlen, rifflen, 4); + le_store (dlen, datalen, 4); + + if (fseek (wav->f, 4, SEEK_SET)) { + dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n", + strerror(errno)); + goto doclose; + } + if (fwrite (rlen, 4, 1, wav->f) != 1) { + dolog ("wav_fini_out: failed to write rlen\nReason: %s\n", + strerror (errno)); + goto doclose; + } + if (fseek (wav->f, 32, SEEK_CUR)) { + dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n", + strerror (errno)); + goto doclose; + } + if (fwrite (dlen, 4, 1, wav->f) != 1) { + dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n", + strerror (errno)); + goto doclose; + } + + doclose: + if (fclose (wav->f)) { + dolog ("wav_fini_out: fclose %p failed\nReason: %s\n", + wav->f, strerror (errno)); + } + wav->f = NULL; + + g_free (wav->pcm_buf); + wav->pcm_buf = NULL; +} + +static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +static void *wav_audio_init (void) +{ + return &conf; +} + +static void wav_audio_fini (void *opaque) +{ + (void) opaque; + ldebug ("wav_fini"); +} + +static struct audio_option wav_options[] = { + { + .name = "FREQUENCY", + .tag = AUD_OPT_INT, + .valp = &conf.settings.freq, + .descr = "Frequency" + }, + { + .name = "FORMAT", + .tag = AUD_OPT_FMT, + .valp = &conf.settings.fmt, + .descr = "Format" + }, + { + .name = "DAC_FIXED_CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.settings.nchannels, + .descr = "Number of channels (1 - mono, 2 - stereo)" + }, + { + .name = "PATH", + .tag = AUD_OPT_STR, + .valp = &conf.wav_path, + .descr = "Path to wave file" + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops wav_pcm_ops = { + .init_out = wav_init_out, + .fini_out = wav_fini_out, + .run_out = wav_run_out, + .write = wav_write_out, + .ctl_out = wav_ctl_out, +}; + +struct audio_driver wav_audio_driver = { + .name = "wav", + .descr = "WAV renderer http://wikipedia.org/wiki/WAV", + .options = wav_options, + .init = wav_audio_init, + .fini = wav_audio_fini, + .pcm_ops = &wav_pcm_ops, + .can_be_default = 0, + .max_voices_out = 1, + .max_voices_in = 0, + .voice_size_out = sizeof (WAVVoiceOut), + .voice_size_in = 0 +}; diff --git a/audio/wavcapture.c b/audio/wavcapture.c new file mode 100644 index 0000000..6f6d792 --- /dev/null +++ b/audio/wavcapture.c @@ -0,0 +1,193 @@ +#include "hw/hw.h" +#include "monitor/monitor.h" +#include "audio.h" + +typedef struct { + FILE *f; + int bytes; + char *path; + int freq; + int bits; + int nchannels; + CaptureVoiceOut *cap; +} WAVState; + +/* VICE code: Store number as little endian. */ +static void le_store (uint8_t *buf, uint32_t val, int len) +{ + int i; + for (i = 0; i < len; i++) { + buf[i] = (uint8_t) (val & 0xff); + val >>= 8; + } +} + +static void wav_notify (void *opaque, audcnotification_e cmd) +{ + (void) opaque; + (void) cmd; +} + +static void wav_destroy (void *opaque) +{ + WAVState *wav = opaque; + uint8_t rlen[4]; + uint8_t dlen[4]; + uint32_t datalen = wav->bytes; + uint32_t rifflen = datalen + 36; + Monitor *mon = cur_mon; + + if (wav->f) { + le_store (rlen, rifflen, 4); + le_store (dlen, datalen, 4); + + if (fseek (wav->f, 4, SEEK_SET)) { + monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n", + strerror (errno)); + goto doclose; + } + if (fwrite (rlen, 4, 1, wav->f) != 1) { + monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n", + strerror (errno)); + goto doclose; + } + if (fseek (wav->f, 32, SEEK_CUR)) { + monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n", + strerror (errno)); + goto doclose; + } + if (fwrite (dlen, 1, 4, wav->f) != 4) { + monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n", + strerror (errno)); + goto doclose; + } + doclose: + if (fclose (wav->f)) { + error_report("wav_destroy: fclose failed: %s", strerror(errno)); + } + } + + g_free (wav->path); +} + +static void wav_capture (void *opaque, void *buf, int size) +{ + WAVState *wav = opaque; + + if (fwrite (buf, size, 1, wav->f) != 1) { + monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s", + strerror (errno)); + } + wav->bytes += size; +} + +static void wav_capture_destroy (void *opaque) +{ + WAVState *wav = opaque; + + AUD_del_capture (wav->cap, wav); +} + +static void wav_capture_info (void *opaque) +{ + WAVState *wav = opaque; + char *path = wav->path; + + monitor_printf (cur_mon, "Capturing audio(%d,%d,%d) to %s: %d bytes\n", + wav->freq, wav->bits, wav->nchannels, + path ? path : "", wav->bytes); +} + +static struct capture_ops wav_capture_ops = { + .destroy = wav_capture_destroy, + .info = wav_capture_info +}; + +int wav_start_capture (CaptureState *s, const char *path, int freq, + int bits, int nchannels) +{ + Monitor *mon = cur_mon; + WAVState *wav; + uint8_t hdr[] = { + 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, + 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, + 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 + }; + struct audsettings as; + struct audio_capture_ops ops; + int stereo, bits16, shift; + CaptureVoiceOut *cap; + + if (bits != 8 && bits != 16) { + monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits); + return -1; + } + + if (nchannels != 1 && nchannels != 2) { + monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n", + nchannels); + return -1; + } + + stereo = nchannels == 2; + bits16 = bits == 16; + + as.freq = freq; + as.nchannels = 1 << stereo; + as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; + as.endianness = 0; + + ops.notify = wav_notify; + ops.capture = wav_capture; + ops.destroy = wav_destroy; + + wav = g_malloc0 (sizeof (*wav)); + + shift = bits16 + stereo; + hdr[34] = bits16 ? 0x10 : 0x08; + + le_store (hdr + 22, as.nchannels, 2); + le_store (hdr + 24, freq, 4); + le_store (hdr + 28, freq << shift, 4); + le_store (hdr + 32, 1 << shift, 2); + + wav->f = fopen (path, "wb"); + if (!wav->f) { + monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n", + path, strerror (errno)); + g_free (wav); + return -1; + } + + wav->path = g_strdup (path); + wav->bits = bits; + wav->nchannels = nchannels; + wav->freq = freq; + + if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { + monitor_printf (mon, "Failed to write header\nReason: %s\n", + strerror (errno)); + goto error_free; + } + + cap = AUD_add_capture (&as, &ops, wav); + if (!cap) { + monitor_printf (mon, "Failed to add audio capture\n"); + goto error_free; + } + + wav->cap = cap; + s->opaque = wav; + s->ops = wav_capture_ops; + return 0; + +error_free: + g_free (wav->path); + if (fclose (wav->f)) { + monitor_printf (mon, "Failed to close wave file\nReason: %s\n", + strerror (errno)); + } + g_free (wav); + return -1; +} diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c new file mode 100644 index 0000000..8dbd145 --- /dev/null +++ b/audio/winwaveaudio.c @@ -0,0 +1,717 @@ +/* public domain */ + +#include "qemu-common.h" +#include "sysemu/sysemu.h" +#include "audio.h" + +#define AUDIO_CAP "winwave" +#include "audio_int.h" + +#include +#include + +#include "audio_win_int.h" + +static struct { + int dac_headers; + int dac_samples; + int adc_headers; + int adc_samples; +} conf = { + .dac_headers = 4, + .dac_samples = 1024, + .adc_headers = 4, + .adc_samples = 1024 +}; + +typedef struct { + HWVoiceOut hw; + HWAVEOUT hwo; + WAVEHDR *hdrs; + HANDLE event; + void *pcm_buf; + int avail; + int pending; + int curhdr; + int paused; + CRITICAL_SECTION crit_sect; +} WaveVoiceOut; + +typedef struct { + HWVoiceIn hw; + HWAVEIN hwi; + WAVEHDR *hdrs; + HANDLE event; + void *pcm_buf; + int curhdr; + int paused; + int rpos; + int avail; + CRITICAL_SECTION crit_sect; +} WaveVoiceIn; + +static void winwave_log_mmresult (MMRESULT mr) +{ + const char *str = "BUG"; + + switch (mr) { + case MMSYSERR_NOERROR: + str = "Success"; + break; + + case MMSYSERR_INVALHANDLE: + str = "Specified device handle is invalid"; + break; + + case MMSYSERR_BADDEVICEID: + str = "Specified device id is out of range"; + break; + + case MMSYSERR_NODRIVER: + str = "No device driver is present"; + break; + + case MMSYSERR_NOMEM: + str = "Unable to allocate or lock memory"; + break; + + case WAVERR_SYNC: + str = "Device is synchronous but waveOutOpen was called " + "without using the WINWAVE_ALLOWSYNC flag"; + break; + + case WAVERR_UNPREPARED: + str = "The data block pointed to by the pwh parameter " + "hasn't been prepared"; + break; + + case WAVERR_STILLPLAYING: + str = "There are still buffers in the queue"; + break; + + default: + dolog ("Reason: Unknown (MMRESULT %#x)\n", mr); + return; + } + + dolog ("Reason: %s\n", str); +} + +static void GCC_FMT_ATTR (2, 3) winwave_logerr ( + MMRESULT mr, + const char *fmt, + ... + ) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (NULL, " failed\n"); + winwave_log_mmresult (mr); +} + +static void winwave_anal_close_out (WaveVoiceOut *wave) +{ + MMRESULT mr; + + mr = waveOutClose (wave->hwo); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutClose"); + } + wave->hwo = NULL; +} + +static void CALLBACK winwave_callback_out ( + HWAVEOUT hwo, + UINT msg, + DWORD_PTR dwInstance, + DWORD_PTR dwParam1, + DWORD_PTR dwParam2 + ) +{ + WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance; + + switch (msg) { + case WOM_DONE: + { + WAVEHDR *h = (WAVEHDR *) dwParam1; + if (!h->dwUser) { + h->dwUser = 1; + EnterCriticalSection (&wave->crit_sect); + { + wave->avail += conf.dac_samples; + } + LeaveCriticalSection (&wave->crit_sect); + if (wave->hw.poll_mode) { + if (!SetEvent (wave->event)) { + dolog ("DAC SetEvent failed %lx\n", GetLastError ()); + } + } + } + } + break; + + case WOM_CLOSE: + case WOM_OPEN: + break; + + default: + dolog ("unknown wave out callback msg %x\n", msg); + } +} + +static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + int i; + int err; + MMRESULT mr; + WAVEFORMATEX wfx; + WaveVoiceOut *wave; + + wave = (WaveVoiceOut *) hw; + + InitializeCriticalSection (&wave->crit_sect); + + err = waveformat_from_audio_settings (&wfx, as); + if (err) { + goto err0; + } + + mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx, + (DWORD_PTR) winwave_callback_out, + (DWORD_PTR) wave, CALLBACK_FUNCTION); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutOpen"); + goto err1; + } + + wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers, + sizeof (*wave->hdrs)); + if (!wave->hdrs) { + goto err2; + } + + audio_pcm_init_info (&hw->info, as); + hw->samples = conf.dac_samples * conf.dac_headers; + wave->avail = hw->samples; + + wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples, + conf.dac_headers << hw->info.shift); + if (!wave->pcm_buf) { + goto err3; + } + + for (i = 0; i < conf.dac_headers; ++i) { + WAVEHDR *h = &wave->hdrs[i]; + + h->dwUser = 0; + h->dwBufferLength = conf.dac_samples << hw->info.shift; + h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength); + h->dwFlags = 0; + + mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h)); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutPrepareHeader(%d)", i); + goto err4; + } + } + + return 0; + + err4: + g_free (wave->pcm_buf); + err3: + g_free (wave->hdrs); + err2: + winwave_anal_close_out (wave); + err1: + err0: + return -1; +} + +static int winwave_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int winwave_run_out (HWVoiceOut *hw, int live) +{ + WaveVoiceOut *wave = (WaveVoiceOut *) hw; + int decr; + int doreset; + + EnterCriticalSection (&wave->crit_sect); + { + decr = audio_MIN (live, wave->avail); + decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending); + wave->pending += decr; + wave->avail -= decr; + } + LeaveCriticalSection (&wave->crit_sect); + + doreset = hw->poll_mode && (wave->pending >= conf.dac_samples); + if (doreset && !ResetEvent (wave->event)) { + dolog ("DAC ResetEvent failed %lx\n", GetLastError ()); + } + + while (wave->pending >= conf.dac_samples) { + MMRESULT mr; + WAVEHDR *h = &wave->hdrs[wave->curhdr]; + + h->dwUser = 0; + mr = waveOutWrite (wave->hwo, h, sizeof (*h)); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr); + break; + } + + wave->pending -= conf.dac_samples; + wave->curhdr = (wave->curhdr + 1) % conf.dac_headers; + } + + return decr; +} + +static void winwave_poll (void *opaque) +{ + (void) opaque; + audio_run ("winwave_poll"); +} + +static void winwave_fini_out (HWVoiceOut *hw) +{ + int i; + MMRESULT mr; + WaveVoiceOut *wave = (WaveVoiceOut *) hw; + + mr = waveOutReset (wave->hwo); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutReset"); + } + + for (i = 0; i < conf.dac_headers; ++i) { + mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i], + sizeof (wave->hdrs[i])); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i); + } + } + + winwave_anal_close_out (wave); + + if (wave->event) { + qemu_del_wait_object (wave->event, winwave_poll, wave); + if (!CloseHandle (wave->event)) { + dolog ("DAC CloseHandle failed %lx\n", GetLastError ()); + } + wave->event = NULL; + } + + g_free (wave->pcm_buf); + wave->pcm_buf = NULL; + + g_free (wave->hdrs); + wave->hdrs = NULL; +} + +static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + MMRESULT mr; + WaveVoiceOut *wave = (WaveVoiceOut *) hw; + + switch (cmd) { + case VOICE_ENABLE: + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + if (poll_mode && !wave->event) { + wave->event = CreateEvent (NULL, TRUE, TRUE, NULL); + if (!wave->event) { + dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n", + GetLastError ()); + } + } + + if (wave->event) { + int ret; + + ret = qemu_add_wait_object (wave->event, winwave_poll, wave); + hw->poll_mode = (ret == 0); + } + else { + hw->poll_mode = 0; + } + wave->paused = 0; + } + return 0; + + case VOICE_DISABLE: + if (!wave->paused) { + mr = waveOutReset (wave->hwo); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutReset"); + } + else { + wave->paused = 1; + } + } + if (wave->event) { + qemu_del_wait_object (wave->event, winwave_poll, wave); + } + return 0; + } + return -1; +} + +static void winwave_anal_close_in (WaveVoiceIn *wave) +{ + MMRESULT mr; + + mr = waveInClose (wave->hwi); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveInClose"); + } + wave->hwi = NULL; +} + +static void CALLBACK winwave_callback_in ( + HWAVEIN *hwi, + UINT msg, + DWORD_PTR dwInstance, + DWORD_PTR dwParam1, + DWORD_PTR dwParam2 + ) +{ + WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance; + + switch (msg) { + case WIM_DATA: + { + WAVEHDR *h = (WAVEHDR *) dwParam1; + if (!h->dwUser) { + h->dwUser = 1; + EnterCriticalSection (&wave->crit_sect); + { + wave->avail += conf.adc_samples; + } + LeaveCriticalSection (&wave->crit_sect); + if (wave->hw.poll_mode) { + if (!SetEvent (wave->event)) { + dolog ("ADC SetEvent failed %lx\n", GetLastError ()); + } + } + } + } + break; + + case WIM_CLOSE: + case WIM_OPEN: + break; + + default: + dolog ("unknown wave in callback msg %x\n", msg); + } +} + +static void winwave_add_buffers (WaveVoiceIn *wave, int samples) +{ + int doreset; + + doreset = wave->hw.poll_mode && (samples >= conf.adc_samples); + if (doreset && !ResetEvent (wave->event)) { + dolog ("ADC ResetEvent failed %lx\n", GetLastError ()); + } + + while (samples >= conf.adc_samples) { + MMRESULT mr; + WAVEHDR *h = &wave->hdrs[wave->curhdr]; + + h->dwUser = 0; + mr = waveInAddBuffer (wave->hwi, h, sizeof (*h)); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr); + } + wave->curhdr = (wave->curhdr + 1) % conf.adc_headers; + samples -= conf.adc_samples; + } +} + +static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as) +{ + int i; + int err; + MMRESULT mr; + WAVEFORMATEX wfx; + WaveVoiceIn *wave; + + wave = (WaveVoiceIn *) hw; + + InitializeCriticalSection (&wave->crit_sect); + + err = waveformat_from_audio_settings (&wfx, as); + if (err) { + goto err0; + } + + mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx, + (DWORD_PTR) winwave_callback_in, + (DWORD_PTR) wave, CALLBACK_FUNCTION); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveInOpen"); + goto err1; + } + + wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers, + sizeof (*wave->hdrs)); + if (!wave->hdrs) { + goto err2; + } + + audio_pcm_init_info (&hw->info, as); + hw->samples = conf.adc_samples * conf.adc_headers; + wave->avail = 0; + + wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples, + conf.adc_headers << hw->info.shift); + if (!wave->pcm_buf) { + goto err3; + } + + for (i = 0; i < conf.adc_headers; ++i) { + WAVEHDR *h = &wave->hdrs[i]; + + h->dwUser = 0; + h->dwBufferLength = conf.adc_samples << hw->info.shift; + h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength); + h->dwFlags = 0; + + mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h)); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveInPrepareHeader(%d)", i); + goto err4; + } + } + + wave->paused = 1; + winwave_add_buffers (wave, hw->samples); + return 0; + + err4: + g_free (wave->pcm_buf); + err3: + g_free (wave->hdrs); + err2: + winwave_anal_close_in (wave); + err1: + err0: + return -1; +} + +static void winwave_fini_in (HWVoiceIn *hw) +{ + int i; + MMRESULT mr; + WaveVoiceIn *wave = (WaveVoiceIn *) hw; + + mr = waveInReset (wave->hwi); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveInReset"); + } + + for (i = 0; i < conf.adc_headers; ++i) { + mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i], + sizeof (wave->hdrs[i])); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveInUnprepareHeader(%d)", i); + } + } + + winwave_anal_close_in (wave); + + if (wave->event) { + qemu_del_wait_object (wave->event, winwave_poll, wave); + if (!CloseHandle (wave->event)) { + dolog ("ADC CloseHandle failed %lx\n", GetLastError ()); + } + wave->event = NULL; + } + + g_free (wave->pcm_buf); + wave->pcm_buf = NULL; + + g_free (wave->hdrs); + wave->hdrs = NULL; +} + +static int winwave_run_in (HWVoiceIn *hw) +{ + WaveVoiceIn *wave = (WaveVoiceIn *) hw; + int live = audio_pcm_hw_get_live_in (hw); + int dead = hw->samples - live; + int decr, ret; + + if (!dead) { + return 0; + } + + EnterCriticalSection (&wave->crit_sect); + { + decr = audio_MIN (dead, wave->avail); + wave->avail -= decr; + } + LeaveCriticalSection (&wave->crit_sect); + + ret = decr; + while (decr) { + int left = hw->samples - hw->wpos; + int conv = audio_MIN (left, decr); + hw->conv (hw->conv_buf + hw->wpos, + advance (wave->pcm_buf, wave->rpos << hw->info.shift), + conv); + + wave->rpos = (wave->rpos + conv) % hw->samples; + hw->wpos = (hw->wpos + conv) % hw->samples; + decr -= conv; + } + + winwave_add_buffers (wave, ret); + return ret; +} + +static int winwave_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + MMRESULT mr; + WaveVoiceIn *wave = (WaveVoiceIn *) hw; + + switch (cmd) { + case VOICE_ENABLE: + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + if (poll_mode && !wave->event) { + wave->event = CreateEvent (NULL, TRUE, TRUE, NULL); + if (!wave->event) { + dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n", + GetLastError ()); + } + } + + if (wave->event) { + int ret; + + ret = qemu_add_wait_object (wave->event, winwave_poll, wave); + hw->poll_mode = (ret == 0); + } + else { + hw->poll_mode = 0; + } + if (wave->paused) { + mr = waveInStart (wave->hwi); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveInStart"); + } + wave->paused = 0; + } + } + return 0; + + case VOICE_DISABLE: + if (!wave->paused) { + mr = waveInStop (wave->hwi); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveInStop"); + } + else { + wave->paused = 1; + } + } + if (wave->event) { + qemu_del_wait_object (wave->event, winwave_poll, wave); + } + return 0; + } + return 0; +} + +static void *winwave_audio_init (void) +{ + return &conf; +} + +static void winwave_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_option winwave_options[] = { + { + .name = "DAC_HEADERS", + .tag = AUD_OPT_INT, + .valp = &conf.dac_headers, + .descr = "DAC number of headers", + }, + { + .name = "DAC_SAMPLES", + .tag = AUD_OPT_INT, + .valp = &conf.dac_samples, + .descr = "DAC number of samples per header", + }, + { + .name = "ADC_HEADERS", + .tag = AUD_OPT_INT, + .valp = &conf.adc_headers, + .descr = "ADC number of headers", + }, + { + .name = "ADC_SAMPLES", + .tag = AUD_OPT_INT, + .valp = &conf.adc_samples, + .descr = "ADC number of samples per header", + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops winwave_pcm_ops = { + .init_out = winwave_init_out, + .fini_out = winwave_fini_out, + .run_out = winwave_run_out, + .write = winwave_write, + .ctl_out = winwave_ctl_out, + .init_in = winwave_init_in, + .fini_in = winwave_fini_in, + .run_in = winwave_run_in, + .read = winwave_read, + .ctl_in = winwave_ctl_in +}; + +struct audio_driver winwave_audio_driver = { + .name = "winwave", + .descr = "Windows Waveform Audio http://msdn.microsoft.com", + .options = winwave_options, + .init = winwave_audio_init, + .fini = winwave_audio_fini, + .pcm_ops = &winwave_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (WaveVoiceOut), + .voice_size_in = sizeof (WaveVoiceIn) +}; diff --git a/backends/Makefile.objs b/backends/Makefile.objs new file mode 100644 index 0000000..31a3a89 --- /dev/null +++ b/backends/Makefile.objs @@ -0,0 +1,11 @@ +common-obj-y += rng.o rng-egd.o +common-obj-$(CONFIG_POSIX) += rng-random.o + +common-obj-y += msmouse.o testdev.o +common-obj-$(CONFIG_BRLAPI) += baum.o +baum.o-cflags := $(SDL_CFLAGS) + +common-obj-$(CONFIG_TPM) += tpm.o + +common-obj-y += hostmem.o hostmem-ram.o +common-obj-$(CONFIG_LINUX) += hostmem-file.o diff --git a/backends/baum.c b/backends/baum.c new file mode 100644 index 0000000..a69aaff --- /dev/null +++ b/backends/baum.c @@ -0,0 +1,635 @@ +/* + * QEMU Baum Braille Device + * + * Copyright (c) 2008 Samuel Thibault + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "sysemu/char.h" +#include "qemu/timer.h" +#include "hw/usb.h" +#include +#include +#include +#ifdef CONFIG_SDL +#include +#endif + +#if 0 +#define DPRINTF(fmt, ...) \ + printf(fmt, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +#define ESC 0x1B + +#define BAUM_REQ_DisplayData 0x01 +#define BAUM_REQ_GetVersionNumber 0x05 +#define BAUM_REQ_GetKeys 0x08 +#define BAUM_REQ_SetMode 0x12 +#define BAUM_REQ_SetProtocol 0x15 +#define BAUM_REQ_GetDeviceIdentity 0x84 +#define BAUM_REQ_GetSerialNumber 0x8A + +#define BAUM_RSP_CellCount 0x01 +#define BAUM_RSP_VersionNumber 0x05 +#define BAUM_RSP_ModeSetting 0x11 +#define BAUM_RSP_CommunicationChannel 0x16 +#define BAUM_RSP_PowerdownSignal 0x17 +#define BAUM_RSP_HorizontalSensors 0x20 +#define BAUM_RSP_VerticalSensors 0x21 +#define BAUM_RSP_RoutingKeys 0x22 +#define BAUM_RSP_Switches 0x23 +#define BAUM_RSP_TopKeys 0x24 +#define BAUM_RSP_HorizontalSensor 0x25 +#define BAUM_RSP_VerticalSensor 0x26 +#define BAUM_RSP_RoutingKey 0x27 +#define BAUM_RSP_FrontKeys6 0x28 +#define BAUM_RSP_BackKeys6 0x29 +#define BAUM_RSP_CommandKeys 0x2B +#define BAUM_RSP_FrontKeys10 0x2C +#define BAUM_RSP_BackKeys10 0x2D +#define BAUM_RSP_EntryKeys 0x33 +#define BAUM_RSP_JoyStick 0x34 +#define BAUM_RSP_ErrorCode 0x40 +#define BAUM_RSP_InfoBlock 0x42 +#define BAUM_RSP_DeviceIdentity 0x84 +#define BAUM_RSP_SerialNumber 0x8A +#define BAUM_RSP_BluetoothName 0x8C + +#define BAUM_TL1 0x01 +#define BAUM_TL2 0x02 +#define BAUM_TL3 0x04 +#define BAUM_TR1 0x08 +#define BAUM_TR2 0x10 +#define BAUM_TR3 0x20 + +#define BUF_SIZE 256 + +typedef struct { + CharDriverState *chr; + + brlapi_handle_t *brlapi; + int brlapi_fd; + unsigned int x, y; + + uint8_t in_buf[BUF_SIZE]; + uint8_t in_buf_used; + uint8_t out_buf[BUF_SIZE]; + uint8_t out_buf_used, out_buf_ptr; + + QEMUTimer *cellCount_timer; +} BaumDriverState; + +/* Let's assume NABCC by default */ +static const uint8_t nabcc_translation[256] = { + [0] = ' ', +#ifndef BRLAPI_DOTS +#define BRLAPI_DOTS(d1,d2,d3,d4,d5,d6,d7,d8) \ + ((d1?BRLAPI_DOT1:0)|\ + (d2?BRLAPI_DOT2:0)|\ + (d3?BRLAPI_DOT3:0)|\ + (d4?BRLAPI_DOT4:0)|\ + (d5?BRLAPI_DOT5:0)|\ + (d6?BRLAPI_DOT6:0)|\ + (d7?BRLAPI_DOT7:0)|\ + (d8?BRLAPI_DOT8:0)) +#endif + [BRLAPI_DOTS(1,0,0,0,0,0,0,0)] = 'a', + [BRLAPI_DOTS(1,1,0,0,0,0,0,0)] = 'b', + [BRLAPI_DOTS(1,0,0,1,0,0,0,0)] = 'c', + [BRLAPI_DOTS(1,0,0,1,1,0,0,0)] = 'd', + [BRLAPI_DOTS(1,0,0,0,1,0,0,0)] = 'e', + [BRLAPI_DOTS(1,1,0,1,0,0,0,0)] = 'f', + [BRLAPI_DOTS(1,1,0,1,1,0,0,0)] = 'g', + [BRLAPI_DOTS(1,1,0,0,1,0,0,0)] = 'h', + [BRLAPI_DOTS(0,1,0,1,0,0,0,0)] = 'i', + [BRLAPI_DOTS(0,1,0,1,1,0,0,0)] = 'j', + [BRLAPI_DOTS(1,0,1,0,0,0,0,0)] = 'k', + [BRLAPI_DOTS(1,1,1,0,0,0,0,0)] = 'l', + [BRLAPI_DOTS(1,0,1,1,0,0,0,0)] = 'm', + [BRLAPI_DOTS(1,0,1,1,1,0,0,0)] = 'n', + [BRLAPI_DOTS(1,0,1,0,1,0,0,0)] = 'o', + [BRLAPI_DOTS(1,1,1,1,0,0,0,0)] = 'p', + [BRLAPI_DOTS(1,1,1,1,1,0,0,0)] = 'q', + [BRLAPI_DOTS(1,1,1,0,1,0,0,0)] = 'r', + [BRLAPI_DOTS(0,1,1,1,0,0,0,0)] = 's', + [BRLAPI_DOTS(0,1,1,1,1,0,0,0)] = 't', + [BRLAPI_DOTS(1,0,1,0,0,1,0,0)] = 'u', + [BRLAPI_DOTS(1,1,1,0,0,1,0,0)] = 'v', + [BRLAPI_DOTS(0,1,0,1,1,1,0,0)] = 'w', + [BRLAPI_DOTS(1,0,1,1,0,1,0,0)] = 'x', + [BRLAPI_DOTS(1,0,1,1,1,1,0,0)] = 'y', + [BRLAPI_DOTS(1,0,1,0,1,1,0,0)] = 'z', + + [BRLAPI_DOTS(1,0,0,0,0,0,1,0)] = 'A', + [BRLAPI_DOTS(1,1,0,0,0,0,1,0)] = 'B', + [BRLAPI_DOTS(1,0,0,1,0,0,1,0)] = 'C', + [BRLAPI_DOTS(1,0,0,1,1,0,1,0)] = 'D', + [BRLAPI_DOTS(1,0,0,0,1,0,1,0)] = 'E', + [BRLAPI_DOTS(1,1,0,1,0,0,1,0)] = 'F', + [BRLAPI_DOTS(1,1,0,1,1,0,1,0)] = 'G', + [BRLAPI_DOTS(1,1,0,0,1,0,1,0)] = 'H', + [BRLAPI_DOTS(0,1,0,1,0,0,1,0)] = 'I', + [BRLAPI_DOTS(0,1,0,1,1,0,1,0)] = 'J', + [BRLAPI_DOTS(1,0,1,0,0,0,1,0)] = 'K', + [BRLAPI_DOTS(1,1,1,0,0,0,1,0)] = 'L', + [BRLAPI_DOTS(1,0,1,1,0,0,1,0)] = 'M', + [BRLAPI_DOTS(1,0,1,1,1,0,1,0)] = 'N', + [BRLAPI_DOTS(1,0,1,0,1,0,1,0)] = 'O', + [BRLAPI_DOTS(1,1,1,1,0,0,1,0)] = 'P', + [BRLAPI_DOTS(1,1,1,1,1,0,1,0)] = 'Q', + [BRLAPI_DOTS(1,1,1,0,1,0,1,0)] = 'R', + [BRLAPI_DOTS(0,1,1,1,0,0,1,0)] = 'S', + [BRLAPI_DOTS(0,1,1,1,1,0,1,0)] = 'T', + [BRLAPI_DOTS(1,0,1,0,0,1,1,0)] = 'U', + [BRLAPI_DOTS(1,1,1,0,0,1,1,0)] = 'V', + [BRLAPI_DOTS(0,1,0,1,1,1,1,0)] = 'W', + [BRLAPI_DOTS(1,0,1,1,0,1,1,0)] = 'X', + [BRLAPI_DOTS(1,0,1,1,1,1,1,0)] = 'Y', + [BRLAPI_DOTS(1,0,1,0,1,1,1,0)] = 'Z', + + [BRLAPI_DOTS(0,0,1,0,1,1,0,0)] = '0', + [BRLAPI_DOTS(0,1,0,0,0,0,0,0)] = '1', + [BRLAPI_DOTS(0,1,1,0,0,0,0,0)] = '2', + [BRLAPI_DOTS(0,1,0,0,1,0,0,0)] = '3', + [BRLAPI_DOTS(0,1,0,0,1,1,0,0)] = '4', + [BRLAPI_DOTS(0,1,0,0,0,1,0,0)] = '5', + [BRLAPI_DOTS(0,1,1,0,1,0,0,0)] = '6', + [BRLAPI_DOTS(0,1,1,0,1,1,0,0)] = '7', + [BRLAPI_DOTS(0,1,1,0,0,1,0,0)] = '8', + [BRLAPI_DOTS(0,0,1,0,1,0,0,0)] = '9', + + [BRLAPI_DOTS(0,0,0,1,0,1,0,0)] = '.', + [BRLAPI_DOTS(0,0,1,1,0,1,0,0)] = '+', + [BRLAPI_DOTS(0,0,1,0,0,1,0,0)] = '-', + [BRLAPI_DOTS(1,0,0,0,0,1,0,0)] = '*', + [BRLAPI_DOTS(0,0,1,1,0,0,0,0)] = '/', + [BRLAPI_DOTS(1,1,1,0,1,1,0,0)] = '(', + [BRLAPI_DOTS(0,1,1,1,1,1,0,0)] = ')', + + [BRLAPI_DOTS(1,1,1,1,0,1,0,0)] = '&', + [BRLAPI_DOTS(0,0,1,1,1,1,0,0)] = '#', + + [BRLAPI_DOTS(0,0,0,0,0,1,0,0)] = ',', + [BRLAPI_DOTS(0,0,0,0,1,1,0,0)] = ';', + [BRLAPI_DOTS(1,0,0,0,1,1,0,0)] = ':', + [BRLAPI_DOTS(0,1,1,1,0,1,0,0)] = '!', + [BRLAPI_DOTS(1,0,0,1,1,1,0,0)] = '?', + [BRLAPI_DOTS(0,0,0,0,1,0,0,0)] = '"', + [BRLAPI_DOTS(0,0,1,0,0,0,0,0)] ='\'', + [BRLAPI_DOTS(0,0,0,1,0,0,0,0)] = '`', + [BRLAPI_DOTS(0,0,0,1,1,0,1,0)] = '^', + [BRLAPI_DOTS(0,0,0,1,1,0,0,0)] = '~', + [BRLAPI_DOTS(0,1,0,1,0,1,1,0)] = '[', + [BRLAPI_DOTS(1,1,0,1,1,1,1,0)] = ']', + [BRLAPI_DOTS(0,1,0,1,0,1,0,0)] = '{', + [BRLAPI_DOTS(1,1,0,1,1,1,0,0)] = '}', + [BRLAPI_DOTS(1,1,1,1,1,1,0,0)] = '=', + [BRLAPI_DOTS(1,1,0,0,0,1,0,0)] = '<', + [BRLAPI_DOTS(0,0,1,1,1,0,0,0)] = '>', + [BRLAPI_DOTS(1,1,0,1,0,1,0,0)] = '$', + [BRLAPI_DOTS(1,0,0,1,0,1,0,0)] = '%', + [BRLAPI_DOTS(0,0,0,1,0,0,1,0)] = '@', + [BRLAPI_DOTS(1,1,0,0,1,1,0,0)] = '|', + [BRLAPI_DOTS(1,1,0,0,1,1,1,0)] ='\\', + [BRLAPI_DOTS(0,0,0,1,1,1,0,0)] = '_', +}; + +/* The serial port can receive more of our data */ +static void baum_accept_input(struct CharDriverState *chr) +{ + BaumDriverState *baum = chr->opaque; + int room, first; + + if (!baum->out_buf_used) + return; + room = qemu_chr_be_can_write(chr); + if (!room) + return; + if (room > baum->out_buf_used) + room = baum->out_buf_used; + + first = BUF_SIZE - baum->out_buf_ptr; + if (room > first) { + qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, first); + baum->out_buf_ptr = 0; + baum->out_buf_used -= first; + room -= first; + } + qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, room); + baum->out_buf_ptr += room; + baum->out_buf_used -= room; +} + +/* We want to send a packet */ +static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len) +{ + uint8_t io_buf[1 + 2 * len], *cur = io_buf; + int room; + *cur++ = ESC; + while (len--) + if ((*cur++ = *buf++) == ESC) + *cur++ = ESC; + room = qemu_chr_be_can_write(baum->chr); + len = cur - io_buf; + if (len <= room) { + /* Fits */ + qemu_chr_be_write(baum->chr, io_buf, len); + } else { + int first; + uint8_t out; + /* Can't fit all, send what can be, and store the rest. */ + qemu_chr_be_write(baum->chr, io_buf, room); + len -= room; + cur = io_buf + room; + if (len > BUF_SIZE - baum->out_buf_used) { + /* Can't even store it, drop the previous data... */ + assert(len <= BUF_SIZE); + baum->out_buf_used = 0; + baum->out_buf_ptr = 0; + } + out = baum->out_buf_ptr; + baum->out_buf_used += len; + first = BUF_SIZE - baum->out_buf_ptr; + if (len > first) { + memcpy(baum->out_buf + out, cur, first); + out = 0; + len -= first; + cur += first; + } + memcpy(baum->out_buf + out, cur, len); + } +} + +/* Called when the other end seems to have a wrong idea of our display size */ +static void baum_cellCount_timer_cb(void *opaque) +{ + BaumDriverState *baum = opaque; + uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y }; + DPRINTF("Timeout waiting for DisplayData, sending cell count\n"); + baum_write_packet(baum, cell_count, sizeof(cell_count)); +} + +/* Try to interpret a whole incoming packet */ +static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len) +{ + const uint8_t *cur = buf; + uint8_t req = 0; + + if (!len--) + return 0; + if (*cur++ != ESC) { + while (*cur != ESC) { + if (!len--) + return 0; + cur++; + } + DPRINTF("Dropped %d bytes!\n", cur - buf); + } + +#define EAT(c) do {\ + if (!len--) \ + return 0; \ + if ((c = *cur++) == ESC) { \ + if (!len--) \ + return 0; \ + if (*cur++ != ESC) { \ + DPRINTF("Broken packet %#2x, tossing\n", req); \ + if (timer_pending(baum->cellCount_timer)) { \ + timer_del(baum->cellCount_timer); \ + baum_cellCount_timer_cb(baum); \ + } \ + return (cur - 2 - buf); \ + } \ + } \ +} while (0) + + EAT(req); + switch (req) { + case BAUM_REQ_DisplayData: + { + uint8_t cells[baum->x * baum->y], c; + uint8_t text[baum->x * baum->y]; + uint8_t zero[baum->x * baum->y]; + int cursor = BRLAPI_CURSOR_OFF; + int i; + + /* Allow 100ms to complete the DisplayData packet */ + timer_mod(baum->cellCount_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + get_ticks_per_sec() / 10); + for (i = 0; i < baum->x * baum->y ; i++) { + EAT(c); + cells[i] = c; + if ((c & (BRLAPI_DOT7|BRLAPI_DOT8)) + == (BRLAPI_DOT7|BRLAPI_DOT8)) { + cursor = i + 1; + c &= ~(BRLAPI_DOT7|BRLAPI_DOT8); + } + if (!(c = nabcc_translation[c])) + c = '?'; + text[i] = c; + } + timer_del(baum->cellCount_timer); + + memset(zero, 0, sizeof(zero)); + + brlapi_writeArguments_t wa = { + .displayNumber = BRLAPI_DISPLAY_DEFAULT, + .regionBegin = 1, + .regionSize = baum->x * baum->y, + .text = (char *)text, + .textSize = baum->x * baum->y, + .andMask = zero, + .orMask = cells, + .cursor = cursor, + .charset = (char *)"ISO-8859-1", + }; + + if (brlapi__write(baum->brlapi, &wa) == -1) + brlapi_perror("baum brlapi_write"); + break; + } + case BAUM_REQ_SetMode: + { + uint8_t mode, setting; + DPRINTF("SetMode\n"); + EAT(mode); + EAT(setting); + /* ignore */ + break; + } + case BAUM_REQ_SetProtocol: + { + uint8_t protocol; + DPRINTF("SetProtocol\n"); + EAT(protocol); + /* ignore */ + break; + } + case BAUM_REQ_GetDeviceIdentity: + { + uint8_t identity[17] = { BAUM_RSP_DeviceIdentity, + 'B','a','u','m',' ','V','a','r','i','o' }; + DPRINTF("GetDeviceIdentity\n"); + identity[11] = '0' + baum->x / 10; + identity[12] = '0' + baum->x % 10; + baum_write_packet(baum, identity, sizeof(identity)); + break; + } + case BAUM_REQ_GetVersionNumber: + { + uint8_t version[] = { BAUM_RSP_VersionNumber, 1 }; /* ? */ + DPRINTF("GetVersionNumber\n"); + baum_write_packet(baum, version, sizeof(version)); + break; + } + case BAUM_REQ_GetSerialNumber: + { + uint8_t serial[] = { BAUM_RSP_SerialNumber, + '0','0','0','0','0','0','0','0' }; + DPRINTF("GetSerialNumber\n"); + baum_write_packet(baum, serial, sizeof(serial)); + break; + } + case BAUM_REQ_GetKeys: + { + DPRINTF("Get%0#2x\n", req); + /* ignore */ + break; + } + default: + DPRINTF("unrecognized request %0#2x\n", req); + do + if (!len--) + return 0; + while (*cur++ != ESC); + cur--; + break; + } + return cur - buf; +} + +/* The other end is writing some data. Store it and try to interpret */ +static int baum_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + BaumDriverState *baum = chr->opaque; + int tocopy, cur, eaten, orig_len = len; + + if (!len) + return 0; + if (!baum->brlapi) + return len; + + while (len) { + /* Complete our buffer as much as possible */ + tocopy = len; + if (tocopy > BUF_SIZE - baum->in_buf_used) + tocopy = BUF_SIZE - baum->in_buf_used; + + memcpy(baum->in_buf + baum->in_buf_used, buf, tocopy); + baum->in_buf_used += tocopy; + buf += tocopy; + len -= tocopy; + + /* Interpret it as much as possible */ + cur = 0; + while (cur < baum->in_buf_used && + (eaten = baum_eat_packet(baum, baum->in_buf + cur, baum->in_buf_used - cur))) + cur += eaten; + + /* Shift the remainder */ + if (cur) { + memmove(baum->in_buf, baum->in_buf + cur, baum->in_buf_used - cur); + baum->in_buf_used -= cur; + } + + /* And continue if any data left */ + } + return orig_len; +} + +/* Send the key code to the other end */ +static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) { + uint8_t packet[] = { type, value }; + DPRINTF("writing key %x %x\n", type, value); + baum_write_packet(baum, packet, sizeof(packet)); +} + +/* We got some data on the BrlAPI socket */ +static void baum_chr_read(void *opaque) +{ + BaumDriverState *baum = opaque; + brlapi_keyCode_t code; + int ret; + if (!baum->brlapi) + return; + while ((ret = brlapi__readKey(baum->brlapi, 0, &code)) == 1) { + DPRINTF("got key %"BRLAPI_PRIxKEYCODE"\n", code); + /* Emulate */ + switch (code & BRLAPI_KEY_TYPE_MASK) { + case BRLAPI_KEY_TYPE_CMD: + switch (code & BRLAPI_KEY_CMD_BLK_MASK) { + case BRLAPI_KEY_CMD_ROUTE: + baum_send_key(baum, BAUM_RSP_RoutingKey, (code & BRLAPI_KEY_CMD_ARG_MASK)+1); + baum_send_key(baum, BAUM_RSP_RoutingKey, 0); + break; + case 0: + switch (code & BRLAPI_KEY_CMD_ARG_MASK) { + case BRLAPI_KEY_CMD_FWINLT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_FWINRT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR2); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_LNUP: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR1); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_LNDN: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR3); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_TOP: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TR1); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_BOT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL3|BAUM_TR3); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_TOP_LEFT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_BOT_LEFT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR3); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_HOME: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1|BAUM_TR3); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_PREFMENU: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TL3|BAUM_TR1); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + } + } + break; + case BRLAPI_KEY_TYPE_SYM: + break; + } + } + if (ret == -1 && (brlapi_errno != BRLAPI_ERROR_LIBCERR || errno != EINTR)) { + brlapi_perror("baum: brlapi_readKey"); + brlapi__closeConnection(baum->brlapi); + g_free(baum->brlapi); + baum->brlapi = NULL; + } +} + +static void baum_close(struct CharDriverState *chr) +{ + BaumDriverState *baum = chr->opaque; + + timer_free(baum->cellCount_timer); + if (baum->brlapi) { + brlapi__closeConnection(baum->brlapi); + g_free(baum->brlapi); + } + g_free(baum); +} + +CharDriverState *chr_baum_init(void) +{ + BaumDriverState *baum; + CharDriverState *chr; + brlapi_handle_t *handle; +#if defined(CONFIG_SDL) +#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) + SDL_SysWMinfo info; +#endif +#endif + int tty; + + baum = g_malloc0(sizeof(BaumDriverState)); + baum->chr = chr = qemu_chr_alloc(); + + chr->opaque = baum; + chr->chr_write = baum_write; + chr->chr_accept_input = baum_accept_input; + chr->chr_close = baum_close; + + handle = g_malloc0(brlapi_getHandleSize()); + baum->brlapi = handle; + + baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL); + if (baum->brlapi_fd == -1) { + brlapi_perror("baum_init: brlapi_openConnection"); + goto fail_handle; + } + + baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); + + if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) { + brlapi_perror("baum_init: brlapi_getDisplaySize"); + goto fail; + } + +#if defined(CONFIG_SDL) +#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) + memset(&info, 0, sizeof(info)); + SDL_VERSION(&info.version); + if (SDL_GetWMInfo(&info)) + tty = info.info.x11.wmwindow; + else +#endif +#endif + tty = BRLAPI_TTY_DEFAULT; + + if (brlapi__enterTtyMode(handle, tty, NULL) == -1) { + brlapi_perror("baum_init: brlapi_enterTtyMode"); + goto fail; + } + + qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); + + return chr; + +fail: + timer_free(baum->cellCount_timer); + brlapi__closeConnection(handle); +fail_handle: + g_free(handle); + g_free(chr); + g_free(baum); + return NULL; +} + +static void register_types(void) +{ + register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL); +} + +type_init(register_types); diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c new file mode 100644 index 0000000..4b55361 --- /dev/null +++ b/backends/hostmem-file.c @@ -0,0 +1,134 @@ +/* + * QEMU Host Memory Backend for hugetlbfs + * + * Copyright (C) 2013-2014 Red Hat Inc + * + * Authors: + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu-common.h" +#include "sysemu/hostmem.h" +#include "sysemu/sysemu.h" +#include "qom/object_interfaces.h" + +/* hostmem-file.c */ +/** + * @TYPE_MEMORY_BACKEND_FILE: + * name of backend that uses mmap on a file descriptor + */ +#define TYPE_MEMORY_BACKEND_FILE "memory-backend-file" + +#define MEMORY_BACKEND_FILE(obj) \ + OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE) + +typedef struct HostMemoryBackendFile HostMemoryBackendFile; + +struct HostMemoryBackendFile { + HostMemoryBackend parent_obj; + + bool share; + char *mem_path; +}; + +static void +file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); + + if (!backend->size) { + error_setg(errp, "can't create backend with size 0"); + return; + } + if (!fb->mem_path) { + error_setg(errp, "mem-path property not set"); + return; + } +#ifndef CONFIG_LINUX + error_setg(errp, "-mem-path not supported on this host"); +#else + if (!memory_region_size(&backend->mr)) { + backend->force_prealloc = mem_prealloc; + memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), + object_get_canonical_path(OBJECT(backend)), + backend->size, fb->share, + fb->mem_path, errp); + } +#endif +} + +static void +file_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = file_backend_memory_alloc; +} + +static char *get_mem_path(Object *o, Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + return g_strdup(fb->mem_path); +} + +static void set_mem_path(Object *o, const char *str, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(o); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + if (memory_region_size(&backend->mr)) { + error_setg(errp, "cannot change property value"); + return; + } + if (fb->mem_path) { + g_free(fb->mem_path); + } + fb->mem_path = g_strdup(str); +} + +static bool file_memory_backend_get_share(Object *o, Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + return fb->share; +} + +static void file_memory_backend_set_share(Object *o, bool value, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(o); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + if (memory_region_size(&backend->mr)) { + error_setg(errp, "cannot change property value"); + return; + } + fb->share = value; +} + +static void +file_backend_instance_init(Object *o) +{ + object_property_add_bool(o, "share", + file_memory_backend_get_share, + file_memory_backend_set_share, NULL); + object_property_add_str(o, "mem-path", get_mem_path, + set_mem_path, NULL); +} + +static const TypeInfo file_backend_info = { + .name = TYPE_MEMORY_BACKEND_FILE, + .parent = TYPE_MEMORY_BACKEND, + .class_init = file_backend_class_init, + .instance_init = file_backend_instance_init, + .instance_size = sizeof(HostMemoryBackendFile), +}; + +static void register_types(void) +{ + type_register_static(&file_backend_info); +} + +type_init(register_types); diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c new file mode 100644 index 0000000..a67a134 --- /dev/null +++ b/backends/hostmem-ram.c @@ -0,0 +1,53 @@ +/* + * QEMU Host Memory Backend + * + * Copyright (C) 2013-2014 Red Hat Inc + * + * Authors: + * Igor Mammedov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "sysemu/hostmem.h" +#include "qom/object_interfaces.h" + +#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" + + +static void +ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + char *path; + + if (!backend->size) { + error_setg(errp, "can't create backend with size 0"); + return; + } + + path = object_get_canonical_path_component(OBJECT(backend)); + memory_region_init_ram(&backend->mr, OBJECT(backend), path, + backend->size, errp); + g_free(path); +} + +static void +ram_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = ram_backend_memory_alloc; +} + +static const TypeInfo ram_backend_info = { + .name = TYPE_MEMORY_BACKEND_RAM, + .parent = TYPE_MEMORY_BACKEND, + .class_init = ram_backend_class_init, +}; + +static void register_types(void) +{ + type_register_static(&ram_backend_info); +} + +type_init(register_types); diff --git a/backends/hostmem.c b/backends/hostmem.c new file mode 100644 index 0000000..b7b6cf8 --- /dev/null +++ b/backends/hostmem.c @@ -0,0 +1,379 @@ +/* + * QEMU Host Memory Backend + * + * Copyright (C) 2013-2014 Red Hat Inc + * + * Authors: + * Igor Mammedov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "sysemu/hostmem.h" +#include "qapi/visitor.h" +#include "qapi-types.h" +#include "qapi-visit.h" +#include "qapi/qmp/qerror.h" +#include "qemu/config-file.h" +#include "qom/object_interfaces.h" + +#ifdef CONFIG_NUMA +#include +QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_DEFAULT != MPOL_DEFAULT); +QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_PREFERRED != MPOL_PREFERRED); +QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); +QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); +#endif + +static void +host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + uint64_t value = backend->size; + + visit_type_size(v, &value, name, errp); +} + +static void +host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + Error *local_err = NULL; + uint64_t value; + + if (memory_region_size(&backend->mr)) { + error_setg(&local_err, "cannot change property value"); + goto out; + } + + visit_type_size(v, &value, name, &local_err); + if (local_err) { + goto out; + } + if (!value) { + error_setg(&local_err, "Property '%s.%s' doesn't take value '%" + PRIu64 "'", object_get_typename(obj), name, value); + goto out; + } + backend->size = value; +out: + error_propagate(errp, local_err); +} + +static void +host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + uint16List *host_nodes = NULL; + uint16List **node = &host_nodes; + unsigned long value; + + value = find_first_bit(backend->host_nodes, MAX_NODES); + if (value == MAX_NODES) { + return; + } + + *node = g_malloc0(sizeof(**node)); + (*node)->value = value; + node = &(*node)->next; + + do { + value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1); + if (value == MAX_NODES) { + break; + } + + *node = g_malloc0(sizeof(**node)); + (*node)->value = value; + node = &(*node)->next; + } while (true); + + visit_type_uint16List(v, &host_nodes, name, errp); +} + +static void +host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ +#ifdef CONFIG_NUMA + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + uint16List *l = NULL; + + visit_type_uint16List(v, &l, name, errp); + + while (l) { + bitmap_set(backend->host_nodes, l->value, 1); + l = l->next; + } +#else + error_setg(errp, "NUMA node binding are not supported by this QEMU"); +#endif +} + +static void +host_memory_backend_get_policy(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + int policy = backend->policy; + + visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp); +} + +static void +host_memory_backend_set_policy(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + int policy; + + visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp); + backend->policy = policy; + +#ifndef CONFIG_NUMA + if (policy != HOST_MEM_POLICY_DEFAULT) { + error_setg(errp, "NUMA policies are not supported by this QEMU"); + } +#endif +} + +static bool host_memory_backend_get_merge(Object *obj, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + return backend->merge; +} + +static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + if (!memory_region_size(&backend->mr)) { + backend->merge = value; + return; + } + + if (value != backend->merge) { + void *ptr = memory_region_get_ram_ptr(&backend->mr); + uint64_t sz = memory_region_size(&backend->mr); + + qemu_madvise(ptr, sz, + value ? QEMU_MADV_MERGEABLE : QEMU_MADV_UNMERGEABLE); + backend->merge = value; + } +} + +static bool host_memory_backend_get_dump(Object *obj, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + return backend->dump; +} + +static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + if (!memory_region_size(&backend->mr)) { + backend->dump = value; + return; + } + + if (value != backend->dump) { + void *ptr = memory_region_get_ram_ptr(&backend->mr); + uint64_t sz = memory_region_size(&backend->mr); + + qemu_madvise(ptr, sz, + value ? QEMU_MADV_DODUMP : QEMU_MADV_DONTDUMP); + backend->dump = value; + } +} + +static bool host_memory_backend_get_prealloc(Object *obj, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + return backend->prealloc || backend->force_prealloc; +} + +static void host_memory_backend_set_prealloc(Object *obj, bool value, + Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + if (backend->force_prealloc) { + if (value) { + error_setg(errp, + "remove -mem-prealloc to use the prealloc property"); + return; + } + } + + if (!memory_region_size(&backend->mr)) { + backend->prealloc = value; + return; + } + + if (value && !backend->prealloc) { + int fd = memory_region_get_fd(&backend->mr); + void *ptr = memory_region_get_ram_ptr(&backend->mr); + uint64_t sz = memory_region_size(&backend->mr); + + os_mem_prealloc(fd, ptr, sz); + backend->prealloc = true; + } +} + +static void host_memory_backend_init(Object *obj) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + backend->merge = qemu_opt_get_bool(qemu_get_machine_opts(), + "mem-merge", true); + backend->dump = qemu_opt_get_bool(qemu_get_machine_opts(), + "dump-guest-core", true); + backend->prealloc = mem_prealloc; + + object_property_add_bool(obj, "merge", + host_memory_backend_get_merge, + host_memory_backend_set_merge, NULL); + object_property_add_bool(obj, "dump", + host_memory_backend_get_dump, + host_memory_backend_set_dump, NULL); + object_property_add_bool(obj, "prealloc", + host_memory_backend_get_prealloc, + host_memory_backend_set_prealloc, NULL); + object_property_add(obj, "size", "int", + host_memory_backend_get_size, + host_memory_backend_set_size, NULL, NULL, NULL); + object_property_add(obj, "host-nodes", "int", + host_memory_backend_get_host_nodes, + host_memory_backend_set_host_nodes, NULL, NULL, NULL); + object_property_add(obj, "policy", "str", + host_memory_backend_get_policy, + host_memory_backend_set_policy, NULL, NULL, NULL); +} + +MemoryRegion * +host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) +{ + return memory_region_size(&backend->mr) ? &backend->mr : NULL; +} + +static void +host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(uc); + HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); + Error *local_err = NULL; + void *ptr; + uint64_t sz; + + if (bc->alloc) { + bc->alloc(backend, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + ptr = memory_region_get_ram_ptr(&backend->mr); + sz = memory_region_size(&backend->mr); + + if (backend->merge) { + qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE); + } + if (!backend->dump) { + qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP); + } +#ifdef CONFIG_NUMA + unsigned long lastbit = find_last_bit(backend->host_nodes, MAX_NODES); + /* lastbit == MAX_NODES means maxnode = 0 */ + unsigned long maxnode = (lastbit + 1) % (MAX_NODES + 1); + /* ensure policy won't be ignored in case memory is preallocated + * before mbind(). note: MPOL_MF_STRICT is ignored on hugepages so + * this doesn't catch hugepage case. */ + unsigned flags = MPOL_MF_STRICT | MPOL_MF_MOVE; + + /* check for invalid host-nodes and policies and give more verbose + * error messages than mbind(). */ + if (maxnode && backend->policy == MPOL_DEFAULT) { + error_setg(errp, "host-nodes must be empty for policy default," + " or you should explicitly specify a policy other" + " than default"); + return; + } else if (maxnode == 0 && backend->policy != MPOL_DEFAULT) { + error_setg(errp, "host-nodes must be set for policy %s", + HostMemPolicy_lookup[backend->policy]); + return; + } + + /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1 + * as argument to mbind() due to an old Linux bug (feature?) which + * cuts off the last specified node. This means backend->host_nodes + * must have MAX_NODES+1 bits available. + */ + assert(sizeof(backend->host_nodes) >= + BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long)); + assert(maxnode <= MAX_NODES); + if (mbind(ptr, sz, backend->policy, + maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) { + error_setg_errno(errp, errno, + "cannot bind memory to host NUMA nodes"); + return; + } +#endif + /* Preallocate memory after the NUMA policy has been instantiated. + * This is necessary to guarantee memory is allocated with + * specified NUMA policy in place. + */ + if (backend->prealloc) { + os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz); + } + } +} + +static bool +host_memory_backend_can_be_deleted(UserCreatable *uc, Error **errp) +{ + MemoryRegion *mr; + + mr = host_memory_backend_get_memory(MEMORY_BACKEND(uc), errp); + if (memory_region_is_mapped(mr)) { + return false; + } else { + return true; + } +} + +static void +host_memory_backend_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = host_memory_backend_memory_complete; + ucc->can_be_deleted = host_memory_backend_can_be_deleted; +} + +static const TypeInfo host_memory_backend_info = { + .name = TYPE_MEMORY_BACKEND, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(HostMemoryBackendClass), + .class_init = host_memory_backend_class_init, + .instance_size = sizeof(HostMemoryBackend), + .instance_init = host_memory_backend_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&host_memory_backend_info); +} + +type_init(register_types); diff --git a/backends/msmouse.c b/backends/msmouse.c new file mode 100644 index 0000000..0119110 --- /dev/null +++ b/backends/msmouse.c @@ -0,0 +1,85 @@ +/* + * QEMU Microsoft serial mouse emulation + * + * Copyright (c) 2008 Lubomir Rintel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "qemu-common.h" +#include "sysemu/char.h" +#include "ui/console.h" + +#define MSMOUSE_LO6(n) ((n) & 0x3f) +#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) + +static void msmouse_event(void *opaque, + int dx, int dy, int dz, int buttons_state) +{ + CharDriverState *chr = (CharDriverState *)opaque; + + unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 }; + + /* Movement deltas */ + bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx); + bytes[1] |= MSMOUSE_LO6(dx); + bytes[2] |= MSMOUSE_LO6(dy); + + /* Buttons */ + bytes[0] |= (buttons_state & 0x01 ? 0x20 : 0x00); + bytes[0] |= (buttons_state & 0x02 ? 0x10 : 0x00); + bytes[3] |= (buttons_state & 0x04 ? 0x20 : 0x00); + + /* We always send the packet of, so that we do not have to keep track + of previous state of the middle button. This can potentially confuse + some very old drivers for two button mice though. */ + qemu_chr_be_write(chr, bytes, 4); +} + +static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len) +{ + /* Ignore writes to mouse port */ + return len; +} + +static void msmouse_chr_close (struct CharDriverState *chr) +{ + g_free (chr); +} + +CharDriverState *qemu_chr_open_msmouse(void) +{ + CharDriverState *chr; + + chr = qemu_chr_alloc(); + chr->chr_write = msmouse_chr_write; + chr->chr_close = msmouse_chr_close; + chr->explicit_be_open = true; + + qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); + + return chr; +} + +static void register_types(void) +{ + register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL); +} + +type_init(register_types); diff --git a/backends/rng-egd.c b/backends/rng-egd.c new file mode 100644 index 0000000..2962795 --- /dev/null +++ b/backends/rng-egd.c @@ -0,0 +1,232 @@ +/* + * QEMU Random Number Generator Backend + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "sysemu/rng.h" +#include "sysemu/char.h" +#include "qapi/qmp/qerror.h" +#include "hw/qdev.h" /* just for DEFINE_PROP_CHR */ + +#define TYPE_RNG_EGD "rng-egd" +#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD) + +typedef struct RngEgd +{ + RngBackend parent; + + CharDriverState *chr; + char *chr_name; + + GSList *requests; +} RngEgd; + +typedef struct RngRequest +{ + EntropyReceiveFunc *receive_entropy; + uint8_t *data; + void *opaque; + size_t offset; + size_t size; +} RngRequest; + +static void rng_egd_request_entropy(RngBackend *b, size_t size, + EntropyReceiveFunc *receive_entropy, + void *opaque) +{ + RngEgd *s = RNG_EGD(b); + RngRequest *req; + + req = g_malloc(sizeof(*req)); + + req->offset = 0; + req->size = size; + req->receive_entropy = receive_entropy; + req->opaque = opaque; + req->data = g_malloc(req->size); + + while (size > 0) { + uint8_t header[2]; + uint8_t len = MIN(size, 255); + + /* synchronous entropy request */ + header[0] = 0x02; + header[1] = len; + + qemu_chr_fe_write(s->chr, header, sizeof(header)); + + size -= len; + } + + s->requests = g_slist_append(s->requests, req); +} + +static void rng_egd_free_request(RngRequest *req) +{ + g_free(req->data); + g_free(req); +} + +static int rng_egd_chr_can_read(void *opaque) +{ + RngEgd *s = RNG_EGD(opaque); + GSList *i; + int size = 0; + + for (i = s->requests; i; i = i->next) { + RngRequest *req = i->data; + size += req->size - req->offset; + } + + return size; +} + +static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size) +{ + RngEgd *s = RNG_EGD(opaque); + size_t buf_offset = 0; + + while (size > 0 && s->requests) { + RngRequest *req = s->requests->data; + int len = MIN(size, req->size - req->offset); + + memcpy(req->data + req->offset, buf + buf_offset, len); + buf_offset += len; + req->offset += len; + size -= len; + + if (req->offset == req->size) { + s->requests = g_slist_remove_link(s->requests, s->requests); + + req->receive_entropy(req->opaque, req->data, req->size); + + rng_egd_free_request(req); + } + } +} + +static void rng_egd_free_requests(RngEgd *s) +{ + GSList *i; + + for (i = s->requests; i; i = i->next) { + rng_egd_free_request(i->data); + } + + g_slist_free(s->requests); + s->requests = NULL; +} + +static void rng_egd_cancel_requests(RngBackend *b) +{ + RngEgd *s = RNG_EGD(b); + + /* We simply delete the list of pending requests. If there is data in the + * queue waiting to be read, this is okay, because there will always be + * more data than we requested originally + */ + rng_egd_free_requests(s); +} + +static void rng_egd_opened(RngBackend *b, Error **errp) +{ + RngEgd *s = RNG_EGD(b); + + if (s->chr_name == NULL) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + "chardev", "a valid character device"); + return; + } + + s->chr = qemu_chr_find(s->chr_name); + if (s->chr == NULL) { + error_set(errp, QERR_DEVICE_NOT_FOUND, s->chr_name); + return; + } + + if (qemu_chr_fe_claim(s->chr) != 0) { + error_set(errp, QERR_DEVICE_IN_USE, s->chr_name); + return; + } + + /* FIXME we should resubmit pending requests when the CDS reconnects. */ + qemu_chr_add_handlers(s->chr, rng_egd_chr_can_read, rng_egd_chr_read, + NULL, s); +} + +static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) +{ + RngBackend *b = RNG_BACKEND(obj); + RngEgd *s = RNG_EGD(b); + + if (b->opened) { + error_set(errp, QERR_PERMISSION_DENIED); + } else { + g_free(s->chr_name); + s->chr_name = g_strdup(value); + } +} + +static char *rng_egd_get_chardev(Object *obj, Error **errp) +{ + RngEgd *s = RNG_EGD(obj); + + if (s->chr && s->chr->label) { + return g_strdup(s->chr->label); + } + + return NULL; +} + +static void rng_egd_init(Object *obj) +{ + object_property_add_str(obj, "chardev", + rng_egd_get_chardev, rng_egd_set_chardev, + NULL); +} + +static void rng_egd_finalize(Object *obj) +{ + RngEgd *s = RNG_EGD(obj); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL); + qemu_chr_fe_release(s->chr); + } + + g_free(s->chr_name); + + rng_egd_free_requests(s); +} + +static void rng_egd_class_init(ObjectClass *klass, void *data) +{ + RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); + + rbc->request_entropy = rng_egd_request_entropy; + rbc->cancel_requests = rng_egd_cancel_requests; + rbc->opened = rng_egd_opened; +} + +static const TypeInfo rng_egd_info = { + .name = TYPE_RNG_EGD, + .parent = TYPE_RNG_BACKEND, + .instance_size = sizeof(RngEgd), + .class_init = rng_egd_class_init, + .instance_init = rng_egd_init, + .instance_finalize = rng_egd_finalize, +}; + +static void register_types(void) +{ + type_register_static(&rng_egd_info); +} + +type_init(register_types); diff --git a/backends/rng-random.c b/backends/rng-random.c new file mode 100644 index 0000000..4f85a8e --- /dev/null +++ b/backends/rng-random.c @@ -0,0 +1,156 @@ +/* + * QEMU Random Number Generator Backend + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "sysemu/rng-random.h" +#include "sysemu/rng.h" +#include "qapi/qmp/qerror.h" +#include "qemu/main-loop.h" + +struct RndRandom +{ + RngBackend parent; + + int fd; + char *filename; + + EntropyReceiveFunc *receive_func; + void *opaque; + size_t size; +}; + +/** + * A simple and incomplete backend to request entropy from /dev/random. + * + * This backend exposes an additional "filename" property that can be used to + * set the filename to use to open the backend. + */ + +static void entropy_available(void *opaque) +{ + RndRandom *s = RNG_RANDOM(opaque); + uint8_t buffer[s->size]; + ssize_t len; + + len = read(s->fd, buffer, s->size); + if (len < 0 && errno == EAGAIN) { + return; + } + g_assert(len != -1); + + s->receive_func(s->opaque, buffer, len); + s->receive_func = NULL; + + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); +} + +static void rng_random_request_entropy(RngBackend *b, size_t size, + EntropyReceiveFunc *receive_entropy, + void *opaque) +{ + RndRandom *s = RNG_RANDOM(b); + + if (s->receive_func) { + s->receive_func(s->opaque, NULL, 0); + } + + s->receive_func = receive_entropy; + s->opaque = opaque; + s->size = size; + + qemu_set_fd_handler(s->fd, entropy_available, NULL, s); +} + +static void rng_random_opened(RngBackend *b, Error **errp) +{ + RndRandom *s = RNG_RANDOM(b); + + if (s->filename == NULL) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + "filename", "a valid filename"); + } else { + s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK); + if (s->fd == -1) { + error_setg_file_open(errp, errno, s->filename); + } + } +} + +static char *rng_random_get_filename(Object *obj, Error **errp) +{ + RndRandom *s = RNG_RANDOM(obj); + + return g_strdup(s->filename); +} + +static void rng_random_set_filename(Object *obj, const char *filename, + Error **errp) +{ + RngBackend *b = RNG_BACKEND(obj); + RndRandom *s = RNG_RANDOM(obj); + + if (b->opened) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + g_free(s->filename); + s->filename = g_strdup(filename); +} + +static void rng_random_init(Object *obj) +{ + RndRandom *s = RNG_RANDOM(obj); + + object_property_add_str(obj, "filename", + rng_random_get_filename, + rng_random_set_filename, + NULL); + + s->filename = g_strdup("/dev/random"); + s->fd = -1; +} + +static void rng_random_finalize(Object *obj) +{ + RndRandom *s = RNG_RANDOM(obj); + + if (s->fd != -1) { + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + qemu_close(s->fd); + } + + g_free(s->filename); +} + +static void rng_random_class_init(ObjectClass *klass, void *data) +{ + RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); + + rbc->request_entropy = rng_random_request_entropy; + rbc->opened = rng_random_opened; +} + +static const TypeInfo rng_random_info = { + .name = TYPE_RNG_RANDOM, + .parent = TYPE_RNG_BACKEND, + .instance_size = sizeof(RndRandom), + .class_init = rng_random_class_init, + .instance_init = rng_random_init, + .instance_finalize = rng_random_finalize, +}; + +static void register_types(void) +{ + type_register_static(&rng_random_info); +} + +type_init(register_types); diff --git a/backends/rng.c b/backends/rng.c new file mode 100644 index 0000000..0f2fc11 --- /dev/null +++ b/backends/rng.c @@ -0,0 +1,109 @@ +/* + * QEMU Random Number Generator Backend + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "sysemu/rng.h" +#include "qapi/qmp/qerror.h" +#include "qom/object_interfaces.h" + +void rng_backend_request_entropy(RngBackend *s, size_t size, + EntropyReceiveFunc *receive_entropy, + void *opaque) +{ + RngBackendClass *k = RNG_BACKEND_GET_CLASS(s); + + if (k->request_entropy) { + k->request_entropy(s, size, receive_entropy, opaque); + } +} + +void rng_backend_cancel_requests(RngBackend *s) +{ + RngBackendClass *k = RNG_BACKEND_GET_CLASS(s); + + if (k->cancel_requests) { + k->cancel_requests(s); + } +} + +static bool rng_backend_prop_get_opened(Object *obj, Error **errp) +{ + RngBackend *s = RNG_BACKEND(obj); + + return s->opened; +} + +static void rng_backend_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "opened", errp); +} + +static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp) +{ + RngBackend *s = RNG_BACKEND(obj); + RngBackendClass *k = RNG_BACKEND_GET_CLASS(s); + Error *local_err = NULL; + + if (value == s->opened) { + return; + } + + if (!value && s->opened) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + if (k->opened) { + k->opened(s, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + s->opened = true; +} + +static void rng_backend_init(Object *obj) +{ + object_property_add_bool(obj, "opened", + rng_backend_prop_get_opened, + rng_backend_prop_set_opened, + NULL); +} + +static void rng_backend_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = rng_backend_complete; +} + +static const TypeInfo rng_backend_info = { + .name = TYPE_RNG_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(RngBackend), + .instance_init = rng_backend_init, + .class_size = sizeof(RngBackendClass), + .class_init = rng_backend_class_init, + .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&rng_backend_info); +} + +type_init(register_types); diff --git a/backends/testdev.c b/backends/testdev.c new file mode 100644 index 0000000..eba396a --- /dev/null +++ b/backends/testdev.c @@ -0,0 +1,131 @@ +/* + * QEMU Char Device for testsuite control + * + * Copyright (c) 2014 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "sysemu/char.h" + +#define BUF_SIZE 32 + +typedef struct { + CharDriverState *chr; + uint8_t in_buf[32]; + int in_buf_used; +} TestdevCharState; + +/* Try to interpret a whole incoming packet */ +static int testdev_eat_packet(TestdevCharState *testdev) +{ + const uint8_t *cur = testdev->in_buf; + int len = testdev->in_buf_used; + uint8_t c; + int arg; + +#define EAT(c) do { \ + if (!len--) { \ + return 0; \ + } \ + c = *cur++; \ +} while (0) + + EAT(c); + + while (isspace(c)) { + EAT(c); + } + + arg = 0; + while (isdigit(c)) { + arg = arg * 10 + c - '0'; + EAT(c); + } + + while (isspace(c)) { + EAT(c); + } + + switch (c) { + case 'q': + exit((arg << 1) | 1); + break; + default: + break; + } + return cur - testdev->in_buf; +} + +/* The other end is writing some data. Store it and try to interpret */ +static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + TestdevCharState *testdev = chr->opaque; + int tocopy, eaten, orig_len = len; + + while (len) { + /* Complete our buffer as much as possible */ + tocopy = MIN(len, BUF_SIZE - testdev->in_buf_used); + + memcpy(testdev->in_buf + testdev->in_buf_used, buf, tocopy); + testdev->in_buf_used += tocopy; + buf += tocopy; + len -= tocopy; + + /* Interpret it as much as possible */ + while (testdev->in_buf_used > 0 && + (eaten = testdev_eat_packet(testdev)) > 0) { + memmove(testdev->in_buf, testdev->in_buf + eaten, + testdev->in_buf_used - eaten); + testdev->in_buf_used -= eaten; + } + } + return orig_len; +} + +static void testdev_close(struct CharDriverState *chr) +{ + TestdevCharState *testdev = chr->opaque; + + g_free(testdev); +} + +CharDriverState *chr_testdev_init(void) +{ + TestdevCharState *testdev; + CharDriverState *chr; + + testdev = g_malloc0(sizeof(TestdevCharState)); + testdev->chr = chr = g_malloc0(sizeof(CharDriverState)); + + chr->opaque = testdev; + chr->chr_write = testdev_write; + chr->chr_close = testdev_close; + + return chr; +} + +static void register_types(void) +{ + register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL); +} + +type_init(register_types); diff --git a/backends/tpm.c b/backends/tpm.c new file mode 100644 index 0000000..36c5d46 --- /dev/null +++ b/backends/tpm.c @@ -0,0 +1,182 @@ +/* + * QEMU TPM Backend + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Based on backends/rng.c by Anthony Liguori + */ + +#include "sysemu/tpm_backend.h" +#include "qapi/qmp/qerror.h" +#include "sysemu/tpm.h" +#include "qemu/thread.h" +#include "sysemu/tpm_backend_int.h" + +enum TpmType tpm_backend_get_type(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->type; +} + +const char *tpm_backend_get_desc(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->desc(); +} + +void tpm_backend_destroy(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->ops->destroy(s); +} + +int tpm_backend_init(TPMBackend *s, TPMState *state, + TPMRecvDataCB *datacb) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->init(s, state, datacb); +} + +int tpm_backend_startup_tpm(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->startup_tpm(s); +} + +bool tpm_backend_had_startup_error(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->had_startup_error(s); +} + +size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->realloc_buffer(sb); +} + +void tpm_backend_deliver_request(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->ops->deliver_request(s); +} + +void tpm_backend_reset(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->ops->reset(s); +} + +void tpm_backend_cancel_cmd(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->ops->cancel_cmd(s); +} + +bool tpm_backend_get_tpm_established_flag(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->get_tpm_established_flag(s); +} + +static bool tpm_backend_prop_get_opened(Object *obj, Error **errp) +{ + TPMBackend *s = TPM_BACKEND(obj); + + return s->opened; +} + +void tpm_backend_open(TPMBackend *s, Error **errp) +{ + object_property_set_bool(OBJECT(s), true, "opened", errp); +} + +static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp) +{ + TPMBackend *s = TPM_BACKEND(obj); + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + Error *local_err = NULL; + + if (value == s->opened) { + return; + } + + if (!value && s->opened) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + if (k->opened) { + k->opened(s, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + s->opened = true; +} + +static void tpm_backend_instance_init(Object *obj) +{ + object_property_add_bool(obj, "opened", + tpm_backend_prop_get_opened, + tpm_backend_prop_set_opened, + NULL); +} + +void tpm_backend_thread_deliver_request(TPMBackendThread *tbt) +{ + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL); +} + +void tpm_backend_thread_create(TPMBackendThread *tbt, + GFunc func, gpointer user_data) +{ + if (!tbt->pool) { + tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL); + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL); + } +} + +void tpm_backend_thread_end(TPMBackendThread *tbt) +{ + if (tbt->pool) { + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL); + g_thread_pool_free(tbt->pool, FALSE, TRUE); + tbt->pool = NULL; + } +} + +static const TypeInfo tpm_backend_info = { + .name = TYPE_TPM_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(TPMBackend), + .instance_init = tpm_backend_instance_init, + .class_size = sizeof(TPMBackendClass), + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&tpm_backend_info); +} + +type_init(register_types); diff --git a/balloon.c b/balloon.c new file mode 100644 index 0000000..c7033e3 --- /dev/null +++ b/balloon.c @@ -0,0 +1,105 @@ +/* + * Generic Balloon handlers and management + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011 Amit Shah + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "monitor/monitor.h" +#include "exec/cpu-common.h" +#include "sysemu/kvm.h" +#include "sysemu/balloon.h" +#include "trace.h" +#include "qmp-commands.h" +#include "qapi/qmp/qjson.h" + +static QEMUBalloonEvent *balloon_event_fn; +static QEMUBalloonStatus *balloon_stat_fn; +static void *balloon_opaque; + +static bool have_balloon(Error **errp) +{ + if (kvm_enabled() && !kvm_has_sync_mmu()) { + error_set(errp, ERROR_CLASS_KVM_MISSING_CAP, + "Using KVM without synchronous MMU, balloon unavailable"); + return false; + } + if (!balloon_event_fn) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, + "No balloon device has been activated"); + return false; + } + return true; +} + +int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, + QEMUBalloonStatus *stat_func, void *opaque) +{ + if (balloon_event_fn || balloon_stat_fn || balloon_opaque) { + /* We're already registered one balloon handler. How many can + * a guest really have? + */ + return -1; + } + balloon_event_fn = event_func; + balloon_stat_fn = stat_func; + balloon_opaque = opaque; + return 0; +} + +void qemu_remove_balloon_handler(void *opaque) +{ + if (balloon_opaque != opaque) { + return; + } + balloon_event_fn = NULL; + balloon_stat_fn = NULL; + balloon_opaque = NULL; +} + +BalloonInfo *qmp_query_balloon(Error **errp) +{ + BalloonInfo *info; + + if (!have_balloon(errp)) { + return NULL; + } + + info = g_malloc0(sizeof(*info)); + balloon_stat_fn(balloon_opaque, info); + return info; +} + +void qmp_balloon(int64_t target, Error **errp) +{ + if (!have_balloon(errp)) { + return; + } + + if (target <= 0) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size"); + return; + } + + trace_balloon_event(balloon_opaque, target); + balloon_event_fn(balloon_opaque, target); +} diff --git a/block.c b/block.c new file mode 100644 index 0000000..f42d70e --- /dev/null +++ b/block.c @@ -0,0 +1,4017 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "config-host.h" +#include "qemu-common.h" +#include "trace.h" +#include "block/block_int.h" +#include "block/blockjob.h" +#include "qemu/module.h" +#include "qapi/qmp/qjson.h" +#include "sysemu/block-backend.h" +#include "sysemu/sysemu.h" +#include "qemu/notify.h" +#include "block/coroutine.h" +#include "block/qapi.h" +#include "qmp-commands.h" +#include "qemu/timer.h" +#include "qapi-event.h" + +#ifdef CONFIG_BSD +#include +#include +#include +#include +#ifndef __DragonFly__ +#include +#endif +#endif + +#ifdef _WIN32 +#include +#endif + +/** + * A BdrvDirtyBitmap can be in three possible states: + * (1) successor is NULL and disabled is false: full r/w mode + * (2) successor is NULL and disabled is true: read only mode ("disabled") + * (3) successor is set: frozen mode. + * A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set, + * or enabled. A frozen bitmap can only abdicate() or reclaim(). + */ +struct BdrvDirtyBitmap { + HBitmap *bitmap; /* Dirty sector bitmap implementation */ + BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ + char *name; /* Optional non-empty unique ID */ + int64_t size; /* Size of the bitmap (Number of sectors) */ + bool disabled; /* Bitmap is read-only */ + QLIST_ENTRY(BdrvDirtyBitmap) list; +}; + +#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ + +static QTAILQ_HEAD(, BlockDriverState) bdrv_states = + QTAILQ_HEAD_INITIALIZER(bdrv_states); + +static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = + QTAILQ_HEAD_INITIALIZER(graph_bdrv_states); + +static QLIST_HEAD(, BlockDriver) bdrv_drivers = + QLIST_HEAD_INITIALIZER(bdrv_drivers); + +static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); +/* If non-zero, use only whitelisted block drivers */ +static int use_bdrv_whitelist; + +#ifdef _WIN32 +static int is_windows_drive_prefix(const char *filename) +{ + return (((filename[0] >= 'a' && filename[0] <= 'z') || + (filename[0] >= 'A' && filename[0] <= 'Z')) && + filename[1] == ':'); +} + +int is_windows_drive(const char *filename) +{ + if (is_windows_drive_prefix(filename) && + filename[2] == '\0') + return 1; + if (strstart(filename, "\\\\.\\", NULL) || + strstart(filename, "//./", NULL)) + return 1; + return 0; +} +#endif + +size_t bdrv_opt_mem_align(BlockDriverState *bs) +{ + if (!bs || !bs->drv) { + /* page size or 4k (hdd sector size) should be on the safe side */ + return MAX(4096, getpagesize()); + } + + return bs->bl.opt_mem_alignment; +} + +size_t bdrv_min_mem_align(BlockDriverState *bs) +{ + if (!bs || !bs->drv) { + /* page size or 4k (hdd sector size) should be on the safe side */ + return MAX(4096, getpagesize()); + } + + return bs->bl.min_mem_alignment; +} + +/* check if the path starts with ":" */ +int path_has_protocol(const char *path) +{ + const char *p; + +#ifdef _WIN32 + if (is_windows_drive(path) || + is_windows_drive_prefix(path)) { + return 0; + } + p = path + strcspn(path, ":/\\"); +#else + p = path + strcspn(path, ":/"); +#endif + + return *p == ':'; +} + +int path_is_absolute(const char *path) +{ +#ifdef _WIN32 + /* specific case for names like: "\\.\d:" */ + if (is_windows_drive(path) || is_windows_drive_prefix(path)) { + return 1; + } + return (*path == '/' || *path == '\\'); +#else + return (*path == '/'); +#endif +} + +/* if filename is absolute, just copy it to dest. Otherwise, build a + path to it by considering it is relative to base_path. URL are + supported. */ +void path_combine(char *dest, int dest_size, + const char *base_path, + const char *filename) +{ + const char *p, *p1; + int len; + + if (dest_size <= 0) + return; + if (path_is_absolute(filename)) { + pstrcpy(dest, dest_size, filename); + } else { + p = strchr(base_path, ':'); + if (p) + p++; + else + p = base_path; + p1 = strrchr(base_path, '/'); +#ifdef _WIN32 + { + const char *p2; + p2 = strrchr(base_path, '\\'); + if (!p1 || p2 > p1) + p1 = p2; + } +#endif + if (p1) + p1++; + else + p1 = base_path; + if (p1 > p) + p = p1; + len = p - base_path; + if (len > dest_size - 1) + len = dest_size - 1; + memcpy(dest, base_path, len); + dest[len] = '\0'; + pstrcat(dest, dest_size, filename); + } +} + +void bdrv_get_full_backing_filename_from_filename(const char *backed, + const char *backing, + char *dest, size_t sz, + Error **errp) +{ + if (backing[0] == '\0' || path_has_protocol(backing) || + path_is_absolute(backing)) + { + pstrcpy(dest, sz, backing); + } else if (backed[0] == '\0' || strstart(backed, "json:", NULL)) { + error_setg(errp, "Cannot use relative backing file names for '%s'", + backed); + } else { + path_combine(dest, sz, backed, backing); + } +} + +void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz, + Error **errp) +{ + char *backed = bs->exact_filename[0] ? bs->exact_filename : bs->filename; + + bdrv_get_full_backing_filename_from_filename(backed, bs->backing_file, + dest, sz, errp); +} + +void bdrv_register(BlockDriver *bdrv) +{ + bdrv_setup_io_funcs(bdrv); + + QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list); +} + +BlockDriverState *bdrv_new_root(void) +{ + BlockDriverState *bs = bdrv_new(); + + QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list); + return bs; +} + +BlockDriverState *bdrv_new(void) +{ + BlockDriverState *bs; + int i; + + bs = g_new0(BlockDriverState, 1); + QLIST_INIT(&bs->dirty_bitmaps); + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + QLIST_INIT(&bs->op_blockers[i]); + } + bdrv_iostatus_disable(bs); + notifier_list_init(&bs->close_notifiers); + notifier_with_return_list_init(&bs->before_write_notifiers); + qemu_co_queue_init(&bs->throttled_reqs[0]); + qemu_co_queue_init(&bs->throttled_reqs[1]); + bs->refcnt = 1; + bs->aio_context = qemu_get_aio_context(); + + return bs; +} + +void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify) +{ + notifier_list_add(&bs->close_notifiers, notify); +} + +BlockDriver *bdrv_find_format(const char *format_name) +{ + BlockDriver *drv1; + QLIST_FOREACH(drv1, &bdrv_drivers, list) { + if (!strcmp(drv1->format_name, format_name)) { + return drv1; + } + } + return NULL; +} + +static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) +{ + static const char *whitelist_rw[] = { + CONFIG_BDRV_RW_WHITELIST + }; + static const char *whitelist_ro[] = { + CONFIG_BDRV_RO_WHITELIST + }; + const char **p; + + if (!whitelist_rw[0] && !whitelist_ro[0]) { + return 1; /* no whitelist, anything goes */ + } + + for (p = whitelist_rw; *p; p++) { + if (!strcmp(drv->format_name, *p)) { + return 1; + } + } + if (read_only) { + for (p = whitelist_ro; *p; p++) { + if (!strcmp(drv->format_name, *p)) { + return 1; + } + } + } + return 0; +} + +BlockDriver *bdrv_find_whitelisted_format(const char *format_name, + bool read_only) +{ + BlockDriver *drv = bdrv_find_format(format_name); + return drv && bdrv_is_whitelisted(drv, read_only) ? drv : NULL; +} + +typedef struct CreateCo { + BlockDriver *drv; + char *filename; + QemuOpts *opts; + int ret; + Error *err; +} CreateCo; + +static void coroutine_fn bdrv_create_co_entry(void *opaque) +{ + Error *local_err = NULL; + int ret; + + CreateCo *cco = opaque; + assert(cco->drv); + + ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err); + if (local_err) { + error_propagate(&cco->err, local_err); + } + cco->ret = ret; +} + +int bdrv_create(BlockDriver *drv, const char* filename, + QemuOpts *opts, Error **errp) +{ + int ret; + + Coroutine *co; + CreateCo cco = { + .drv = drv, + .filename = g_strdup(filename), + .opts = opts, + .ret = NOT_DONE, + .err = NULL, + }; + + if (!drv->bdrv_create) { + error_setg(errp, "Driver '%s' does not support image creation", drv->format_name); + ret = -ENOTSUP; + goto out; + } + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_create_co_entry(&cco); + } else { + co = qemu_coroutine_create(bdrv_create_co_entry); + qemu_coroutine_enter(co, &cco); + while (cco.ret == NOT_DONE) { + aio_poll(qemu_get_aio_context(), true); + } + } + + ret = cco.ret; + if (ret < 0) { + if (cco.err) { + error_propagate(errp, cco.err); + } else { + error_setg_errno(errp, -ret, "Could not create image"); + } + } + +out: + g_free(cco.filename); + return ret; +} + +int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) +{ + BlockDriver *drv; + Error *local_err = NULL; + int ret; + + drv = bdrv_find_protocol(filename, true, errp); + if (drv == NULL) { + return -ENOENT; + } + + ret = bdrv_create(drv, filename, opts, &local_err); + if (local_err) { + error_propagate(errp, local_err); + } + return ret; +} + +/** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz struct and return 0. + * On failure return -errno. + * @bs must not be empty. + */ +int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_probe_blocksizes) { + return drv->bdrv_probe_blocksizes(bs, bsz); + } + + return -ENOTSUP; +} + +/** + * Try to get @bs's geometry (cyls, heads, sectors). + * On success, store them in @geo struct and return 0. + * On failure return -errno. + * @bs must not be empty. + */ +int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_probe_geometry) { + return drv->bdrv_probe_geometry(bs, geo); + } + + return -ENOTSUP; +} + +/* + * Create a uniquely-named empty temporary file. + * Return 0 upon success, otherwise a negative errno value. + */ +int get_tmp_filename(char *filename, int size) +{ +#ifdef _WIN32 + char temp_dir[MAX_PATH]; + /* GetTempFileName requires that its output buffer (4th param) + have length MAX_PATH or greater. */ + assert(size >= MAX_PATH); + return (GetTempPath(MAX_PATH, temp_dir) + && GetTempFileName(temp_dir, "qem", 0, filename) + ? 0 : -GetLastError()); +#else + int fd; + const char *tmpdir; + tmpdir = getenv("TMPDIR"); + if (!tmpdir) { + tmpdir = "/var/tmp"; + } + if (snprintf(filename, size, "%s/vl.XXXXXX", tmpdir) >= size) { + return -EOVERFLOW; + } + fd = mkstemp(filename); + if (fd < 0) { + return -errno; + } + if (close(fd) != 0) { + unlink(filename); + return -errno; + } + return 0; +#endif +} + +/* + * Detect host devices. By convention, /dev/cdrom[N] is always + * recognized as a host CDROM. + */ +static BlockDriver *find_hdev_driver(const char *filename) +{ + int score_max = 0, score; + BlockDriver *drv = NULL, *d; + + QLIST_FOREACH(d, &bdrv_drivers, list) { + if (d->bdrv_probe_device) { + score = d->bdrv_probe_device(filename); + if (score > score_max) { + score_max = score; + drv = d; + } + } + } + + return drv; +} + +BlockDriver *bdrv_find_protocol(const char *filename, + bool allow_protocol_prefix, + Error **errp) +{ + BlockDriver *drv1; + char protocol[128]; + int len; + const char *p; + + /* TODO Drivers without bdrv_file_open must be specified explicitly */ + + /* + * XXX(hch): we really should not let host device detection + * override an explicit protocol specification, but moving this + * later breaks access to device names with colons in them. + * Thanks to the brain-dead persistent naming schemes on udev- + * based Linux systems those actually are quite common. + */ + drv1 = find_hdev_driver(filename); + if (drv1) { + return drv1; + } + + if (!path_has_protocol(filename) || !allow_protocol_prefix) { + return &bdrv_file; + } + + p = strchr(filename, ':'); + assert(p != NULL); + len = p - filename; + if (len > sizeof(protocol) - 1) + len = sizeof(protocol) - 1; + memcpy(protocol, filename, len); + protocol[len] = '\0'; + QLIST_FOREACH(drv1, &bdrv_drivers, list) { + if (drv1->protocol_name && + !strcmp(drv1->protocol_name, protocol)) { + return drv1; + } + } + + error_setg(errp, "Unknown protocol '%s'", protocol); + return NULL; +} + +/* + * Guess image format by probing its contents. + * This is not a good idea when your image is raw (CVE-2008-2004), but + * we do it anyway for backward compatibility. + * + * @buf contains the image's first @buf_size bytes. + * @buf_size is the buffer size in bytes (generally BLOCK_PROBE_BUF_SIZE, + * but can be smaller if the image file is smaller) + * @filename is its filename. + * + * For all block drivers, call the bdrv_probe() method to get its + * probing score. + * Return the first block driver with the highest probing score. + */ +BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, + const char *filename) +{ + int score_max = 0, score; + BlockDriver *drv = NULL, *d; + + QLIST_FOREACH(d, &bdrv_drivers, list) { + if (d->bdrv_probe) { + score = d->bdrv_probe(buf, buf_size, filename); + if (score > score_max) { + score_max = score; + drv = d; + } + } + } + + return drv; +} + +static int find_image_format(BlockDriverState *bs, const char *filename, + BlockDriver **pdrv, Error **errp) +{ + BlockDriver *drv; + uint8_t buf[BLOCK_PROBE_BUF_SIZE]; + int ret = 0; + + /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ + if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) { + *pdrv = &bdrv_raw; + return ret; + } + + ret = bdrv_pread(bs, 0, buf, sizeof(buf)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read image for determining its " + "format"); + *pdrv = NULL; + return ret; + } + + drv = bdrv_probe_all(buf, ret, filename); + if (!drv) { + error_setg(errp, "Could not determine image format: No compatible " + "driver found"); + ret = -ENOENT; + } + *pdrv = drv; + return ret; +} + +/** + * Set the current 'total_sectors' value + * Return 0 on success, -errno on error. + */ +static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) +{ + BlockDriver *drv = bs->drv; + + /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */ + if (bs->sg) + return 0; + + /* query actual device if possible, otherwise just trust the hint */ + if (drv->bdrv_getlength) { + int64_t length = drv->bdrv_getlength(bs); + if (length < 0) { + return length; + } + hint = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE); + } + + bs->total_sectors = hint; + return 0; +} + +/** + * Set open flags for a given discard mode + * + * Return 0 on success, -1 if the discard mode was invalid. + */ +int bdrv_parse_discard_flags(const char *mode, int *flags) +{ + *flags &= ~BDRV_O_UNMAP; + + if (!strcmp(mode, "off") || !strcmp(mode, "ignore")) { + /* do nothing */ + } else if (!strcmp(mode, "on") || !strcmp(mode, "unmap")) { + *flags |= BDRV_O_UNMAP; + } else { + return -1; + } + + return 0; +} + +/** + * Set open flags for a given cache mode + * + * Return 0 on success, -1 if the cache mode was invalid. + */ +int bdrv_parse_cache_flags(const char *mode, int *flags) +{ + *flags &= ~BDRV_O_CACHE_MASK; + + if (!strcmp(mode, "off") || !strcmp(mode, "none")) { + *flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; + } else if (!strcmp(mode, "directsync")) { + *flags |= BDRV_O_NOCACHE; + } else if (!strcmp(mode, "writeback")) { + *flags |= BDRV_O_CACHE_WB; + } else if (!strcmp(mode, "unsafe")) { + *flags |= BDRV_O_CACHE_WB; + *flags |= BDRV_O_NO_FLUSH; + } else if (!strcmp(mode, "writethrough")) { + /* this is the default */ + } else { + return -1; + } + + return 0; +} + +/* + * Returns the flags that a temporary snapshot should get, based on the + * originally requested flags (the originally requested image will have flags + * like a backing file) + */ +static int bdrv_temp_snapshot_flags(int flags) +{ + return (flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY; +} + +/* + * Returns the flags that bs->file should get, based on the given flags for + * the parent BDS + */ +static int bdrv_inherited_flags(int flags) +{ + /* Enable protocol handling, disable format probing for bs->file */ + flags |= BDRV_O_PROTOCOL; + + /* Our block drivers take care to send flushes and respect unmap policy, + * so we can enable both unconditionally on lower layers. */ + flags |= BDRV_O_CACHE_WB | BDRV_O_UNMAP; + + /* Clear flags that only apply to the top layer */ + flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ); + + return flags; +} + +/* + * Returns the flags that bs->backing_hd should get, based on the given flags + * for the parent BDS + */ +static int bdrv_backing_flags(int flags) +{ + /* backing files always opened read-only */ + flags &= ~(BDRV_O_RDWR | BDRV_O_COPY_ON_READ); + + /* snapshot=on is handled on the top layer */ + flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_TEMPORARY); + + return flags; +} + +static int bdrv_open_flags(BlockDriverState *bs, int flags) +{ + int open_flags = flags | BDRV_O_CACHE_WB; + + /* + * Clear flags that are internal to the block layer before opening the + * image. + */ + open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL); + + /* + * Snapshots should be writable. + */ + if (flags & BDRV_O_TEMPORARY) { + open_flags |= BDRV_O_RDWR; + } + + return open_flags; +} + +static void bdrv_assign_node_name(BlockDriverState *bs, + const char *node_name, + Error **errp) +{ + if (!node_name) { + return; + } + + /* Check for empty string or invalid characters */ + if (!id_wellformed(node_name)) { + error_setg(errp, "Invalid node name"); + return; + } + + /* takes care of avoiding namespaces collisions */ + if (blk_by_name(node_name)) { + error_setg(errp, "node-name=%s is conflicting with a device id", + node_name); + return; + } + + /* takes care of avoiding duplicates node names */ + if (bdrv_find_node(node_name)) { + error_setg(errp, "Duplicate node name"); + return; + } + + /* copy node name into the bs and insert it into the graph list */ + pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); + QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); +} + +/* + * Common part for opening disk images and files + * + * Removes all processed options from *options. + */ +static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, + QDict *options, int flags, BlockDriver *drv, Error **errp) +{ + int ret, open_flags; + const char *filename; + const char *node_name = NULL; + Error *local_err = NULL; + + assert(drv != NULL); + assert(bs->file == NULL); + assert(options != NULL && bs->options != options); + + if (file != NULL) { + filename = file->filename; + } else { + filename = qdict_get_try_str(options, "filename"); + } + + if (drv->bdrv_needs_filename && !filename) { + error_setg(errp, "The '%s' block driver requires a file name", + drv->format_name); + return -EINVAL; + } + + trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name); + + node_name = qdict_get_try_str(options, "node-name"); + bdrv_assign_node_name(bs, node_name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + qdict_del(options, "node-name"); + + /* bdrv_open() with directly using a protocol as drv. This layer is already + * opened, so assign it to bs (while file becomes a closed BlockDriverState) + * and return immediately. */ + if (file != NULL && drv->bdrv_file_open) { + bdrv_swap(file, bs); + return 0; + } + + bs->open_flags = flags; + bs->guest_block_size = 512; + bs->request_alignment = 512; + bs->zero_beyond_eof = true; + open_flags = bdrv_open_flags(bs, flags); + bs->read_only = !(open_flags & BDRV_O_RDWR); + + if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { + error_setg(errp, + !bs->read_only && bdrv_is_whitelisted(drv, true) + ? "Driver '%s' can only be used for read-only devices" + : "Driver '%s' is not whitelisted", + drv->format_name); + return -ENOTSUP; + } + + assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */ + if (flags & BDRV_O_COPY_ON_READ) { + if (!bs->read_only) { + bdrv_enable_copy_on_read(bs); + } else { + error_setg(errp, "Can't use copy-on-read on read-only device"); + return -EINVAL; + } + } + + if (filename != NULL) { + pstrcpy(bs->filename, sizeof(bs->filename), filename); + } else { + bs->filename[0] = '\0'; + } + pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->filename); + + bs->drv = drv; + bs->opaque = g_malloc0(drv->instance_size); + + bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB); + + /* Open the image, either directly or using a protocol */ + if (drv->bdrv_file_open) { + assert(file == NULL); + assert(!drv->bdrv_needs_filename || filename != NULL); + ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); + } else { + if (file == NULL) { + error_setg(errp, "Can't use '%s' as a block driver for the " + "protocol level", drv->format_name); + ret = -EINVAL; + goto free_and_fail; + } + bs->file = file; + ret = drv->bdrv_open(bs, options, open_flags, &local_err); + } + + if (ret < 0) { + if (local_err) { + error_propagate(errp, local_err); + } else if (bs->filename[0]) { + error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename); + } else { + error_setg_errno(errp, -ret, "Could not open image"); + } + goto free_and_fail; + } + + if (bs->encrypted) { + error_report("Encrypted images are deprecated"); + error_printf("Support for them will be removed in a future release.\n" + "You can use 'qemu-img convert' to convert your image" + " to an unencrypted one.\n"); + } + + ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + goto free_and_fail; + } + + bdrv_refresh_limits(bs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto free_and_fail; + } + + assert(bdrv_opt_mem_align(bs) != 0); + assert(bdrv_min_mem_align(bs) != 0); + assert((bs->request_alignment != 0) || bs->sg); + return 0; + +free_and_fail: + bs->file = NULL; + g_free(bs->opaque); + bs->opaque = NULL; + bs->drv = NULL; + return ret; +} + +static QDict *parse_json_filename(const char *filename, Error **errp) +{ + QObject *options_obj; + QDict *options; + int ret; + + ret = strstart(filename, "json:", &filename); + assert(ret); + + options_obj = qobject_from_json(filename); + if (!options_obj) { + error_setg(errp, "Could not parse the JSON options"); + return NULL; + } + + if (qobject_type(options_obj) != QTYPE_QDICT) { + qobject_decref(options_obj); + error_setg(errp, "Invalid JSON object given"); + return NULL; + } + + options = qobject_to_qdict(options_obj); + qdict_flatten(options); + + return options; +} + +/* + * Fills in default options for opening images and converts the legacy + * filename/flags pair to option QDict entries. + */ +static int bdrv_fill_options(QDict **options, const char **pfilename, int flags, + BlockDriver *drv, Error **errp) +{ + const char *filename = *pfilename; + const char *drvname; + bool protocol = flags & BDRV_O_PROTOCOL; + bool parse_filename = false; + Error *local_err = NULL; + + /* Parse json: pseudo-protocol */ + if (filename && g_str_has_prefix(filename, "json:")) { + QDict *json_options = parse_json_filename(filename, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + + /* Options given in the filename have lower priority than options + * specified directly */ + qdict_join(*options, json_options, false); + QDECREF(json_options); + *pfilename = filename = NULL; + } + + /* Fetch the file name from the options QDict if necessary */ + if (protocol && filename) { + if (!qdict_haskey(*options, "filename")) { + qdict_put(*options, "filename", qstring_from_str(filename)); + parse_filename = true; + } else { + error_setg(errp, "Can't specify 'file' and 'filename' options at " + "the same time"); + return -EINVAL; + } + } + + /* Find the right block driver */ + filename = qdict_get_try_str(*options, "filename"); + drvname = qdict_get_try_str(*options, "driver"); + + if (drv) { + if (drvname) { + error_setg(errp, "Driver specified twice"); + return -EINVAL; + } + drvname = drv->format_name; + qdict_put(*options, "driver", qstring_from_str(drvname)); + } else { + if (!drvname && protocol) { + if (filename) { + drv = bdrv_find_protocol(filename, parse_filename, errp); + if (!drv) { + return -EINVAL; + } + + drvname = drv->format_name; + qdict_put(*options, "driver", qstring_from_str(drvname)); + } else { + error_setg(errp, "Must specify either driver or file"); + return -EINVAL; + } + } else if (drvname) { + drv = bdrv_find_format(drvname); + if (!drv) { + error_setg(errp, "Unknown driver '%s'", drvname); + return -ENOENT; + } + } + } + + assert(drv || !protocol); + + /* Driver-specific filename parsing */ + if (drv && drv->bdrv_parse_filename && parse_filename) { + drv->bdrv_parse_filename(filename, *options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + + if (!drv->bdrv_needs_filename) { + qdict_del(*options, "filename"); + } + } + + return 0; +} + +void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd) +{ + + if (bs->backing_hd) { + assert(bs->backing_blocker); + bdrv_op_unblock_all(bs->backing_hd, bs->backing_blocker); + } else if (backing_hd) { + error_setg(&bs->backing_blocker, + "node is used as backing hd of '%s'", + bdrv_get_device_or_node_name(bs)); + } + + bs->backing_hd = backing_hd; + if (!backing_hd) { + error_free(bs->backing_blocker); + bs->backing_blocker = NULL; + goto out; + } + bs->open_flags &= ~BDRV_O_NO_BACKING; + pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename); + pstrcpy(bs->backing_format, sizeof(bs->backing_format), + backing_hd->drv ? backing_hd->drv->format_name : ""); + + bdrv_op_block_all(bs->backing_hd, bs->backing_blocker); + /* Otherwise we won't be able to commit due to check in bdrv_commit */ + bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, + bs->backing_blocker); +out: + bdrv_refresh_limits(bs, NULL); +} + +/* + * Opens the backing file for a BlockDriverState if not yet open + * + * options is a QDict of options to pass to the block drivers, or NULL for an + * empty set of options. The reference to the QDict is transferred to this + * function (even on failure), so if the caller intends to reuse the dictionary, + * it needs to use QINCREF() before calling bdrv_file_open. + */ +int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) +{ + char *backing_filename = g_malloc0(PATH_MAX); + int ret = 0; + BlockDriverState *backing_hd; + Error *local_err = NULL; + + if (bs->backing_hd != NULL) { + QDECREF(options); + goto free_exit; + } + + /* NULL means an empty set of options */ + if (options == NULL) { + options = qdict_new(); + } + + bs->open_flags &= ~BDRV_O_NO_BACKING; + if (qdict_haskey(options, "file.filename")) { + backing_filename[0] = '\0'; + } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { + QDECREF(options); + goto free_exit; + } else { + bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX, + &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + QDECREF(options); + goto free_exit; + } + } + + if (!bs->drv || !bs->drv->supports_backing) { + ret = -EINVAL; + error_setg(errp, "Driver doesn't support backing files"); + QDECREF(options); + goto free_exit; + } + + backing_hd = bdrv_new(); + + if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) { + qdict_put(options, "driver", qstring_from_str(bs->backing_format)); + } + + assert(bs->backing_hd == NULL); + ret = bdrv_open(&backing_hd, + *backing_filename ? backing_filename : NULL, NULL, options, + bdrv_backing_flags(bs->open_flags), NULL, &local_err); + if (ret < 0) { + bdrv_unref(backing_hd); + backing_hd = NULL; + bs->open_flags |= BDRV_O_NO_BACKING; + error_setg(errp, "Could not open backing file: %s", + error_get_pretty(local_err)); + error_free(local_err); + goto free_exit; + } + bdrv_set_backing_hd(bs, backing_hd); + +free_exit: + g_free(backing_filename); + return ret; +} + +/* + * Opens a disk image whose options are given as BlockdevRef in another block + * device's options. + * + * If allow_none is true, no image will be opened if filename is false and no + * BlockdevRef is given. *pbs will remain unchanged and 0 will be returned. + * + * bdrev_key specifies the key for the image's BlockdevRef in the options QDict. + * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict + * itself, all options starting with "${bdref_key}." are considered part of the + * BlockdevRef. + * + * The BlockdevRef will be removed from the options QDict. + * + * To conform with the behavior of bdrv_open(), *pbs has to be NULL. + */ +int bdrv_open_image(BlockDriverState **pbs, const char *filename, + QDict *options, const char *bdref_key, int flags, + bool allow_none, Error **errp) +{ + QDict *image_options; + int ret; + char *bdref_key_dot; + const char *reference; + + assert(pbs); + assert(*pbs == NULL); + + bdref_key_dot = g_strdup_printf("%s.", bdref_key); + qdict_extract_subqdict(options, &image_options, bdref_key_dot); + g_free(bdref_key_dot); + + reference = qdict_get_try_str(options, bdref_key); + if (!filename && !reference && !qdict_size(image_options)) { + if (allow_none) { + ret = 0; + } else { + error_setg(errp, "A block device must be specified for \"%s\"", + bdref_key); + ret = -EINVAL; + } + QDECREF(image_options); + goto done; + } + + ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp); + +done: + qdict_del(options, bdref_key); + return ret; +} + +int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) +{ + /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ + char *tmp_filename = g_malloc0(PATH_MAX + 1); + int64_t total_size; + QemuOpts *opts = NULL; + QDict *snapshot_options; + BlockDriverState *bs_snapshot; + Error *local_err; + int ret; + + /* if snapshot, we create a temporary backing file and open it + instead of opening 'filename' directly */ + + /* Get the required size from the image */ + total_size = bdrv_getlength(bs); + if (total_size < 0) { + ret = total_size; + error_setg_errno(errp, -total_size, "Could not get image size"); + goto out; + } + + /* Create the temporary image */ + ret = get_tmp_filename(tmp_filename, PATH_MAX + 1); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not get temporary filename"); + goto out; + } + + opts = qemu_opts_create(bdrv_qcow2.create_opts, NULL, 0, + &error_abort); + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_size, &error_abort); + ret = bdrv_create(&bdrv_qcow2, tmp_filename, opts, &local_err); + qemu_opts_del(opts); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not create temporary overlay " + "'%s': %s", tmp_filename, + error_get_pretty(local_err)); + error_free(local_err); + goto out; + } + + /* Prepare a new options QDict for the temporary file */ + snapshot_options = qdict_new(); + qdict_put(snapshot_options, "file.driver", + qstring_from_str("file")); + qdict_put(snapshot_options, "file.filename", + qstring_from_str(tmp_filename)); + + bs_snapshot = bdrv_new(); + + ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options, + flags, &bdrv_qcow2, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto out; + } + + bdrv_append(bs_snapshot, bs); + +out: + g_free(tmp_filename); + return ret; +} + +/* + * Opens a disk image (raw, qcow2, vmdk, ...) + * + * options is a QDict of options to pass to the block drivers, or NULL for an + * empty set of options. The reference to the QDict belongs to the block layer + * after the call (even on failure), so if the caller intends to reuse the + * dictionary, it needs to use QINCREF() before calling bdrv_open. + * + * If *pbs is NULL, a new BDS will be created with a pointer to it stored there. + * If it is not NULL, the referenced BDS will be reused. + * + * The reference parameter may be used to specify an existing block device which + * should be opened. If specified, neither options nor a filename may be given, + * nor can an existing BDS be reused (that is, *pbs has to be NULL). + */ +int bdrv_open(BlockDriverState **pbs, const char *filename, + const char *reference, QDict *options, int flags, + BlockDriver *drv, Error **errp) +{ + int ret; + BlockDriverState *file = NULL, *bs; + const char *drvname; + Error *local_err = NULL; + int snapshot_flags = 0; + + assert(pbs); + + if (reference) { + bool options_non_empty = options ? qdict_size(options) : false; + QDECREF(options); + + if (*pbs) { + error_setg(errp, "Cannot reuse an existing BDS when referencing " + "another block device"); + return -EINVAL; + } + + if (filename || options_non_empty) { + error_setg(errp, "Cannot reference an existing block device with " + "additional options or a new filename"); + return -EINVAL; + } + + bs = bdrv_lookup_bs(reference, reference, errp); + if (!bs) { + return -ENODEV; + } + bdrv_ref(bs); + *pbs = bs; + return 0; + } + + if (*pbs) { + bs = *pbs; + } else { + bs = bdrv_new(); + } + + /* NULL means an empty set of options */ + if (options == NULL) { + options = qdict_new(); + } + + ret = bdrv_fill_options(&options, &filename, flags, drv, &local_err); + if (local_err) { + goto fail; + } + + /* Find the right image format driver */ + drv = NULL; + drvname = qdict_get_try_str(options, "driver"); + if (drvname) { + drv = bdrv_find_format(drvname); + qdict_del(options, "driver"); + if (!drv) { + error_setg(errp, "Unknown driver: '%s'", drvname); + ret = -EINVAL; + goto fail; + } + } + + assert(drvname || !(flags & BDRV_O_PROTOCOL)); + if (drv && !drv->bdrv_file_open) { + /* If the user explicitly wants a format driver here, we'll need to add + * another layer for the protocol in bs->file */ + flags &= ~BDRV_O_PROTOCOL; + } + + bs->options = options; + options = qdict_clone_shallow(options); + + /* Open image file without format layer */ + if ((flags & BDRV_O_PROTOCOL) == 0) { + if (flags & BDRV_O_RDWR) { + flags |= BDRV_O_ALLOW_RDWR; + } + if (flags & BDRV_O_SNAPSHOT) { + snapshot_flags = bdrv_temp_snapshot_flags(flags); + flags = bdrv_backing_flags(flags); + } + + assert(file == NULL); + ret = bdrv_open_image(&file, filename, options, "file", + bdrv_inherited_flags(flags), + true, &local_err); + if (ret < 0) { + goto fail; + } + } + + /* Image format probing */ + bs->probed = !drv; + if (!drv && file) { + ret = find_image_format(file, filename, &drv, &local_err); + if (ret < 0) { + goto fail; + } + } else if (!drv) { + error_setg(errp, "Must specify either driver or file"); + ret = -EINVAL; + goto fail; + } + + /* Open the image */ + ret = bdrv_open_common(bs, file, options, flags, drv, &local_err); + if (ret < 0) { + goto fail; + } + + if (file && (bs->file != file)) { + bdrv_unref(file); + file = NULL; + } + + /* If there is a backing file, use it */ + if ((flags & BDRV_O_NO_BACKING) == 0) { + QDict *backing_options; + + qdict_extract_subqdict(options, &backing_options, "backing."); + ret = bdrv_open_backing_file(bs, backing_options, &local_err); + if (ret < 0) { + goto close_and_fail; + } + } + + bdrv_refresh_filename(bs); + + /* For snapshot=on, create a temporary qcow2 overlay. bs points to the + * temporary snapshot afterwards. */ + if (snapshot_flags) { + ret = bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err); + if (local_err) { + goto close_and_fail; + } + } + + /* Check if any unknown options were used */ + if (options && (qdict_size(options) != 0)) { + const QDictEntry *entry = qdict_first(options); + if (flags & BDRV_O_PROTOCOL) { + error_setg(errp, "Block protocol '%s' doesn't support the option " + "'%s'", drv->format_name, entry->key); + } else { + error_setg(errp, "Block format '%s' used by device '%s' doesn't " + "support the option '%s'", drv->format_name, + bdrv_get_device_name(bs), entry->key); + } + + ret = -EINVAL; + goto close_and_fail; + } + + if (!bdrv_key_required(bs)) { + if (bs->blk) { + blk_dev_change_media_cb(bs->blk, true); + } + } else if (!runstate_check(RUN_STATE_PRELAUNCH) + && !runstate_check(RUN_STATE_INMIGRATE) + && !runstate_check(RUN_STATE_PAUSED)) { /* HACK */ + error_setg(errp, + "Guest must be stopped for opening of encrypted image"); + ret = -EBUSY; + goto close_and_fail; + } + + QDECREF(options); + *pbs = bs; + return 0; + +fail: + if (file != NULL) { + bdrv_unref(file); + } + QDECREF(bs->options); + QDECREF(options); + bs->options = NULL; + if (!*pbs) { + /* If *pbs is NULL, a new BDS has been created in this function and + needs to be freed now. Otherwise, it does not need to be closed, + since it has not really been opened yet. */ + bdrv_unref(bs); + } + if (local_err) { + error_propagate(errp, local_err); + } + return ret; + +close_and_fail: + /* See fail path, but now the BDS has to be always closed */ + if (*pbs) { + bdrv_close(bs); + } else { + bdrv_unref(bs); + } + QDECREF(options); + if (local_err) { + error_propagate(errp, local_err); + } + return ret; +} + +typedef struct BlockReopenQueueEntry { + bool prepared; + BDRVReopenState state; + QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry; +} BlockReopenQueueEntry; + +/* + * Adds a BlockDriverState to a simple queue for an atomic, transactional + * reopen of multiple devices. + * + * bs_queue can either be an existing BlockReopenQueue that has had QSIMPLE_INIT + * already performed, or alternatively may be NULL a new BlockReopenQueue will + * be created and initialized. This newly created BlockReopenQueue should be + * passed back in for subsequent calls that are intended to be of the same + * atomic 'set'. + * + * bs is the BlockDriverState to add to the reopen queue. + * + * flags contains the open flags for the associated bs + * + * returns a pointer to bs_queue, which is either the newly allocated + * bs_queue, or the existing bs_queue being used. + * + */ +BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, + BlockDriverState *bs, int flags) +{ + assert(bs != NULL); + + BlockReopenQueueEntry *bs_entry; + if (bs_queue == NULL) { + bs_queue = g_new0(BlockReopenQueue, 1); + QSIMPLEQ_INIT(bs_queue); + } + + /* bdrv_open() masks this flag out */ + flags &= ~BDRV_O_PROTOCOL; + + if (bs->file) { + bdrv_reopen_queue(bs_queue, bs->file, bdrv_inherited_flags(flags)); + } + + bs_entry = g_new0(BlockReopenQueueEntry, 1); + QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); + + bs_entry->state.bs = bs; + bs_entry->state.flags = flags; + + return bs_queue; +} + +/* + * Reopen multiple BlockDriverStates atomically & transactionally. + * + * The queue passed in (bs_queue) must have been built up previous + * via bdrv_reopen_queue(). + * + * Reopens all BDS specified in the queue, with the appropriate + * flags. All devices are prepared for reopen, and failure of any + * device will cause all device changes to be abandonded, and intermediate + * data cleaned up. + * + * If all devices prepare successfully, then the changes are committed + * to all devices. + * + */ +int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) +{ + int ret = -1; + BlockReopenQueueEntry *bs_entry, *next; + Error *local_err = NULL; + + assert(bs_queue != NULL); + + bdrv_drain_all(); + + QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) { + error_propagate(errp, local_err); + goto cleanup; + } + bs_entry->prepared = true; + } + + /* If we reach this point, we have success and just need to apply the + * changes + */ + QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + bdrv_reopen_commit(&bs_entry->state); + } + + ret = 0; + +cleanup: + QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { + if (ret && bs_entry->prepared) { + bdrv_reopen_abort(&bs_entry->state); + } + g_free(bs_entry); + } + g_free(bs_queue); + return ret; +} + + +/* Reopen a single BlockDriverState with the specified flags. */ +int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) +{ + int ret = -1; + Error *local_err = NULL; + BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, bdrv_flags); + + ret = bdrv_reopen_multiple(queue, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + } + return ret; +} + + +/* + * Prepares a BlockDriverState for reopen. All changes are staged in the + * 'opaque' field of the BDRVReopenState, which is used and allocated by + * the block driver layer .bdrv_reopen_prepare() + * + * bs is the BlockDriverState to reopen + * flags are the new open flags + * queue is the reopen queue + * + * Returns 0 on success, non-zero on error. On error errp will be set + * as well. + * + * On failure, bdrv_reopen_abort() will be called to clean up any data. + * It is the responsibility of the caller to then call the abort() or + * commit() for any other BDS that have been left in a prepare() state + * + */ +int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, + Error **errp) +{ + int ret = -1; + Error *local_err = NULL; + BlockDriver *drv; + + assert(reopen_state != NULL); + assert(reopen_state->bs->drv != NULL); + drv = reopen_state->bs->drv; + + /* if we are to stay read-only, do not allow permission change + * to r/w */ + if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) && + reopen_state->flags & BDRV_O_RDWR) { + error_setg(errp, "Node '%s' is read only", + bdrv_get_device_or_node_name(reopen_state->bs)); + goto error; + } + + + ret = bdrv_flush(reopen_state->bs); + if (ret) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "Error (%s) flushing drive", + strerror(-ret)); + goto error; + } + + if (drv->bdrv_reopen_prepare) { + ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err); + if (ret) { + if (local_err != NULL) { + error_propagate(errp, local_err); + } else { + error_setg(errp, "failed while preparing to reopen image '%s'", + reopen_state->bs->filename); + } + goto error; + } + } else { + /* It is currently mandatory to have a bdrv_reopen_prepare() + * handler for each supported drv. */ + error_setg(errp, "Block format '%s' used by node '%s' " + "does not support reopening files", drv->format_name, + bdrv_get_device_or_node_name(reopen_state->bs)); + ret = -1; + goto error; + } + + ret = 0; + +error: + return ret; +} + +/* + * Takes the staged changes for the reopen from bdrv_reopen_prepare(), and + * makes them final by swapping the staging BlockDriverState contents into + * the active BlockDriverState contents. + */ +void bdrv_reopen_commit(BDRVReopenState *reopen_state) +{ + BlockDriver *drv; + + assert(reopen_state != NULL); + drv = reopen_state->bs->drv; + assert(drv != NULL); + + /* If there are any driver level actions to take */ + if (drv->bdrv_reopen_commit) { + drv->bdrv_reopen_commit(reopen_state); + } + + /* set BDS specific flags now */ + reopen_state->bs->open_flags = reopen_state->flags; + reopen_state->bs->enable_write_cache = !!(reopen_state->flags & + BDRV_O_CACHE_WB); + reopen_state->bs->read_only = !(reopen_state->flags & BDRV_O_RDWR); + + bdrv_refresh_limits(reopen_state->bs, NULL); +} + +/* + * Abort the reopen, and delete and free the staged changes in + * reopen_state + */ +void bdrv_reopen_abort(BDRVReopenState *reopen_state) +{ + BlockDriver *drv; + + assert(reopen_state != NULL); + drv = reopen_state->bs->drv; + assert(drv != NULL); + + if (drv->bdrv_reopen_abort) { + drv->bdrv_reopen_abort(reopen_state); + } +} + + +void bdrv_close(BlockDriverState *bs) +{ + BdrvAioNotifier *ban, *ban_next; + + if (bs->job) { + block_job_cancel_sync(bs->job); + } + bdrv_drain_all(); /* complete I/O */ + bdrv_flush(bs); + bdrv_drain_all(); /* in case flush left pending I/O */ + notifier_list_notify(&bs->close_notifiers, bs); + + if (bs->drv) { + if (bs->backing_hd) { + BlockDriverState *backing_hd = bs->backing_hd; + bdrv_set_backing_hd(bs, NULL); + bdrv_unref(backing_hd); + } + bs->drv->bdrv_close(bs); + g_free(bs->opaque); + bs->opaque = NULL; + bs->drv = NULL; + bs->copy_on_read = 0; + bs->backing_file[0] = '\0'; + bs->backing_format[0] = '\0'; + bs->total_sectors = 0; + bs->encrypted = 0; + bs->valid_key = 0; + bs->sg = 0; + bs->zero_beyond_eof = false; + QDECREF(bs->options); + bs->options = NULL; + QDECREF(bs->full_open_options); + bs->full_open_options = NULL; + + if (bs->file != NULL) { + bdrv_unref(bs->file); + bs->file = NULL; + } + } + + if (bs->blk) { + blk_dev_change_media_cb(bs->blk, false); + } + + /*throttling disk I/O limits*/ + if (bs->io_limits_enabled) { + bdrv_io_limits_disable(bs); + } + + QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) { + g_free(ban); + } + QLIST_INIT(&bs->aio_notifiers); +} + +void bdrv_close_all(void) +{ + BlockDriverState *bs; + + QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + bdrv_close(bs); + aio_context_release(aio_context); + } +} + +/* make a BlockDriverState anonymous by removing from bdrv_state and + * graph_bdrv_state list. + Also, NULL terminate the device_name to prevent double remove */ +void bdrv_make_anon(BlockDriverState *bs) +{ + /* + * Take care to remove bs from bdrv_states only when it's actually + * in it. Note that bs->device_list.tqe_prev is initially null, + * and gets set to non-null by QTAILQ_INSERT_TAIL(). Establish + * the useful invariant "bs in bdrv_states iff bs->tqe_prev" by + * resetting it to null on remove. + */ + if (bs->device_list.tqe_prev) { + QTAILQ_REMOVE(&bdrv_states, bs, device_list); + bs->device_list.tqe_prev = NULL; + } + if (bs->node_name[0] != '\0') { + QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); + } + bs->node_name[0] = '\0'; +} + +static void bdrv_rebind(BlockDriverState *bs) +{ + if (bs->drv && bs->drv->bdrv_rebind) { + bs->drv->bdrv_rebind(bs); + } +} + +static void bdrv_move_feature_fields(BlockDriverState *bs_dest, + BlockDriverState *bs_src) +{ + /* move some fields that need to stay attached to the device */ + + /* dev info */ + bs_dest->guest_block_size = bs_src->guest_block_size; + bs_dest->copy_on_read = bs_src->copy_on_read; + + bs_dest->enable_write_cache = bs_src->enable_write_cache; + + /* i/o throttled req */ + memcpy(&bs_dest->throttle_state, + &bs_src->throttle_state, + sizeof(ThrottleState)); + bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0]; + bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1]; + bs_dest->io_limits_enabled = bs_src->io_limits_enabled; + + /* r/w error */ + bs_dest->on_read_error = bs_src->on_read_error; + bs_dest->on_write_error = bs_src->on_write_error; + + /* i/o status */ + bs_dest->iostatus_enabled = bs_src->iostatus_enabled; + bs_dest->iostatus = bs_src->iostatus; + + /* dirty bitmap */ + bs_dest->dirty_bitmaps = bs_src->dirty_bitmaps; + + /* reference count */ + bs_dest->refcnt = bs_src->refcnt; + + /* job */ + bs_dest->job = bs_src->job; + + /* keep the same entry in bdrv_states */ + bs_dest->device_list = bs_src->device_list; + bs_dest->blk = bs_src->blk; + + memcpy(bs_dest->op_blockers, bs_src->op_blockers, + sizeof(bs_dest->op_blockers)); +} + +/* + * Swap bs contents for two image chains while they are live, + * while keeping required fields on the BlockDriverState that is + * actually attached to a device. + * + * This will modify the BlockDriverState fields, and swap contents + * between bs_new and bs_old. Both bs_new and bs_old are modified. + * + * bs_new must not be attached to a BlockBackend. + * + * This function does not create any image files. + */ +void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) +{ + BlockDriverState tmp; + + /* The code needs to swap the node_name but simply swapping node_list won't + * work so first remove the nodes from the graph list, do the swap then + * insert them back if needed. + */ + if (bs_new->node_name[0] != '\0') { + QTAILQ_REMOVE(&graph_bdrv_states, bs_new, node_list); + } + if (bs_old->node_name[0] != '\0') { + QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list); + } + + /* bs_new must be unattached and shouldn't have anything fancy enabled */ + assert(!bs_new->blk); + assert(QLIST_EMPTY(&bs_new->dirty_bitmaps)); + assert(bs_new->job == NULL); + assert(bs_new->io_limits_enabled == false); + assert(!throttle_have_timer(&bs_new->throttle_state)); + + tmp = *bs_new; + *bs_new = *bs_old; + *bs_old = tmp; + + /* there are some fields that should not be swapped, move them back */ + bdrv_move_feature_fields(&tmp, bs_old); + bdrv_move_feature_fields(bs_old, bs_new); + bdrv_move_feature_fields(bs_new, &tmp); + + /* bs_new must remain unattached */ + assert(!bs_new->blk); + + /* Check a few fields that should remain attached to the device */ + assert(bs_new->job == NULL); + assert(bs_new->io_limits_enabled == false); + assert(!throttle_have_timer(&bs_new->throttle_state)); + + /* insert the nodes back into the graph node list if needed */ + if (bs_new->node_name[0] != '\0') { + QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_new, node_list); + } + if (bs_old->node_name[0] != '\0') { + QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_old, node_list); + } + + bdrv_rebind(bs_new); + bdrv_rebind(bs_old); +} + +/* + * Add new bs contents at the top of an image chain while the chain is + * live, while keeping required fields on the top layer. + * + * This will modify the BlockDriverState fields, and swap contents + * between bs_new and bs_top. Both bs_new and bs_top are modified. + * + * bs_new must not be attached to a BlockBackend. + * + * This function does not create any image files. + */ +void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) +{ + bdrv_swap(bs_new, bs_top); + + /* The contents of 'tmp' will become bs_top, as we are + * swapping bs_new and bs_top contents. */ + bdrv_set_backing_hd(bs_top, bs_new); +} + +static void bdrv_delete(BlockDriverState *bs) +{ + assert(!bs->job); + assert(bdrv_op_blocker_is_empty(bs)); + assert(!bs->refcnt); + assert(QLIST_EMPTY(&bs->dirty_bitmaps)); + + bdrv_close(bs); + + /* remove from list, if necessary */ + bdrv_make_anon(bs); + + g_free(bs); +} + +/* + * Run consistency checks on an image + * + * Returns 0 if the check could be completed (it doesn't mean that the image is + * free of errors) or -errno when an internal error occurred. The results of the + * check are stored in res. + */ +int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) +{ + if (bs->drv == NULL) { + return -ENOMEDIUM; + } + if (bs->drv->bdrv_check == NULL) { + return -ENOTSUP; + } + + memset(res, 0, sizeof(*res)); + return bs->drv->bdrv_check(bs, res, fix); +} + +#define COMMIT_BUF_SECTORS 2048 + +/* commit COW file into the raw image */ +int bdrv_commit(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + int64_t sector, total_sectors, length, backing_length; + int n, ro, open_flags; + int ret = 0; + uint8_t *buf = NULL; + + if (!drv) + return -ENOMEDIUM; + + if (!bs->backing_hd) { + return -ENOTSUP; + } + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || + bdrv_op_is_blocked(bs->backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { + return -EBUSY; + } + + ro = bs->backing_hd->read_only; + open_flags = bs->backing_hd->open_flags; + + if (ro) { + if (bdrv_reopen(bs->backing_hd, open_flags | BDRV_O_RDWR, NULL)) { + return -EACCES; + } + } + + length = bdrv_getlength(bs); + if (length < 0) { + ret = length; + goto ro_cleanup; + } + + backing_length = bdrv_getlength(bs->backing_hd); + if (backing_length < 0) { + ret = backing_length; + goto ro_cleanup; + } + + /* If our top snapshot is larger than the backing file image, + * grow the backing file image if possible. If not possible, + * we must return an error */ + if (length > backing_length) { + ret = bdrv_truncate(bs->backing_hd, length); + if (ret < 0) { + goto ro_cleanup; + } + } + + total_sectors = length >> BDRV_SECTOR_BITS; + + /* qemu_try_blockalign() for bs will choose an alignment that works for + * bs->backing_hd as well, so no need to compare the alignment manually. */ + buf = qemu_try_blockalign(bs, COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE); + if (buf == NULL) { + ret = -ENOMEM; + goto ro_cleanup; + } + + for (sector = 0; sector < total_sectors; sector += n) { + ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n); + if (ret < 0) { + goto ro_cleanup; + } + if (ret) { + ret = bdrv_read(bs, sector, buf, n); + if (ret < 0) { + goto ro_cleanup; + } + + ret = bdrv_write(bs->backing_hd, sector, buf, n); + if (ret < 0) { + goto ro_cleanup; + } + } + } + + if (drv->bdrv_make_empty) { + ret = drv->bdrv_make_empty(bs); + if (ret < 0) { + goto ro_cleanup; + } + bdrv_flush(bs); + } + + /* + * Make sure all data we wrote to the backing device is actually + * stable on disk. + */ + if (bs->backing_hd) { + bdrv_flush(bs->backing_hd); + } + + ret = 0; +ro_cleanup: + qemu_vfree(buf); + + if (ro) { + /* ignoring error return here */ + bdrv_reopen(bs->backing_hd, open_flags & ~BDRV_O_RDWR, NULL); + } + + return ret; +} + +int bdrv_commit_all(void) +{ + BlockDriverState *bs; + + QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + if (bs->drv && bs->backing_hd) { + int ret = bdrv_commit(bs); + if (ret < 0) { + aio_context_release(aio_context); + return ret; + } + } + aio_context_release(aio_context); + } + return 0; +} + +/* + * Return values: + * 0 - success + * -EINVAL - backing format specified, but no file + * -ENOSPC - can't update the backing file because no space is left in the + * image file header + * -ENOTSUP - format driver doesn't support changing the backing file + */ +int bdrv_change_backing_file(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt) +{ + BlockDriver *drv = bs->drv; + int ret; + + /* Backing file format doesn't make sense without a backing file */ + if (backing_fmt && !backing_file) { + return -EINVAL; + } + + if (drv->bdrv_change_backing_file != NULL) { + ret = drv->bdrv_change_backing_file(bs, backing_file, backing_fmt); + } else { + ret = -ENOTSUP; + } + + if (ret == 0) { + pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); + pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); + } + return ret; +} + +/* + * Finds the image layer in the chain that has 'bs' as its backing file. + * + * active is the current topmost image. + * + * Returns NULL if bs is not found in active's image chain, + * or if active == bs. + * + * Returns the bottommost base image if bs == NULL. + */ +BlockDriverState *bdrv_find_overlay(BlockDriverState *active, + BlockDriverState *bs) +{ + while (active && bs != active->backing_hd) { + active = active->backing_hd; + } + + return active; +} + +/* Given a BDS, searches for the base layer. */ +BlockDriverState *bdrv_find_base(BlockDriverState *bs) +{ + return bdrv_find_overlay(bs, NULL); +} + +typedef struct BlkIntermediateStates { + BlockDriverState *bs; + QSIMPLEQ_ENTRY(BlkIntermediateStates) entry; +} BlkIntermediateStates; + + +/* + * Drops images above 'base' up to and including 'top', and sets the image + * above 'top' to have base as its backing file. + * + * Requires that the overlay to 'top' is opened r/w, so that the backing file + * information in 'bs' can be properly updated. + * + * E.g., this will convert the following chain: + * bottom <- base <- intermediate <- top <- active + * + * to + * + * bottom <- base <- active + * + * It is allowed for bottom==base, in which case it converts: + * + * base <- intermediate <- top <- active + * + * to + * + * base <- active + * + * If backing_file_str is non-NULL, it will be used when modifying top's + * overlay image metadata. + * + * Error conditions: + * if active == top, that is considered an error + * + */ +int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, + BlockDriverState *base, const char *backing_file_str) +{ + BlockDriverState *intermediate; + BlockDriverState *base_bs = NULL; + BlockDriverState *new_top_bs = NULL; + BlkIntermediateStates *intermediate_state, *next; + int ret = -EIO; + + QSIMPLEQ_HEAD(states_to_delete, BlkIntermediateStates) states_to_delete; + QSIMPLEQ_INIT(&states_to_delete); + + if (!top->drv || !base->drv) { + goto exit; + } + + new_top_bs = bdrv_find_overlay(active, top); + + if (new_top_bs == NULL) { + /* we could not find the image above 'top', this is an error */ + goto exit; + } + + /* special case of new_top_bs->backing_hd already pointing to base - nothing + * to do, no intermediate images */ + if (new_top_bs->backing_hd == base) { + ret = 0; + goto exit; + } + + intermediate = top; + + /* now we will go down through the list, and add each BDS we find + * into our deletion queue, until we hit the 'base' + */ + while (intermediate) { + intermediate_state = g_new0(BlkIntermediateStates, 1); + intermediate_state->bs = intermediate; + QSIMPLEQ_INSERT_TAIL(&states_to_delete, intermediate_state, entry); + + if (intermediate->backing_hd == base) { + base_bs = intermediate->backing_hd; + break; + } + intermediate = intermediate->backing_hd; + } + if (base_bs == NULL) { + /* something went wrong, we did not end at the base. safely + * unravel everything, and exit with error */ + goto exit; + } + + /* success - we can delete the intermediate states, and link top->base */ + backing_file_str = backing_file_str ? backing_file_str : base_bs->filename; + ret = bdrv_change_backing_file(new_top_bs, backing_file_str, + base_bs->drv ? base_bs->drv->format_name : ""); + if (ret) { + goto exit; + } + bdrv_set_backing_hd(new_top_bs, base_bs); + + QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { + /* so that bdrv_close() does not recursively close the chain */ + bdrv_set_backing_hd(intermediate_state->bs, NULL); + bdrv_unref(intermediate_state->bs); + } + ret = 0; + +exit: + QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { + g_free(intermediate_state); + } + return ret; +} + +/** + * Truncate file to 'offset' bytes (needed only for file protocols) + */ +int bdrv_truncate(BlockDriverState *bs, int64_t offset) +{ + BlockDriver *drv = bs->drv; + int ret; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_truncate) + return -ENOTSUP; + if (bs->read_only) + return -EACCES; + + ret = drv->bdrv_truncate(bs, offset); + if (ret == 0) { + ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + bdrv_dirty_bitmap_truncate(bs); + if (bs->blk) { + blk_dev_resize_cb(bs->blk); + } + } + return ret; +} + +/** + * Length of a allocated file in bytes. Sparse files are counted by actual + * allocated space. Return < 0 if error or unknown. + */ +int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_get_allocated_file_size) { + return drv->bdrv_get_allocated_file_size(bs); + } + if (bs->file) { + return bdrv_get_allocated_file_size(bs->file); + } + return -ENOTSUP; +} + +/** + * Return number of sectors on success, -errno on error. + */ +int64_t bdrv_nb_sectors(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + + if (!drv) + return -ENOMEDIUM; + + if (drv->has_variable_length) { + int ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + return ret; + } + } + return bs->total_sectors; +} + +/** + * Return length in bytes on success, -errno on error. + * The length is always a multiple of BDRV_SECTOR_SIZE. + */ +int64_t bdrv_getlength(BlockDriverState *bs) +{ + int64_t ret = bdrv_nb_sectors(bs); + + ret = ret > INT64_MAX / BDRV_SECTOR_SIZE ? -EFBIG : ret; + return ret < 0 ? ret : ret * BDRV_SECTOR_SIZE; +} + +/* return 0 as number of sectors if no device present or error */ +void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) +{ + int64_t nb_sectors = bdrv_nb_sectors(bs); + + *nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors; +} + +void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, + BlockdevOnError on_write_error) +{ + bs->on_read_error = on_read_error; + bs->on_write_error = on_write_error; +} + +BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, bool is_read) +{ + return is_read ? bs->on_read_error : bs->on_write_error; +} + +BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int error) +{ + BlockdevOnError on_err = is_read ? bs->on_read_error : bs->on_write_error; + + switch (on_err) { + case BLOCKDEV_ON_ERROR_ENOSPC: + return (error == ENOSPC) ? + BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_STOP: + return BLOCK_ERROR_ACTION_STOP; + case BLOCKDEV_ON_ERROR_REPORT: + return BLOCK_ERROR_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_IGNORE: + return BLOCK_ERROR_ACTION_IGNORE; + default: + abort(); + } +} + +static void send_qmp_error_event(BlockDriverState *bs, + BlockErrorAction action, + bool is_read, int error) +{ + IoOperationType optype; + + optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; + qapi_event_send_block_io_error(bdrv_get_device_name(bs), optype, action, + bdrv_iostatus_is_enabled(bs), + error == ENOSPC, strerror(error), + &error_abort); +} + +/* This is done by device models because, while the block layer knows + * about the error, it does not know whether an operation comes from + * the device or the block layer (from a job, for example). + */ +void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, + bool is_read, int error) +{ + assert(error >= 0); + + if (action == BLOCK_ERROR_ACTION_STOP) { + /* First set the iostatus, so that "info block" returns an iostatus + * that matches the events raised so far (an additional error iostatus + * is fine, but not a lost one). + */ + bdrv_iostatus_set_err(bs, error); + + /* Then raise the request to stop the VM and the event. + * qemu_system_vmstop_request_prepare has two effects. First, + * it ensures that the STOP event always comes after the + * BLOCK_IO_ERROR event. Second, it ensures that even if management + * can observe the STOP event and do a "cont" before the STOP + * event is issued, the VM will not stop. In this case, vm_start() + * also ensures that the STOP/RESUME pair of events is emitted. + */ + qemu_system_vmstop_request_prepare(); + send_qmp_error_event(bs, action, is_read, error); + qemu_system_vmstop_request(RUN_STATE_IO_ERROR); + } else { + send_qmp_error_event(bs, action, is_read, error); + } +} + +int bdrv_is_read_only(BlockDriverState *bs) +{ + return bs->read_only; +} + +int bdrv_is_sg(BlockDriverState *bs) +{ + return bs->sg; +} + +int bdrv_enable_write_cache(BlockDriverState *bs) +{ + return bs->enable_write_cache; +} + +void bdrv_set_enable_write_cache(BlockDriverState *bs, bool wce) +{ + bs->enable_write_cache = wce; + + /* so a reopen() will preserve wce */ + if (wce) { + bs->open_flags |= BDRV_O_CACHE_WB; + } else { + bs->open_flags &= ~BDRV_O_CACHE_WB; + } +} + +int bdrv_is_encrypted(BlockDriverState *bs) +{ + if (bs->backing_hd && bs->backing_hd->encrypted) + return 1; + return bs->encrypted; +} + +int bdrv_key_required(BlockDriverState *bs) +{ + BlockDriverState *backing_hd = bs->backing_hd; + + if (backing_hd && backing_hd->encrypted && !backing_hd->valid_key) + return 1; + return (bs->encrypted && !bs->valid_key); +} + +int bdrv_set_key(BlockDriverState *bs, const char *key) +{ + int ret; + if (bs->backing_hd && bs->backing_hd->encrypted) { + ret = bdrv_set_key(bs->backing_hd, key); + if (ret < 0) + return ret; + if (!bs->encrypted) + return 0; + } + if (!bs->encrypted) { + return -EINVAL; + } else if (!bs->drv || !bs->drv->bdrv_set_key) { + return -ENOMEDIUM; + } + ret = bs->drv->bdrv_set_key(bs, key); + if (ret < 0) { + bs->valid_key = 0; + } else if (!bs->valid_key) { + bs->valid_key = 1; + if (bs->blk) { + /* call the change callback now, we skipped it on open */ + blk_dev_change_media_cb(bs->blk, true); + } + } + return ret; +} + +/* + * Provide an encryption key for @bs. + * If @key is non-null: + * If @bs is not encrypted, fail. + * Else if the key is invalid, fail. + * Else set @bs's key to @key, replacing the existing key, if any. + * If @key is null: + * If @bs is encrypted and still lacks a key, fail. + * Else do nothing. + * On failure, store an error object through @errp if non-null. + */ +void bdrv_add_key(BlockDriverState *bs, const char *key, Error **errp) +{ + if (key) { + if (!bdrv_is_encrypted(bs)) { + error_setg(errp, "Node '%s' is not encrypted", + bdrv_get_device_or_node_name(bs)); + } else if (bdrv_set_key(bs, key) < 0) { + error_set(errp, QERR_INVALID_PASSWORD); + } + } else { + if (bdrv_key_required(bs)) { + error_set(errp, ERROR_CLASS_DEVICE_ENCRYPTED, + "'%s' (%s) is encrypted", + bdrv_get_device_or_node_name(bs), + bdrv_get_encrypted_filename(bs)); + } + } +} + +const char *bdrv_get_format_name(BlockDriverState *bs) +{ + return bs->drv ? bs->drv->format_name : NULL; +} + +static int qsort_strcmp(const void *a, const void *b) +{ + return strcmp(a, b); +} + +void bdrv_iterate_format(void (*it)(void *opaque, const char *name), + void *opaque) +{ + BlockDriver *drv; + int count = 0; + int i; + const char **formats = NULL; + + QLIST_FOREACH(drv, &bdrv_drivers, list) { + if (drv->format_name) { + bool found = false; + int i = count; + while (formats && i && !found) { + found = !strcmp(formats[--i], drv->format_name); + } + + if (!found) { + formats = g_renew(const char *, formats, count + 1); + formats[count++] = drv->format_name; + } + } + } + + qsort(formats, count, sizeof(formats[0]), qsort_strcmp); + + for (i = 0; i < count; i++) { + it(opaque, formats[i]); + } + + g_free(formats); +} + +/* This function is to find a node in the bs graph */ +BlockDriverState *bdrv_find_node(const char *node_name) +{ + BlockDriverState *bs; + + assert(node_name); + + QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { + if (!strcmp(node_name, bs->node_name)) { + return bs; + } + } + return NULL; +} + +/* Put this QMP function here so it can access the static graph_bdrv_states. */ +BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp) +{ + BlockDeviceInfoList *list, *entry; + BlockDriverState *bs; + + list = NULL; + QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { + BlockDeviceInfo *info = bdrv_block_device_info(bs, errp); + if (!info) { + qapi_free_BlockDeviceInfoList(list); + return NULL; + } + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = list; + list = entry; + } + + return list; +} + +BlockDriverState *bdrv_lookup_bs(const char *device, + const char *node_name, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + + if (device) { + blk = blk_by_name(device); + + if (blk) { + return blk_bs(blk); + } + } + + if (node_name) { + bs = bdrv_find_node(node_name); + + if (bs) { + return bs; + } + } + + error_setg(errp, "Cannot find device=%s nor node_name=%s", + device ? device : "", + node_name ? node_name : ""); + return NULL; +} + +/* If 'base' is in the same chain as 'top', return true. Otherwise, + * return false. If either argument is NULL, return false. */ +bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) +{ + while (top && top != base) { + top = top->backing_hd; + } + + return top != NULL; +} + +BlockDriverState *bdrv_next_node(BlockDriverState *bs) +{ + if (!bs) { + return QTAILQ_FIRST(&graph_bdrv_states); + } + return QTAILQ_NEXT(bs, node_list); +} + +BlockDriverState *bdrv_next(BlockDriverState *bs) +{ + if (!bs) { + return QTAILQ_FIRST(&bdrv_states); + } + return QTAILQ_NEXT(bs, device_list); +} + +const char *bdrv_get_node_name(const BlockDriverState *bs) +{ + return bs->node_name; +} + +/* TODO check what callers really want: bs->node_name or blk_name() */ +const char *bdrv_get_device_name(const BlockDriverState *bs) +{ + return bs->blk ? blk_name(bs->blk) : ""; +} + +/* This can be used to identify nodes that might not have a device + * name associated. Since node and device names live in the same + * namespace, the result is unambiguous. The exception is if both are + * absent, then this returns an empty (non-null) string. */ +const char *bdrv_get_device_or_node_name(const BlockDriverState *bs) +{ + return bs->blk ? blk_name(bs->blk) : bs->node_name; +} + +int bdrv_get_flags(BlockDriverState *bs) +{ + return bs->open_flags; +} + +int bdrv_has_zero_init_1(BlockDriverState *bs) +{ + return 1; +} + +int bdrv_has_zero_init(BlockDriverState *bs) +{ + assert(bs->drv); + + /* If BS is a copy on write image, it is initialized to + the contents of the base image, which may not be zeroes. */ + if (bs->backing_hd) { + return 0; + } + if (bs->drv->bdrv_has_zero_init) { + return bs->drv->bdrv_has_zero_init(bs); + } + + /* safe default */ + return 0; +} + +bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs) +{ + BlockDriverInfo bdi; + + if (bs->backing_hd) { + return false; + } + + if (bdrv_get_info(bs, &bdi) == 0) { + return bdi.unallocated_blocks_are_zero; + } + + return false; +} + +bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) +{ + BlockDriverInfo bdi; + + if (bs->backing_hd || !(bs->open_flags & BDRV_O_UNMAP)) { + return false; + } + + if (bdrv_get_info(bs, &bdi) == 0) { + return bdi.can_write_zeroes_with_unmap; + } + + return false; +} + +const char *bdrv_get_encrypted_filename(BlockDriverState *bs) +{ + if (bs->backing_hd && bs->backing_hd->encrypted) + return bs->backing_file; + else if (bs->encrypted) + return bs->filename; + else + return NULL; +} + +void bdrv_get_backing_filename(BlockDriverState *bs, + char *filename, int filename_size) +{ + pstrcpy(filename, filename_size, bs->backing_file); +} + +int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_get_info) + return -ENOTSUP; + memset(bdi, 0, sizeof(*bdi)); + return drv->bdrv_get_info(bs, bdi); +} + +ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (drv && drv->bdrv_get_specific_info) { + return drv->bdrv_get_specific_info(bs); + } + return NULL; +} + +void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event) +{ + if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) { + return; + } + + bs->drv->bdrv_debug_event(bs, event); +} + +int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, + const char *tag) +{ + while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { + bs = bs->file; + } + + if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) { + return bs->drv->bdrv_debug_breakpoint(bs, event, tag); + } + + return -ENOTSUP; +} + +int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) +{ + while (bs && bs->drv && !bs->drv->bdrv_debug_remove_breakpoint) { + bs = bs->file; + } + + if (bs && bs->drv && bs->drv->bdrv_debug_remove_breakpoint) { + return bs->drv->bdrv_debug_remove_breakpoint(bs, tag); + } + + return -ENOTSUP; +} + +int bdrv_debug_resume(BlockDriverState *bs, const char *tag) +{ + while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) { + bs = bs->file; + } + + if (bs && bs->drv && bs->drv->bdrv_debug_resume) { + return bs->drv->bdrv_debug_resume(bs, tag); + } + + return -ENOTSUP; +} + +bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag) +{ + while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) { + bs = bs->file; + } + + if (bs && bs->drv && bs->drv->bdrv_debug_is_suspended) { + return bs->drv->bdrv_debug_is_suspended(bs, tag); + } + + return false; +} + +int bdrv_is_snapshot(BlockDriverState *bs) +{ + return !!(bs->open_flags & BDRV_O_SNAPSHOT); +} + +/* backing_file can either be relative, or absolute, or a protocol. If it is + * relative, it must be relative to the chain. So, passing in bs->filename + * from a BDS as backing_file should not be done, as that may be relative to + * the CWD rather than the chain. */ +BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, + const char *backing_file) +{ + char *filename_full = NULL; + char *backing_file_full = NULL; + char *filename_tmp = NULL; + int is_protocol = 0; + BlockDriverState *curr_bs = NULL; + BlockDriverState *retval = NULL; + + if (!bs || !bs->drv || !backing_file) { + return NULL; + } + + filename_full = g_malloc(PATH_MAX); + backing_file_full = g_malloc(PATH_MAX); + filename_tmp = g_malloc(PATH_MAX); + + is_protocol = path_has_protocol(backing_file); + + for (curr_bs = bs; curr_bs->backing_hd; curr_bs = curr_bs->backing_hd) { + + /* If either of the filename paths is actually a protocol, then + * compare unmodified paths; otherwise make paths relative */ + if (is_protocol || path_has_protocol(curr_bs->backing_file)) { + if (strcmp(backing_file, curr_bs->backing_file) == 0) { + retval = curr_bs->backing_hd; + break; + } + } else { + /* If not an absolute filename path, make it relative to the current + * image's filename path */ + path_combine(filename_tmp, PATH_MAX, curr_bs->filename, + backing_file); + + /* We are going to compare absolute pathnames */ + if (!realpath(filename_tmp, filename_full)) { + continue; + } + + /* We need to make sure the backing filename we are comparing against + * is relative to the current image filename (or absolute) */ + path_combine(filename_tmp, PATH_MAX, curr_bs->filename, + curr_bs->backing_file); + + if (!realpath(filename_tmp, backing_file_full)) { + continue; + } + + if (strcmp(backing_file_full, filename_full) == 0) { + retval = curr_bs->backing_hd; + break; + } + } + } + + g_free(filename_full); + g_free(backing_file_full); + g_free(filename_tmp); + return retval; +} + +int bdrv_get_backing_file_depth(BlockDriverState *bs) +{ + if (!bs->drv) { + return 0; + } + + if (!bs->backing_hd) { + return 0; + } + + return 1 + bdrv_get_backing_file_depth(bs->backing_hd); +} + +void bdrv_init(void) +{ + module_call_init(MODULE_INIT_BLOCK); +} + +void bdrv_init_with_whitelist(void) +{ + use_bdrv_whitelist = 1; + bdrv_init(); +} + +void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) +{ + Error *local_err = NULL; + int ret; + + if (!bs->drv) { + return; + } + + if (!(bs->open_flags & BDRV_O_INCOMING)) { + return; + } + bs->open_flags &= ~BDRV_O_INCOMING; + + if (bs->drv->bdrv_invalidate_cache) { + bs->drv->bdrv_invalidate_cache(bs, &local_err); + } else if (bs->file) { + bdrv_invalidate_cache(bs->file, &local_err); + } + if (local_err) { + error_propagate(errp, local_err); + return; + } + + ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + return; + } +} + +void bdrv_invalidate_cache_all(Error **errp) +{ + BlockDriverState *bs; + Error *local_err = NULL; + + QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + bdrv_invalidate_cache(bs, &local_err); + aio_context_release(aio_context); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } +} + +/**************************************************************/ +/* removable device support */ + +/** + * Return TRUE if the media is present + */ +int bdrv_is_inserted(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + + if (!drv) + return 0; + if (!drv->bdrv_is_inserted) + return 1; + return drv->bdrv_is_inserted(bs); +} + +/** + * Return whether the media changed since the last call to this + * function, or -ENOTSUP if we don't know. Most drivers don't know. + */ +int bdrv_media_changed(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_media_changed) { + return drv->bdrv_media_changed(bs); + } + return -ENOTSUP; +} + +/** + * If eject_flag is TRUE, eject the media. Otherwise, close the tray + */ +void bdrv_eject(BlockDriverState *bs, bool eject_flag) +{ + BlockDriver *drv = bs->drv; + const char *device_name; + + if (drv && drv->bdrv_eject) { + drv->bdrv_eject(bs, eject_flag); + } + + device_name = bdrv_get_device_name(bs); + if (device_name[0] != '\0') { + qapi_event_send_device_tray_moved(device_name, + eject_flag, &error_abort); + } +} + +/** + * Lock or unlock the media (if it is locked, the user won't be able + * to eject it manually). + */ +void bdrv_lock_medium(BlockDriverState *bs, bool locked) +{ + BlockDriver *drv = bs->drv; + + trace_bdrv_lock_medium(bs, locked); + + if (drv && drv->bdrv_lock_medium) { + drv->bdrv_lock_medium(bs, locked); + } +} + +void bdrv_set_guest_block_size(BlockDriverState *bs, int align) +{ + bs->guest_block_size = align; +} + +BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) +{ + BdrvDirtyBitmap *bm; + + assert(name); + QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { + if (bm->name && !strcmp(name, bm->name)) { + return bm; + } + } + return NULL; +} + +void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) +{ + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + g_free(bitmap->name); + bitmap->name = NULL; +} + +BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, + uint32_t granularity, + const char *name, + Error **errp) +{ + int64_t bitmap_size; + BdrvDirtyBitmap *bitmap; + uint32_t sector_granularity; + + assert((granularity & (granularity - 1)) == 0); + + if (name && bdrv_find_dirty_bitmap(bs, name)) { + error_setg(errp, "Bitmap already exists: %s", name); + return NULL; + } + sector_granularity = granularity >> BDRV_SECTOR_BITS; + assert(sector_granularity); + bitmap_size = bdrv_nb_sectors(bs); + if (bitmap_size < 0) { + error_setg_errno(errp, -bitmap_size, "could not get length of device"); + errno = -bitmap_size; + return NULL; + } + bitmap = g_new0(BdrvDirtyBitmap, 1); + bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity)); + bitmap->size = bitmap_size; + bitmap->name = g_strdup(name); + bitmap->disabled = false; + QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list); + return bitmap; +} + +bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) +{ + return bitmap->successor; +} + +bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) +{ + return !(bitmap->disabled || bitmap->successor); +} + +/** + * Create a successor bitmap destined to replace this bitmap after an operation. + * Requires that the bitmap is not frozen and has no successor. + */ +int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, Error **errp) +{ + uint64_t granularity; + BdrvDirtyBitmap *child; + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, "Cannot create a successor for a bitmap that is " + "currently frozen"); + return -1; + } + assert(!bitmap->successor); + + /* Create an anonymous successor */ + granularity = bdrv_dirty_bitmap_granularity(bitmap); + child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); + if (!child) { + return -1; + } + + /* Successor will be on or off based on our current state. */ + child->disabled = bitmap->disabled; + + /* Install the successor and freeze the parent */ + bitmap->successor = child; + return 0; +} + +/** + * For a bitmap with a successor, yield our name to the successor, + * delete the old bitmap, and return a handle to the new bitmap. + */ +BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp) +{ + char *name; + BdrvDirtyBitmap *successor = bitmap->successor; + + if (successor == NULL) { + error_setg(errp, "Cannot relinquish control if " + "there's no successor present"); + return NULL; + } + + name = bitmap->name; + bitmap->name = NULL; + successor->name = name; + bitmap->successor = NULL; + bdrv_release_dirty_bitmap(bs, bitmap); + + return successor; +} + +/** + * In cases of failure where we can no longer safely delete the parent, + * we may wish to re-join the parent and child/successor. + * The merged parent will be un-frozen, but not explicitly re-enabled. + */ +BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, + BdrvDirtyBitmap *parent, + Error **errp) +{ + BdrvDirtyBitmap *successor = parent->successor; + + if (!successor) { + error_setg(errp, "Cannot reclaim a successor when none is present"); + return NULL; + } + + if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { + error_setg(errp, "Merging of parent and successor bitmap failed"); + return NULL; + } + bdrv_release_dirty_bitmap(bs, successor); + parent->successor = NULL; + + return parent; +} + +/** + * Truncates _all_ bitmaps attached to a BDS. + */ +static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs) +{ + BdrvDirtyBitmap *bitmap; + uint64_t size = bdrv_nb_sectors(bs); + + QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { + if (bdrv_dirty_bitmap_frozen(bitmap)) { + continue; + } + hbitmap_truncate(bitmap->bitmap, size); + } +} + +void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) +{ + BdrvDirtyBitmap *bm, *next; + QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { + if (bm == bitmap) { + assert(!bdrv_dirty_bitmap_frozen(bm)); + QLIST_REMOVE(bitmap, list); + hbitmap_free(bitmap->bitmap); + g_free(bitmap->name); + g_free(bitmap); + return; + } + } +} + +void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) +{ + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = true; +} + +void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) +{ + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = false; +} + +BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) +{ + BdrvDirtyBitmap *bm; + BlockDirtyInfoList *list = NULL; + BlockDirtyInfoList **plist = &list; + + QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { + BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1); + BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1); + info->count = bdrv_get_dirty_count(bm); + info->granularity = bdrv_dirty_bitmap_granularity(bm); + info->has_name = !!bm->name; + info->name = g_strdup(bm->name); + info->frozen = bdrv_dirty_bitmap_frozen(bm); + entry->value = info; + *plist = entry; + plist = &entry->next; + } + + return list; +} + +int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t sector) +{ + if (bitmap) { + return hbitmap_get(bitmap->bitmap, sector); + } else { + return 0; + } +} + +/** + * Chooses a default granularity based on the existing cluster size, + * but clamped between [4K, 64K]. Defaults to 64K in the case that there + * is no cluster size information available. + */ +uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs) +{ + BlockDriverInfo bdi; + uint32_t granularity; + + if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) { + granularity = MAX(4096, bdi.cluster_size); + granularity = MIN(65536, granularity); + } else { + granularity = 65536; + } + + return granularity; +} + +uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap) +{ + return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap); +} + +void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi) +{ + hbitmap_iter_init(hbi, bitmap->bitmap, 0); +} + +void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors) +{ + assert(bdrv_dirty_bitmap_enabled(bitmap)); + hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); +} + +void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors) +{ + assert(bdrv_dirty_bitmap_enabled(bitmap)); + hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors); +} + +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap) +{ + assert(bdrv_dirty_bitmap_enabled(bitmap)); + hbitmap_reset(bitmap->bitmap, 0, bitmap->size); +} + +void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, + int nr_sectors) +{ + BdrvDirtyBitmap *bitmap; + QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { + if (!bdrv_dirty_bitmap_enabled(bitmap)) { + continue; + } + hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); + } +} + +void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, + int nr_sectors) +{ + BdrvDirtyBitmap *bitmap; + QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { + if (!bdrv_dirty_bitmap_enabled(bitmap)) { + continue; + } + hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors); + } +} + +/** + * Advance an HBitmapIter to an arbitrary offset. + */ +void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset) +{ + assert(hbi->hb); + hbitmap_iter_init(hbi, hbi->hb, offset); +} + +int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap) +{ + return hbitmap_count(bitmap->bitmap); +} + +/* Get a reference to bs */ +void bdrv_ref(BlockDriverState *bs) +{ + bs->refcnt++; +} + +/* Release a previously grabbed reference to bs. + * If after releasing, reference count is zero, the BlockDriverState is + * deleted. */ +void bdrv_unref(BlockDriverState *bs) +{ + if (!bs) { + return; + } + assert(bs->refcnt > 0); + if (--bs->refcnt == 0) { + bdrv_delete(bs); + } +} + +struct BdrvOpBlocker { + Error *reason; + QLIST_ENTRY(BdrvOpBlocker) list; +}; + +bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) +{ + BdrvOpBlocker *blocker; + assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); + if (!QLIST_EMPTY(&bs->op_blockers[op])) { + blocker = QLIST_FIRST(&bs->op_blockers[op]); + if (errp) { + error_setg(errp, "Node '%s' is busy: %s", + bdrv_get_device_or_node_name(bs), + error_get_pretty(blocker->reason)); + } + return true; + } + return false; +} + +void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason) +{ + BdrvOpBlocker *blocker; + assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); + + blocker = g_new0(BdrvOpBlocker, 1); + blocker->reason = reason; + QLIST_INSERT_HEAD(&bs->op_blockers[op], blocker, list); +} + +void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason) +{ + BdrvOpBlocker *blocker, *next; + assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); + QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, next) { + if (blocker->reason == reason) { + QLIST_REMOVE(blocker, list); + g_free(blocker); + } + } +} + +void bdrv_op_block_all(BlockDriverState *bs, Error *reason) +{ + int i; + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + bdrv_op_block(bs, i, reason); + } +} + +void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason) +{ + int i; + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + bdrv_op_unblock(bs, i, reason); + } +} + +bool bdrv_op_blocker_is_empty(BlockDriverState *bs) +{ + int i; + + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + if (!QLIST_EMPTY(&bs->op_blockers[i])) { + return false; + } + } + return true; +} + +void bdrv_iostatus_enable(BlockDriverState *bs) +{ + bs->iostatus_enabled = true; + bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK; +} + +/* The I/O status is only enabled if the drive explicitly + * enables it _and_ the VM is configured to stop on errors */ +bool bdrv_iostatus_is_enabled(const BlockDriverState *bs) +{ + return (bs->iostatus_enabled && + (bs->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || + bs->on_write_error == BLOCKDEV_ON_ERROR_STOP || + bs->on_read_error == BLOCKDEV_ON_ERROR_STOP)); +} + +void bdrv_iostatus_disable(BlockDriverState *bs) +{ + bs->iostatus_enabled = false; +} + +void bdrv_iostatus_reset(BlockDriverState *bs) +{ + if (bdrv_iostatus_is_enabled(bs)) { + bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK; + if (bs->job) { + block_job_iostatus_reset(bs->job); + } + } +} + +void bdrv_iostatus_set_err(BlockDriverState *bs, int error) +{ + assert(bdrv_iostatus_is_enabled(bs)); + if (bs->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + bs->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : + BLOCK_DEVICE_IO_STATUS_FAILED; + } +} + +void bdrv_img_create(const char *filename, const char *fmt, + const char *base_filename, const char *base_fmt, + char *options, uint64_t img_size, int flags, + Error **errp, bool quiet) +{ + QemuOptsList *create_opts = NULL; + QemuOpts *opts = NULL; + const char *backing_fmt, *backing_file; + int64_t size; + BlockDriver *drv, *proto_drv; + BlockDriver *backing_drv = NULL; + Error *local_err = NULL; + int ret = 0; + + /* Find driver and parse its options */ + drv = bdrv_find_format(fmt); + if (!drv) { + error_setg(errp, "Unknown file format '%s'", fmt); + return; + } + + proto_drv = bdrv_find_protocol(filename, true, errp); + if (!proto_drv) { + return; + } + + if (!drv->create_opts) { + error_setg(errp, "Format driver '%s' does not support image creation", + drv->format_name); + return; + } + + if (!proto_drv->create_opts) { + error_setg(errp, "Protocol driver '%s' does not support image creation", + proto_drv->format_name); + return; + } + + create_opts = qemu_opts_append(create_opts, drv->create_opts); + create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + + /* Create parameter list with default values */ + opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); + + /* Parse -o options */ + if (options) { + qemu_opts_do_parse(opts, options, NULL, &local_err); + if (local_err) { + error_report_err(local_err); + local_err = NULL; + error_setg(errp, "Invalid options for file format '%s'", fmt); + goto out; + } + } + + if (base_filename) { + qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, &local_err); + if (local_err) { + error_setg(errp, "Backing file not supported for file format '%s'", + fmt); + goto out; + } + } + + if (base_fmt) { + qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, &local_err); + if (local_err) { + error_setg(errp, "Backing file format not supported for file " + "format '%s'", fmt); + goto out; + } + } + + backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); + if (backing_file) { + if (!strcmp(filename, backing_file)) { + error_setg(errp, "Error: Trying to create an image with the " + "same filename as the backing file"); + goto out; + } + } + + backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); + if (backing_fmt) { + backing_drv = bdrv_find_format(backing_fmt); + if (!backing_drv) { + error_setg(errp, "Unknown backing file format '%s'", + backing_fmt); + goto out; + } + } + + // The size for the image must always be specified, with one exception: + // If we are using a backing file, we can obtain the size from there + size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); + if (size == -1) { + if (backing_file) { + BlockDriverState *bs; + char *full_backing = g_new0(char, PATH_MAX); + int64_t size; + int back_flags; + + bdrv_get_full_backing_filename_from_filename(filename, backing_file, + full_backing, PATH_MAX, + &local_err); + if (local_err) { + g_free(full_backing); + goto out; + } + + /* backing files always opened read-only */ + back_flags = + flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); + + bs = NULL; + ret = bdrv_open(&bs, full_backing, NULL, NULL, back_flags, + backing_drv, &local_err); + g_free(full_backing); + if (ret < 0) { + goto out; + } + size = bdrv_getlength(bs); + if (size < 0) { + error_setg_errno(errp, -size, "Could not get size of '%s'", + backing_file); + bdrv_unref(bs); + goto out; + } + + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort); + + bdrv_unref(bs); + } else { + error_setg(errp, "Image creation needs a size parameter"); + goto out; + } + } + + if (!quiet) { + printf("Formatting '%s', fmt=%s", filename, fmt); + qemu_opts_print(opts, " "); + puts(""); + } + + ret = bdrv_create(drv, filename, opts, &local_err); + + if (ret == -EFBIG) { + /* This is generally a better message than whatever the driver would + * deliver (especially because of the cluster_size_hint), since that + * is most probably not much different from "image too large". */ + const char *cluster_size_hint = ""; + if (qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, 0)) { + cluster_size_hint = " (try using a larger cluster size)"; + } + error_setg(errp, "The image size is too large for file format '%s'" + "%s", fmt, cluster_size_hint); + error_free(local_err); + local_err = NULL; + } + +out: + qemu_opts_del(opts); + qemu_opts_free(create_opts); + if (local_err) { + error_propagate(errp, local_err); + } +} + +AioContext *bdrv_get_aio_context(BlockDriverState *bs) +{ + return bs->aio_context; +} + +void bdrv_detach_aio_context(BlockDriverState *bs) +{ + BdrvAioNotifier *baf; + + if (!bs->drv) { + return; + } + + QLIST_FOREACH(baf, &bs->aio_notifiers, list) { + baf->detach_aio_context(baf->opaque); + } + + if (bs->io_limits_enabled) { + throttle_detach_aio_context(&bs->throttle_state); + } + if (bs->drv->bdrv_detach_aio_context) { + bs->drv->bdrv_detach_aio_context(bs); + } + if (bs->file) { + bdrv_detach_aio_context(bs->file); + } + if (bs->backing_hd) { + bdrv_detach_aio_context(bs->backing_hd); + } + + bs->aio_context = NULL; +} + +void bdrv_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BdrvAioNotifier *ban; + + if (!bs->drv) { + return; + } + + bs->aio_context = new_context; + + if (bs->backing_hd) { + bdrv_attach_aio_context(bs->backing_hd, new_context); + } + if (bs->file) { + bdrv_attach_aio_context(bs->file, new_context); + } + if (bs->drv->bdrv_attach_aio_context) { + bs->drv->bdrv_attach_aio_context(bs, new_context); + } + if (bs->io_limits_enabled) { + throttle_attach_aio_context(&bs->throttle_state, new_context); + } + + QLIST_FOREACH(ban, &bs->aio_notifiers, list) { + ban->attached_aio_context(new_context, ban->opaque); + } +} + +void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) +{ + bdrv_drain_all(); /* ensure there are no in-flight requests */ + + bdrv_detach_aio_context(bs); + + /* This function executes in the old AioContext so acquire the new one in + * case it runs in a different thread. + */ + aio_context_acquire(new_context); + bdrv_attach_aio_context(bs, new_context); + aio_context_release(new_context); +} + +void bdrv_add_aio_context_notifier(BlockDriverState *bs, + void (*attached_aio_context)(AioContext *new_context, void *opaque), + void (*detach_aio_context)(void *opaque), void *opaque) +{ + BdrvAioNotifier *ban = g_new(BdrvAioNotifier, 1); + *ban = (BdrvAioNotifier){ + .attached_aio_context = attached_aio_context, + .detach_aio_context = detach_aio_context, + .opaque = opaque + }; + + QLIST_INSERT_HEAD(&bs->aio_notifiers, ban, list); +} + +void bdrv_remove_aio_context_notifier(BlockDriverState *bs, + void (*attached_aio_context)(AioContext *, + void *), + void (*detach_aio_context)(void *), + void *opaque) +{ + BdrvAioNotifier *ban, *ban_next; + + QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) { + if (ban->attached_aio_context == attached_aio_context && + ban->detach_aio_context == detach_aio_context && + ban->opaque == opaque) + { + QLIST_REMOVE(ban, list); + g_free(ban); + + return; + } + } + + abort(); +} + +int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb) +{ + if (!bs->drv->bdrv_amend_options) { + return -ENOTSUP; + } + return bs->drv->bdrv_amend_options(bs, opts, status_cb); +} + +/* This function will be called by the bdrv_recurse_is_first_non_filter method + * of block filter and by bdrv_is_first_non_filter. + * It is used to test if the given bs is the candidate or recurse more in the + * node graph. + */ +bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, + BlockDriverState *candidate) +{ + /* return false if basic checks fails */ + if (!bs || !bs->drv) { + return false; + } + + /* the code reached a non block filter driver -> check if the bs is + * the same as the candidate. It's the recursion termination condition. + */ + if (!bs->drv->is_filter) { + return bs == candidate; + } + /* Down this path the driver is a block filter driver */ + + /* If the block filter recursion method is defined use it to recurse down + * the node graph. + */ + if (bs->drv->bdrv_recurse_is_first_non_filter) { + return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate); + } + + /* the driver is a block filter but don't allow to recurse -> return false + */ + return false; +} + +/* This function checks if the candidate is the first non filter bs down it's + * bs chain. Since we don't have pointers to parents it explore all bs chains + * from the top. Some filters can choose not to pass down the recursion. + */ +bool bdrv_is_first_non_filter(BlockDriverState *candidate) +{ + BlockDriverState *bs; + + /* walk down the bs forest recursively */ + QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + bool perm; + + /* try to recurse in this top level bs */ + perm = bdrv_recurse_is_first_non_filter(bs, candidate); + + /* candidate is the first non filter */ + if (perm) { + return true; + } + } + + return false; +} + +BlockDriverState *check_to_replace_node(const char *node_name, Error **errp) +{ + BlockDriverState *to_replace_bs = bdrv_find_node(node_name); + AioContext *aio_context; + + if (!to_replace_bs) { + error_setg(errp, "Node name '%s' not found", node_name); + return NULL; + } + + aio_context = bdrv_get_aio_context(to_replace_bs); + aio_context_acquire(aio_context); + + if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) { + to_replace_bs = NULL; + goto out; + } + + /* We don't want arbitrary node of the BDS chain to be replaced only the top + * most non filter in order to prevent data corruption. + * Another benefit is that this tests exclude backing files which are + * blocked by the backing blockers. + */ + if (!bdrv_is_first_non_filter(to_replace_bs)) { + error_setg(errp, "Only top most non filter can be replaced"); + to_replace_bs = NULL; + goto out; + } + +out: + aio_context_release(aio_context); + return to_replace_bs; +} + +static bool append_open_options(QDict *d, BlockDriverState *bs) +{ + const QDictEntry *entry; + bool found_any = false; + + for (entry = qdict_first(bs->options); entry; + entry = qdict_next(bs->options, entry)) + { + /* Only take options for this level and exclude all non-driver-specific + * options */ + if (!strchr(qdict_entry_key(entry), '.') && + strcmp(qdict_entry_key(entry), "node-name")) + { + qobject_incref(qdict_entry_value(entry)); + qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); + found_any = true; + } + } + + return found_any; +} + +/* Updates the following BDS fields: + * - exact_filename: A filename which may be used for opening a block device + * which (mostly) equals the given BDS (even without any + * other options; so reading and writing must return the same + * results, but caching etc. may be different) + * - full_open_options: Options which, when given when opening a block device + * (without a filename), result in a BDS (mostly) + * equalling the given one + * - filename: If exact_filename is set, it is copied here. Otherwise, + * full_open_options is converted to a JSON object, prefixed with + * "json:" (for use through the JSON pseudo protocol) and put here. + */ +void bdrv_refresh_filename(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + QDict *opts; + + if (!drv) { + return; + } + + /* This BDS's file name will most probably depend on its file's name, so + * refresh that first */ + if (bs->file) { + bdrv_refresh_filename(bs->file); + } + + if (drv->bdrv_refresh_filename) { + /* Obsolete information is of no use here, so drop the old file name + * information before refreshing it */ + bs->exact_filename[0] = '\0'; + if (bs->full_open_options) { + QDECREF(bs->full_open_options); + bs->full_open_options = NULL; + } + + drv->bdrv_refresh_filename(bs); + } else if (bs->file) { + /* Try to reconstruct valid information from the underlying file */ + bool has_open_options; + + bs->exact_filename[0] = '\0'; + if (bs->full_open_options) { + QDECREF(bs->full_open_options); + bs->full_open_options = NULL; + } + + opts = qdict_new(); + has_open_options = append_open_options(opts, bs); + + /* If no specific options have been given for this BDS, the filename of + * the underlying file should suffice for this one as well */ + if (bs->file->exact_filename[0] && !has_open_options) { + strcpy(bs->exact_filename, bs->file->exact_filename); + } + /* Reconstructing the full options QDict is simple for most format block + * drivers, as long as the full options are known for the underlying + * file BDS. The full options QDict of that file BDS should somehow + * contain a representation of the filename, therefore the following + * suffices without querying the (exact_)filename of this BDS. */ + if (bs->file->full_open_options) { + qdict_put_obj(opts, "driver", + QOBJECT(qstring_from_str(drv->format_name))); + QINCREF(bs->file->full_open_options); + qdict_put_obj(opts, "file", QOBJECT(bs->file->full_open_options)); + + bs->full_open_options = opts; + } else { + QDECREF(opts); + } + } else if (!bs->full_open_options && qdict_size(bs->options)) { + /* There is no underlying file BDS (at least referenced by BDS.file), + * so the full options QDict should be equal to the options given + * specifically for this block device when it was opened (plus the + * driver specification). + * Because those options don't change, there is no need to update + * full_open_options when it's already set. */ + + opts = qdict_new(); + append_open_options(opts, bs); + qdict_put_obj(opts, "driver", + QOBJECT(qstring_from_str(drv->format_name))); + + if (bs->exact_filename[0]) { + /* This may not work for all block protocol drivers (some may + * require this filename to be parsed), but we have to find some + * default solution here, so just include it. If some block driver + * does not support pure options without any filename at all or + * needs some special format of the options QDict, it needs to + * implement the driver-specific bdrv_refresh_filename() function. + */ + qdict_put_obj(opts, "filename", + QOBJECT(qstring_from_str(bs->exact_filename))); + } + + bs->full_open_options = opts; + } + + if (bs->exact_filename[0]) { + pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename); + } else if (bs->full_open_options) { + QString *json = qobject_to_json(QOBJECT(bs->full_open_options)); + snprintf(bs->filename, sizeof(bs->filename), "json:%s", + qstring_get_str(json)); + QDECREF(json); + } +} + +/* This accessor function purpose is to allow the device models to access the + * BlockAcctStats structure embedded inside a BlockDriverState without being + * aware of the BlockDriverState structure layout. + * It will go away when the BlockAcctStats structure will be moved inside + * the device models. + */ +BlockAcctStats *bdrv_get_stats(BlockDriverState *bs) +{ + return &bs->stats; +} diff --git a/block/Makefile.objs b/block/Makefile.objs new file mode 100644 index 0000000..0d8c2a4 --- /dev/null +++ b/block/Makefile.objs @@ -0,0 +1,43 @@ +block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o +block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o +block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o +block-obj-y += qed-check.o +block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o +block-obj-$(CONFIG_QUORUM) += quorum.o +block-obj-y += parallels.o blkdebug.o blkverify.o +block-obj-y += block-backend.o snapshot.o qapi.o +block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o +block-obj-$(CONFIG_POSIX) += raw-posix.o +block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o +block-obj-y += null.o mirror.o io.o + +block-obj-y += nbd.o nbd-client.o sheepdog.o +block-obj-$(CONFIG_LIBISCSI) += iscsi.o +block-obj-$(CONFIG_LIBNFS) += nfs.o +block-obj-$(CONFIG_CURL) += curl.o +block-obj-$(CONFIG_RBD) += rbd.o +block-obj-$(CONFIG_GLUSTERFS) += gluster.o +block-obj-$(CONFIG_ARCHIPELAGO) += archipelago.o +block-obj-$(CONFIG_LIBSSH2) += ssh.o +block-obj-y += accounting.o +block-obj-y += write-threshold.o + +common-obj-y += stream.o +common-obj-y += commit.o +common-obj-y += backup.o + +iscsi.o-cflags := $(LIBISCSI_CFLAGS) +iscsi.o-libs := $(LIBISCSI_LIBS) +curl.o-cflags := $(CURL_CFLAGS) +curl.o-libs := $(CURL_LIBS) +rbd.o-cflags := $(RBD_CFLAGS) +rbd.o-libs := $(RBD_LIBS) +gluster.o-cflags := $(GLUSTERFS_CFLAGS) +gluster.o-libs := $(GLUSTERFS_LIBS) +ssh.o-cflags := $(LIBSSH2_CFLAGS) +ssh.o-libs := $(LIBSSH2_LIBS) +archipelago.o-libs := $(ARCHIPELAGO_LIBS) +block-obj-m += dmg.o +dmg.o-libs := $(BZIP2_LIBS) +qcow.o-libs := -lz +linux-aio.o-libs := -laio diff --git a/block/accounting.c b/block/accounting.c new file mode 100644 index 0000000..01d594f --- /dev/null +++ b/block/accounting.c @@ -0,0 +1,63 @@ +/* + * QEMU System Emulator block accounting + * + * Copyright (c) 2011 Christoph Hellwig + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block/accounting.h" +#include "block/block_int.h" +#include "qemu/timer.h" + +void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, + int64_t bytes, enum BlockAcctType type) +{ + assert(type < BLOCK_MAX_IOTYPE); + + cookie->bytes = bytes; + cookie->start_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + cookie->type = type; +} + +void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) +{ + assert(cookie->type < BLOCK_MAX_IOTYPE); + + stats->nr_bytes[cookie->type] += cookie->bytes; + stats->nr_ops[cookie->type]++; + stats->total_time_ns[cookie->type] += + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - cookie->start_time_ns; +} + + +void block_acct_highest_sector(BlockAcctStats *stats, int64_t sector_num, + unsigned int nb_sectors) +{ + if (stats->wr_highest_sector < sector_num + nb_sectors - 1) { + stats->wr_highest_sector = sector_num + nb_sectors - 1; + } +} + +void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, + int num_requests) +{ + assert(type < BLOCK_MAX_IOTYPE); + stats->merged[type] += num_requests; +} diff --git a/block/archipelago.c b/block/archipelago.c new file mode 100644 index 0000000..855655c --- /dev/null +++ b/block/archipelago.c @@ -0,0 +1,1084 @@ +/* + * QEMU Block driver for Archipelago + * + * Copyright (C) 2014 Chrysostomos Nanakos + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +/* + * VM Image on Archipelago volume is specified like this: + * + * file.driver=archipelago,file.volume= + * [,file.mport=[,file.vport=] + * [,file.segment=]] + * + * or + * + * file=archipelago:[/mport=[:vport=][: + * segment=]] + * + * 'archipelago' is the protocol. + * + * 'mport' is the port number on which mapperd is listening. This is optional + * and if not specified, QEMU will make Archipelago to use the default port. + * + * 'vport' is the port number on which vlmcd is listening. This is optional + * and if not specified, QEMU will make Archipelago to use the default port. + * + * 'segment' is the name of the shared memory segment Archipelago stack + * is using. This is optional and if not specified, QEMU will make Archipelago + * to use the default value, 'archipelago'. + * + * Examples: + * + * file.driver=archipelago,file.volume=my_vm_volume + * file.driver=archipelago,file.volume=my_vm_volume,file.mport=123 + * file.driver=archipelago,file.volume=my_vm_volume,file.mport=123, + * file.vport=1234 + * file.driver=archipelago,file.volume=my_vm_volume,file.mport=123, + * file.vport=1234,file.segment=my_segment + * + * or + * + * file=archipelago:my_vm_volume + * file=archipelago:my_vm_volume/mport=123 + * file=archipelago:my_vm_volume/mport=123:vport=1234 + * file=archipelago:my_vm_volume/mport=123:vport=1234:segment=my_segment + * + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/error-report.h" +#include "qemu/thread.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qjson.h" +#include "qemu/atomic.h" + +#include +#include +#include + +#define MAX_REQUEST_SIZE 524288 + +#define ARCHIPELAGO_OPT_VOLUME "volume" +#define ARCHIPELAGO_OPT_SEGMENT "segment" +#define ARCHIPELAGO_OPT_MPORT "mport" +#define ARCHIPELAGO_OPT_VPORT "vport" +#define ARCHIPELAGO_DFL_MPORT 1001 +#define ARCHIPELAGO_DFL_VPORT 501 + +#define archipelagolog(fmt, ...) \ + do { \ + fprintf(stderr, "archipelago\t%-24s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +typedef enum { + ARCHIP_OP_READ, + ARCHIP_OP_WRITE, + ARCHIP_OP_FLUSH, + ARCHIP_OP_VOLINFO, + ARCHIP_OP_TRUNCATE, +} ARCHIPCmd; + +typedef struct ArchipelagoAIOCB { + BlockAIOCB common; + QEMUBH *bh; + struct BDRVArchipelagoState *s; + QEMUIOVector *qiov; + ARCHIPCmd cmd; + int status; + int64_t size; + int64_t ret; +} ArchipelagoAIOCB; + +typedef struct BDRVArchipelagoState { + ArchipelagoAIOCB *event_acb; + char *volname; + char *segment_name; + uint64_t size; + /* Archipelago specific */ + struct xseg *xseg; + struct xseg_port *port; + xport srcport; + xport sport; + xport mportno; + xport vportno; + QemuMutex archip_mutex; + QemuCond archip_cond; + bool is_signaled; + /* Request handler specific */ + QemuThread request_th; + QemuCond request_cond; + QemuMutex request_mutex; + bool th_is_signaled; + bool stopping; +} BDRVArchipelagoState; + +typedef struct ArchipelagoSegmentedRequest { + size_t count; + size_t total; + int ref; + int failed; +} ArchipelagoSegmentedRequest; + +typedef struct AIORequestData { + const char *volname; + off_t offset; + size_t size; + uint64_t bufidx; + int ret; + int op; + ArchipelagoAIOCB *aio_cb; + ArchipelagoSegmentedRequest *segreq; +} AIORequestData; + +static void qemu_archipelago_complete_aio(void *opaque); + +static void init_local_signal(struct xseg *xseg, xport sport, xport srcport) +{ + if (xseg && (sport != srcport)) { + xseg_init_local_signal(xseg, srcport); + sport = srcport; + } +} + +static void archipelago_finish_aiocb(AIORequestData *reqdata) +{ + if (reqdata->aio_cb->ret != reqdata->segreq->total) { + reqdata->aio_cb->ret = -EIO; + } else if (reqdata->aio_cb->ret == reqdata->segreq->total) { + reqdata->aio_cb->ret = 0; + } + reqdata->aio_cb->bh = aio_bh_new( + bdrv_get_aio_context(reqdata->aio_cb->common.bs), + qemu_archipelago_complete_aio, reqdata + ); + qemu_bh_schedule(reqdata->aio_cb->bh); +} + +static int wait_reply(struct xseg *xseg, xport srcport, struct xseg_port *port, + struct xseg_request *expected_req) +{ + struct xseg_request *req; + xseg_prepare_wait(xseg, srcport); + void *psd = xseg_get_signal_desc(xseg, port); + while (1) { + req = xseg_receive(xseg, srcport, X_NONBLOCK); + if (req) { + if (req != expected_req) { + archipelagolog("Unknown received request\n"); + xseg_put_request(xseg, req, srcport); + } else if (!(req->state & XS_SERVED)) { + return -1; + } else { + break; + } + } + xseg_wait_signal(xseg, psd, 100000UL); + } + xseg_cancel_wait(xseg, srcport); + return 0; +} + +static void xseg_request_handler(void *state) +{ + BDRVArchipelagoState *s = (BDRVArchipelagoState *) state; + void *psd = xseg_get_signal_desc(s->xseg, s->port); + qemu_mutex_lock(&s->request_mutex); + + while (!s->stopping) { + struct xseg_request *req; + void *data; + xseg_prepare_wait(s->xseg, s->srcport); + req = xseg_receive(s->xseg, s->srcport, X_NONBLOCK); + if (req) { + AIORequestData *reqdata; + ArchipelagoSegmentedRequest *segreq; + xseg_get_req_data(s->xseg, req, (void **)&reqdata); + + switch (reqdata->op) { + case ARCHIP_OP_READ: + data = xseg_get_data(s->xseg, req); + segreq = reqdata->segreq; + segreq->count += req->serviced; + + qemu_iovec_from_buf(reqdata->aio_cb->qiov, reqdata->bufidx, + data, + req->serviced); + + xseg_put_request(s->xseg, req, s->srcport); + + if (atomic_fetch_dec(&segreq->ref) == 1) { + if (!segreq->failed) { + reqdata->aio_cb->ret = segreq->count; + archipelago_finish_aiocb(reqdata); + g_free(segreq); + } else { + g_free(segreq); + g_free(reqdata); + } + } else { + g_free(reqdata); + } + break; + case ARCHIP_OP_WRITE: + case ARCHIP_OP_FLUSH: + segreq = reqdata->segreq; + segreq->count += req->serviced; + xseg_put_request(s->xseg, req, s->srcport); + + if (atomic_fetch_dec(&segreq->ref) == 1) { + if (!segreq->failed) { + reqdata->aio_cb->ret = segreq->count; + archipelago_finish_aiocb(reqdata); + g_free(segreq); + } else { + g_free(segreq); + g_free(reqdata); + } + } else { + g_free(reqdata); + } + break; + case ARCHIP_OP_VOLINFO: + case ARCHIP_OP_TRUNCATE: + s->is_signaled = true; + qemu_cond_signal(&s->archip_cond); + break; + } + } else { + xseg_wait_signal(s->xseg, psd, 100000UL); + } + xseg_cancel_wait(s->xseg, s->srcport); + } + + s->th_is_signaled = true; + qemu_cond_signal(&s->request_cond); + qemu_mutex_unlock(&s->request_mutex); + qemu_thread_exit(NULL); +} + +static int qemu_archipelago_xseg_init(BDRVArchipelagoState *s) +{ + if (xseg_initialize()) { + archipelagolog("Cannot initialize XSEG\n"); + goto err_exit; + } + + s->xseg = xseg_join("posix", s->segment_name, + "posixfd", NULL); + if (!s->xseg) { + archipelagolog("Cannot join XSEG shared memory segment\n"); + goto err_exit; + } + s->port = xseg_bind_dynport(s->xseg); + s->srcport = s->port->portno; + init_local_signal(s->xseg, s->sport, s->srcport); + return 0; + +err_exit: + return -1; +} + +static int qemu_archipelago_init(BDRVArchipelagoState *s) +{ + int ret; + + ret = qemu_archipelago_xseg_init(s); + if (ret < 0) { + error_report("Cannot initialize XSEG. Aborting..."); + goto err_exit; + } + + qemu_cond_init(&s->archip_cond); + qemu_mutex_init(&s->archip_mutex); + qemu_cond_init(&s->request_cond); + qemu_mutex_init(&s->request_mutex); + s->th_is_signaled = false; + qemu_thread_create(&s->request_th, "xseg_io_th", + (void *) xseg_request_handler, + (void *) s, QEMU_THREAD_JOINABLE); + +err_exit: + return ret; +} + +static void qemu_archipelago_complete_aio(void *opaque) +{ + AIORequestData *reqdata = (AIORequestData *) opaque; + ArchipelagoAIOCB *aio_cb = (ArchipelagoAIOCB *) reqdata->aio_cb; + + qemu_bh_delete(aio_cb->bh); + aio_cb->common.cb(aio_cb->common.opaque, aio_cb->ret); + aio_cb->status = 0; + + qemu_aio_unref(aio_cb); + g_free(reqdata); +} + +static void xseg_find_port(char *pstr, const char *needle, xport *aport) +{ + const char *a; + char *endptr = NULL; + unsigned long port; + if (strstart(pstr, needle, &a)) { + if (strlen(a) > 0) { + port = strtoul(a, &endptr, 10); + if (strlen(endptr)) { + *aport = -2; + return; + } + *aport = (xport) port; + } + } +} + +static void xseg_find_segment(char *pstr, const char *needle, + char **segment_name) +{ + const char *a; + if (strstart(pstr, needle, &a)) { + if (strlen(a) > 0) { + *segment_name = g_strdup(a); + } + } +} + +static void parse_filename_opts(const char *filename, Error **errp, + char **volume, char **segment_name, + xport *mport, xport *vport) +{ + const char *start; + char *tokens[4], *ds; + int idx; + xport lmport = NoPort, lvport = NoPort; + + strstart(filename, "archipelago:", &start); + + ds = g_strdup(start); + tokens[0] = strtok(ds, "/"); + tokens[1] = strtok(NULL, ":"); + tokens[2] = strtok(NULL, ":"); + tokens[3] = strtok(NULL, "\0"); + + if (!strlen(tokens[0])) { + error_setg(errp, "volume name must be specified first"); + g_free(ds); + return; + } + + for (idx = 1; idx < 4; idx++) { + if (tokens[idx] != NULL) { + if (strstart(tokens[idx], "mport=", NULL)) { + xseg_find_port(tokens[idx], "mport=", &lmport); + } + if (strstart(tokens[idx], "vport=", NULL)) { + xseg_find_port(tokens[idx], "vport=", &lvport); + } + if (strstart(tokens[idx], "segment=", NULL)) { + xseg_find_segment(tokens[idx], "segment=", segment_name); + } + } + } + + if ((lmport == -2) || (lvport == -2)) { + error_setg(errp, "mport and/or vport must be set"); + g_free(ds); + return; + } + *volume = g_strdup(tokens[0]); + *mport = lmport; + *vport = lvport; + g_free(ds); +} + +static void archipelago_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + const char *start; + char *volume = NULL, *segment_name = NULL; + xport mport = NoPort, vport = NoPort; + + if (qdict_haskey(options, ARCHIPELAGO_OPT_VOLUME) + || qdict_haskey(options, ARCHIPELAGO_OPT_SEGMENT) + || qdict_haskey(options, ARCHIPELAGO_OPT_MPORT) + || qdict_haskey(options, ARCHIPELAGO_OPT_VPORT)) { + error_setg(errp, "volume/mport/vport/segment and a file name may not" + " be specified at the same time"); + return; + } + + if (!strstart(filename, "archipelago:", &start)) { + error_setg(errp, "File name must start with 'archipelago:'"); + return; + } + + if (!strlen(start) || strstart(start, "/", NULL)) { + error_setg(errp, "volume name must be specified"); + return; + } + + parse_filename_opts(filename, errp, &volume, &segment_name, &mport, &vport); + + if (volume) { + qdict_put(options, ARCHIPELAGO_OPT_VOLUME, qstring_from_str(volume)); + g_free(volume); + } + if (segment_name) { + qdict_put(options, ARCHIPELAGO_OPT_SEGMENT, + qstring_from_str(segment_name)); + g_free(segment_name); + } + if (mport != NoPort) { + qdict_put(options, ARCHIPELAGO_OPT_MPORT, qint_from_int(mport)); + } + if (vport != NoPort) { + qdict_put(options, ARCHIPELAGO_OPT_VPORT, qint_from_int(vport)); + } +} + +static QemuOptsList archipelago_runtime_opts = { + .name = "archipelago", + .head = QTAILQ_HEAD_INITIALIZER(archipelago_runtime_opts.head), + .desc = { + { + .name = ARCHIPELAGO_OPT_VOLUME, + .type = QEMU_OPT_STRING, + .help = "Name of the volume image", + }, + { + .name = ARCHIPELAGO_OPT_SEGMENT, + .type = QEMU_OPT_STRING, + .help = "Name of the Archipelago shared memory segment", + }, + { + .name = ARCHIPELAGO_OPT_MPORT, + .type = QEMU_OPT_NUMBER, + .help = "Archipelago mapperd port number" + }, + { + .name = ARCHIPELAGO_OPT_VPORT, + .type = QEMU_OPT_NUMBER, + .help = "Archipelago vlmcd port number" + + }, + { /* end of list */ } + }, +}; + +static int qemu_archipelago_open(BlockDriverState *bs, + QDict *options, + int bdrv_flags, + Error **errp) +{ + int ret = 0; + const char *volume, *segment_name; + QemuOpts *opts; + Error *local_err = NULL; + BDRVArchipelagoState *s = bs->opaque; + + opts = qemu_opts_create(&archipelago_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto err_exit; + } + + s->mportno = qemu_opt_get_number(opts, ARCHIPELAGO_OPT_MPORT, + ARCHIPELAGO_DFL_MPORT); + s->vportno = qemu_opt_get_number(opts, ARCHIPELAGO_OPT_VPORT, + ARCHIPELAGO_DFL_VPORT); + + segment_name = qemu_opt_get(opts, ARCHIPELAGO_OPT_SEGMENT); + if (segment_name == NULL) { + s->segment_name = g_strdup("archipelago"); + } else { + s->segment_name = g_strdup(segment_name); + } + + volume = qemu_opt_get(opts, ARCHIPELAGO_OPT_VOLUME); + if (volume == NULL) { + error_setg(errp, "archipelago block driver requires the 'volume'" + " option"); + ret = -EINVAL; + goto err_exit; + } + s->volname = g_strdup(volume); + + /* Initialize XSEG, join shared memory segment */ + ret = qemu_archipelago_init(s); + if (ret < 0) { + error_setg(errp, "cannot initialize XSEG and join shared " + "memory segment"); + goto err_exit; + } + + qemu_opts_del(opts); + return 0; + +err_exit: + g_free(s->volname); + g_free(s->segment_name); + qemu_opts_del(opts); + return ret; +} + +static void qemu_archipelago_close(BlockDriverState *bs) +{ + int r, targetlen; + char *target; + struct xseg_request *req; + BDRVArchipelagoState *s = bs->opaque; + + s->stopping = true; + + qemu_mutex_lock(&s->request_mutex); + while (!s->th_is_signaled) { + qemu_cond_wait(&s->request_cond, + &s->request_mutex); + } + qemu_mutex_unlock(&s->request_mutex); + qemu_thread_join(&s->request_th); + qemu_cond_destroy(&s->request_cond); + qemu_mutex_destroy(&s->request_mutex); + + qemu_cond_destroy(&s->archip_cond); + qemu_mutex_destroy(&s->archip_mutex); + + targetlen = strlen(s->volname); + req = xseg_get_request(s->xseg, s->srcport, s->vportno, X_ALLOC); + if (!req) { + archipelagolog("Cannot get XSEG request\n"); + goto err_exit; + } + r = xseg_prep_request(s->xseg, req, targetlen, 0); + if (r < 0) { + xseg_put_request(s->xseg, req, s->srcport); + archipelagolog("Cannot prepare XSEG close request\n"); + goto err_exit; + } + + target = xseg_get_target(s->xseg, req); + memcpy(target, s->volname, targetlen); + req->size = req->datalen; + req->offset = 0; + req->op = X_CLOSE; + + xport p = xseg_submit(s->xseg, req, s->srcport, X_ALLOC); + if (p == NoPort) { + xseg_put_request(s->xseg, req, s->srcport); + archipelagolog("Cannot submit XSEG close request\n"); + goto err_exit; + } + + xseg_signal(s->xseg, p); + wait_reply(s->xseg, s->srcport, s->port, req); + + xseg_put_request(s->xseg, req, s->srcport); + +err_exit: + g_free(s->volname); + g_free(s->segment_name); + xseg_quit_local_signal(s->xseg, s->srcport); + xseg_leave_dynport(s->xseg, s->port); + xseg_leave(s->xseg); +} + +static int qemu_archipelago_create_volume(Error **errp, const char *volname, + char *segment_name, + uint64_t size, xport mportno, + xport vportno) +{ + int ret, targetlen; + struct xseg *xseg = NULL; + struct xseg_request *req; + struct xseg_request_clone *xclone; + struct xseg_port *port; + xport srcport = NoPort, sport = NoPort; + char *target; + + /* Try default values if none has been set */ + if (mportno == (xport) -1) { + mportno = ARCHIPELAGO_DFL_MPORT; + } + + if (vportno == (xport) -1) { + vportno = ARCHIPELAGO_DFL_VPORT; + } + + if (xseg_initialize()) { + error_setg(errp, "Cannot initialize XSEG"); + return -1; + } + + xseg = xseg_join("posix", segment_name, + "posixfd", NULL); + + if (!xseg) { + error_setg(errp, "Cannot join XSEG shared memory segment"); + return -1; + } + + port = xseg_bind_dynport(xseg); + srcport = port->portno; + init_local_signal(xseg, sport, srcport); + + req = xseg_get_request(xseg, srcport, mportno, X_ALLOC); + if (!req) { + error_setg(errp, "Cannot get XSEG request"); + return -1; + } + + targetlen = strlen(volname); + ret = xseg_prep_request(xseg, req, targetlen, + sizeof(struct xseg_request_clone)); + if (ret < 0) { + error_setg(errp, "Cannot prepare XSEG request"); + goto err_exit; + } + + target = xseg_get_target(xseg, req); + if (!target) { + error_setg(errp, "Cannot get XSEG target."); + goto err_exit; + } + memcpy(target, volname, targetlen); + xclone = (struct xseg_request_clone *) xseg_get_data(xseg, req); + memset(xclone->target, 0 , XSEG_MAX_TARGETLEN); + xclone->targetlen = 0; + xclone->size = size; + req->offset = 0; + req->size = req->datalen; + req->op = X_CLONE; + + xport p = xseg_submit(xseg, req, srcport, X_ALLOC); + if (p == NoPort) { + error_setg(errp, "Could not submit XSEG request"); + goto err_exit; + } + xseg_signal(xseg, p); + + ret = wait_reply(xseg, srcport, port, req); + if (ret < 0) { + error_setg(errp, "wait_reply() error."); + } + + xseg_put_request(xseg, req, srcport); + xseg_quit_local_signal(xseg, srcport); + xseg_leave_dynport(xseg, port); + xseg_leave(xseg); + return ret; + +err_exit: + xseg_put_request(xseg, req, srcport); + xseg_quit_local_signal(xseg, srcport); + xseg_leave_dynport(xseg, port); + xseg_leave(xseg); + return -1; +} + +static int qemu_archipelago_create(const char *filename, + QemuOpts *options, + Error **errp) +{ + int ret = 0; + uint64_t total_size = 0; + char *volname = NULL, *segment_name = NULL; + const char *start; + xport mport = NoPort, vport = NoPort; + + if (!strstart(filename, "archipelago:", &start)) { + error_setg(errp, "File name must start with 'archipelago:'"); + return -1; + } + + if (!strlen(start) || strstart(start, "/", NULL)) { + error_setg(errp, "volume name must be specified"); + return -1; + } + + parse_filename_opts(filename, errp, &volname, &segment_name, &mport, + &vport); + total_size = ROUND_UP(qemu_opt_get_size_del(options, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + + if (segment_name == NULL) { + segment_name = g_strdup("archipelago"); + } + + /* Create an Archipelago volume */ + ret = qemu_archipelago_create_volume(errp, volname, segment_name, + total_size, mport, + vport); + + g_free(volname); + g_free(segment_name); + return ret; +} + +static const AIOCBInfo archipelago_aiocb_info = { + .aiocb_size = sizeof(ArchipelagoAIOCB), +}; + +static int archipelago_submit_request(BDRVArchipelagoState *s, + uint64_t bufidx, + size_t count, + off_t offset, + ArchipelagoAIOCB *aio_cb, + ArchipelagoSegmentedRequest *segreq, + int op) +{ + int ret, targetlen; + char *target; + void *data = NULL; + struct xseg_request *req; + AIORequestData *reqdata = g_new(AIORequestData, 1); + + targetlen = strlen(s->volname); + req = xseg_get_request(s->xseg, s->srcport, s->vportno, X_ALLOC); + if (!req) { + archipelagolog("Cannot get XSEG request\n"); + goto err_exit2; + } + ret = xseg_prep_request(s->xseg, req, targetlen, count); + if (ret < 0) { + archipelagolog("Cannot prepare XSEG request\n"); + goto err_exit; + } + target = xseg_get_target(s->xseg, req); + if (!target) { + archipelagolog("Cannot get XSEG target\n"); + goto err_exit; + } + memcpy(target, s->volname, targetlen); + req->size = count; + req->offset = offset; + + switch (op) { + case ARCHIP_OP_READ: + req->op = X_READ; + break; + case ARCHIP_OP_WRITE: + req->op = X_WRITE; + break; + case ARCHIP_OP_FLUSH: + req->op = X_FLUSH; + break; + } + reqdata->volname = s->volname; + reqdata->offset = offset; + reqdata->size = count; + reqdata->bufidx = bufidx; + reqdata->aio_cb = aio_cb; + reqdata->segreq = segreq; + reqdata->op = op; + + xseg_set_req_data(s->xseg, req, reqdata); + if (op == ARCHIP_OP_WRITE) { + data = xseg_get_data(s->xseg, req); + if (!data) { + archipelagolog("Cannot get XSEG data\n"); + goto err_exit; + } + qemu_iovec_to_buf(aio_cb->qiov, bufidx, data, count); + } + + xport p = xseg_submit(s->xseg, req, s->srcport, X_ALLOC); + if (p == NoPort) { + archipelagolog("Could not submit XSEG request\n"); + goto err_exit; + } + xseg_signal(s->xseg, p); + return 0; + +err_exit: + g_free(reqdata); + xseg_put_request(s->xseg, req, s->srcport); + return -EIO; +err_exit2: + g_free(reqdata); + return -EIO; +} + +static int archipelago_aio_segmented_rw(BDRVArchipelagoState *s, + size_t count, + off_t offset, + ArchipelagoAIOCB *aio_cb, + int op) +{ + int ret, segments_nr; + size_t pos = 0; + ArchipelagoSegmentedRequest *segreq; + + segreq = g_new0(ArchipelagoSegmentedRequest, 1); + + if (op == ARCHIP_OP_FLUSH) { + segments_nr = 1; + } else { + segments_nr = (int)(count / MAX_REQUEST_SIZE) + \ + ((count % MAX_REQUEST_SIZE) ? 1 : 0); + } + segreq->total = count; + atomic_mb_set(&segreq->ref, segments_nr); + + while (segments_nr > 1) { + ret = archipelago_submit_request(s, pos, + MAX_REQUEST_SIZE, + offset + pos, + aio_cb, segreq, op); + + if (ret < 0) { + goto err_exit; + } + count -= MAX_REQUEST_SIZE; + pos += MAX_REQUEST_SIZE; + segments_nr--; + } + ret = archipelago_submit_request(s, pos, count, offset + pos, + aio_cb, segreq, op); + + if (ret < 0) { + goto err_exit; + } + return 0; + +err_exit: + segreq->failed = 1; + if (atomic_fetch_sub(&segreq->ref, segments_nr) == segments_nr) { + g_free(segreq); + } + return ret; +} + +static BlockAIOCB *qemu_archipelago_aio_rw(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque, + int op) +{ + ArchipelagoAIOCB *aio_cb; + BDRVArchipelagoState *s = bs->opaque; + int64_t size, off; + int ret; + + aio_cb = qemu_aio_get(&archipelago_aiocb_info, bs, cb, opaque); + aio_cb->cmd = op; + aio_cb->qiov = qiov; + + aio_cb->ret = 0; + aio_cb->s = s; + aio_cb->status = -EINPROGRESS; + + off = sector_num * BDRV_SECTOR_SIZE; + size = nb_sectors * BDRV_SECTOR_SIZE; + aio_cb->size = size; + + ret = archipelago_aio_segmented_rw(s, size, off, + aio_cb, op); + if (ret < 0) { + goto err_exit; + } + return &aio_cb->common; + +err_exit: + error_report("qemu_archipelago_aio_rw(): I/O Error"); + qemu_aio_unref(aio_cb); + return NULL; +} + +static BlockAIOCB *qemu_archipelago_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return qemu_archipelago_aio_rw(bs, sector_num, qiov, nb_sectors, cb, + opaque, ARCHIP_OP_READ); +} + +static BlockAIOCB *qemu_archipelago_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return qemu_archipelago_aio_rw(bs, sector_num, qiov, nb_sectors, cb, + opaque, ARCHIP_OP_WRITE); +} + +static int64_t archipelago_volume_info(BDRVArchipelagoState *s) +{ + uint64_t size; + int ret, targetlen; + struct xseg_request *req; + struct xseg_reply_info *xinfo; + AIORequestData *reqdata = g_new(AIORequestData, 1); + + const char *volname = s->volname; + targetlen = strlen(volname); + req = xseg_get_request(s->xseg, s->srcport, s->mportno, X_ALLOC); + if (!req) { + archipelagolog("Cannot get XSEG request\n"); + goto err_exit2; + } + ret = xseg_prep_request(s->xseg, req, targetlen, + sizeof(struct xseg_reply_info)); + if (ret < 0) { + archipelagolog("Cannot prepare XSEG request\n"); + goto err_exit; + } + char *target = xseg_get_target(s->xseg, req); + if (!target) { + archipelagolog("Cannot get XSEG target\n"); + goto err_exit; + } + memcpy(target, volname, targetlen); + req->size = req->datalen; + req->offset = 0; + req->op = X_INFO; + + reqdata->op = ARCHIP_OP_VOLINFO; + reqdata->volname = volname; + xseg_set_req_data(s->xseg, req, reqdata); + + xport p = xseg_submit(s->xseg, req, s->srcport, X_ALLOC); + if (p == NoPort) { + archipelagolog("Cannot submit XSEG request\n"); + goto err_exit; + } + xseg_signal(s->xseg, p); + qemu_mutex_lock(&s->archip_mutex); + while (!s->is_signaled) { + qemu_cond_wait(&s->archip_cond, &s->archip_mutex); + } + s->is_signaled = false; + qemu_mutex_unlock(&s->archip_mutex); + + xinfo = (struct xseg_reply_info *) xseg_get_data(s->xseg, req); + size = xinfo->size; + xseg_put_request(s->xseg, req, s->srcport); + g_free(reqdata); + s->size = size; + return size; + +err_exit: + xseg_put_request(s->xseg, req, s->srcport); +err_exit2: + g_free(reqdata); + return -EIO; +} + +static int64_t qemu_archipelago_getlength(BlockDriverState *bs) +{ + int64_t ret; + BDRVArchipelagoState *s = bs->opaque; + + ret = archipelago_volume_info(s); + return ret; +} + +static int qemu_archipelago_truncate(BlockDriverState *bs, int64_t offset) +{ + int ret, targetlen; + struct xseg_request *req; + BDRVArchipelagoState *s = bs->opaque; + AIORequestData *reqdata = g_new(AIORequestData, 1); + + const char *volname = s->volname; + targetlen = strlen(volname); + req = xseg_get_request(s->xseg, s->srcport, s->mportno, X_ALLOC); + if (!req) { + archipelagolog("Cannot get XSEG request\n"); + goto err_exit2; + } + + ret = xseg_prep_request(s->xseg, req, targetlen, 0); + if (ret < 0) { + archipelagolog("Cannot prepare XSEG request\n"); + goto err_exit; + } + char *target = xseg_get_target(s->xseg, req); + if (!target) { + archipelagolog("Cannot get XSEG target\n"); + goto err_exit; + } + memcpy(target, volname, targetlen); + req->offset = offset; + req->op = X_TRUNCATE; + + reqdata->op = ARCHIP_OP_TRUNCATE; + reqdata->volname = volname; + + xseg_set_req_data(s->xseg, req, reqdata); + + xport p = xseg_submit(s->xseg, req, s->srcport, X_ALLOC); + if (p == NoPort) { + archipelagolog("Cannot submit XSEG request\n"); + goto err_exit; + } + + xseg_signal(s->xseg, p); + qemu_mutex_lock(&s->archip_mutex); + while (!s->is_signaled) { + qemu_cond_wait(&s->archip_cond, &s->archip_mutex); + } + s->is_signaled = false; + qemu_mutex_unlock(&s->archip_mutex); + xseg_put_request(s->xseg, req, s->srcport); + g_free(reqdata); + return 0; + +err_exit: + xseg_put_request(s->xseg, req, s->srcport); +err_exit2: + g_free(reqdata); + return -EIO; +} + +static QemuOptsList qemu_archipelago_create_opts = { + .name = "archipelago-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(qemu_archipelago_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { /* end of list */ } + } +}; + +static BlockAIOCB *qemu_archipelago_aio_flush(BlockDriverState *bs, + BlockCompletionFunc *cb, void *opaque) +{ + return qemu_archipelago_aio_rw(bs, 0, NULL, 0, cb, opaque, + ARCHIP_OP_FLUSH); +} + +static BlockDriver bdrv_archipelago = { + .format_name = "archipelago", + .protocol_name = "archipelago", + .instance_size = sizeof(BDRVArchipelagoState), + .bdrv_parse_filename = archipelago_parse_filename, + .bdrv_file_open = qemu_archipelago_open, + .bdrv_close = qemu_archipelago_close, + .bdrv_create = qemu_archipelago_create, + .bdrv_getlength = qemu_archipelago_getlength, + .bdrv_truncate = qemu_archipelago_truncate, + .bdrv_aio_readv = qemu_archipelago_aio_readv, + .bdrv_aio_writev = qemu_archipelago_aio_writev, + .bdrv_aio_flush = qemu_archipelago_aio_flush, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .create_opts = &qemu_archipelago_create_opts, +}; + +static void bdrv_archipelago_init(void) +{ + bdrv_register(&bdrv_archipelago); +} + +block_init(bdrv_archipelago_init); diff --git a/block/backup.c b/block/backup.c new file mode 100644 index 0000000..d3f648d --- /dev/null +++ b/block/backup.c @@ -0,0 +1,548 @@ +/* + * QEMU backup + * + * Copyright (C) 2013 Proxmox Server Solutions + * + * Authors: + * Dietmar Maurer (dietmar@proxmox.com) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include +#include +#include + +#include "trace.h" +#include "block/block.h" +#include "block/block_int.h" +#include "block/blockjob.h" +#include "qemu/ratelimit.h" + +#define BACKUP_CLUSTER_BITS 16 +#define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS) +#define BACKUP_SECTORS_PER_CLUSTER (BACKUP_CLUSTER_SIZE / BDRV_SECTOR_SIZE) + +#define SLICE_TIME 100000000ULL /* ns */ + +typedef struct CowRequest { + int64_t start; + int64_t end; + QLIST_ENTRY(CowRequest) list; + CoQueue wait_queue; /* coroutines blocked on this request */ +} CowRequest; + +typedef struct BackupBlockJob { + BlockJob common; + BlockDriverState *target; + /* bitmap for sync=dirty-bitmap */ + BdrvDirtyBitmap *sync_bitmap; + MirrorSyncMode sync_mode; + RateLimit limit; + BlockdevOnError on_source_error; + BlockdevOnError on_target_error; + CoRwlock flush_rwlock; + uint64_t sectors_read; + HBitmap *bitmap; + QLIST_HEAD(, CowRequest) inflight_reqs; +} BackupBlockJob; + +/* See if in-flight requests overlap and wait for them to complete */ +static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, + int64_t start, + int64_t end) +{ + CowRequest *req; + bool retry; + + do { + retry = false; + QLIST_FOREACH(req, &job->inflight_reqs, list) { + if (end > req->start && start < req->end) { + qemu_co_queue_wait(&req->wait_queue); + retry = true; + break; + } + } + } while (retry); +} + +/* Keep track of an in-flight request */ +static void cow_request_begin(CowRequest *req, BackupBlockJob *job, + int64_t start, int64_t end) +{ + req->start = start; + req->end = end; + qemu_co_queue_init(&req->wait_queue); + QLIST_INSERT_HEAD(&job->inflight_reqs, req, list); +} + +/* Forget about a completed request */ +static void cow_request_end(CowRequest *req) +{ + QLIST_REMOVE(req, list); + qemu_co_queue_restart_all(&req->wait_queue); +} + +static int coroutine_fn backup_do_cow(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + bool *error_is_read) +{ + BackupBlockJob *job = (BackupBlockJob *)bs->job; + CowRequest cow_request; + struct iovec iov; + QEMUIOVector bounce_qiov; + void *bounce_buffer = NULL; + int ret = 0; + int64_t start, end; + int n; + + qemu_co_rwlock_rdlock(&job->flush_rwlock); + + start = sector_num / BACKUP_SECTORS_PER_CLUSTER; + end = DIV_ROUND_UP(sector_num + nb_sectors, BACKUP_SECTORS_PER_CLUSTER); + + trace_backup_do_cow_enter(job, start, sector_num, nb_sectors); + + wait_for_overlapping_requests(job, start, end); + cow_request_begin(&cow_request, job, start, end); + + for (; start < end; start++) { + if (hbitmap_get(job->bitmap, start)) { + trace_backup_do_cow_skip(job, start); + continue; /* already copied */ + } + + trace_backup_do_cow_process(job, start); + + n = MIN(BACKUP_SECTORS_PER_CLUSTER, + job->common.len / BDRV_SECTOR_SIZE - + start * BACKUP_SECTORS_PER_CLUSTER); + + if (!bounce_buffer) { + bounce_buffer = qemu_blockalign(bs, BACKUP_CLUSTER_SIZE); + } + iov.iov_base = bounce_buffer; + iov.iov_len = n * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&bounce_qiov, &iov, 1); + + ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n, + &bounce_qiov); + if (ret < 0) { + trace_backup_do_cow_read_fail(job, start, ret); + if (error_is_read) { + *error_is_read = true; + } + goto out; + } + + if (buffer_is_zero(iov.iov_base, iov.iov_len)) { + ret = bdrv_co_write_zeroes(job->target, + start * BACKUP_SECTORS_PER_CLUSTER, + n, BDRV_REQ_MAY_UNMAP); + } else { + ret = bdrv_co_writev(job->target, + start * BACKUP_SECTORS_PER_CLUSTER, n, + &bounce_qiov); + } + if (ret < 0) { + trace_backup_do_cow_write_fail(job, start, ret); + if (error_is_read) { + *error_is_read = false; + } + goto out; + } + + hbitmap_set(job->bitmap, start, 1); + + /* Publish progress, guest I/O counts as progress too. Note that the + * offset field is an opaque progress value, it is not a disk offset. + */ + job->sectors_read += n; + job->common.offset += n * BDRV_SECTOR_SIZE; + } + +out: + if (bounce_buffer) { + qemu_vfree(bounce_buffer); + } + + cow_request_end(&cow_request); + + trace_backup_do_cow_return(job, sector_num, nb_sectors, ret); + + qemu_co_rwlock_unlock(&job->flush_rwlock); + + return ret; +} + +static int coroutine_fn backup_before_write_notify( + NotifierWithReturn *notifier, + void *opaque) +{ + BdrvTrackedRequest *req = opaque; + int64_t sector_num = req->offset >> BDRV_SECTOR_BITS; + int nb_sectors = req->bytes >> BDRV_SECTOR_BITS; + + assert((req->offset & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((req->bytes & (BDRV_SECTOR_SIZE - 1)) == 0); + + return backup_do_cow(req->bs, sector_num, nb_sectors, NULL); +} + +static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + + if (speed < 0) { + error_set(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } + ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); +} + +static void backup_iostatus_reset(BlockJob *job) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + + bdrv_iostatus_reset(s->target); +} + +static const BlockJobDriver backup_job_driver = { + .instance_size = sizeof(BackupBlockJob), + .job_type = BLOCK_JOB_TYPE_BACKUP, + .set_speed = backup_set_speed, + .iostatus_reset = backup_iostatus_reset, +}; + +static BlockErrorAction backup_error_action(BackupBlockJob *job, + bool read, int error) +{ + if (read) { + return block_job_error_action(&job->common, job->common.bs, + job->on_source_error, true, error); + } else { + return block_job_error_action(&job->common, job->target, + job->on_target_error, false, error); + } +} + +typedef struct { + int ret; +} BackupCompleteData; + +static void backup_complete(BlockJob *job, void *opaque) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupCompleteData *data = opaque; + + bdrv_unref(s->target); + + block_job_completed(job, data->ret); + g_free(data); +} + +static bool coroutine_fn yield_and_check(BackupBlockJob *job) +{ + if (block_job_is_cancelled(&job->common)) { + return true; + } + + /* we need to yield so that bdrv_drain_all() returns. + * (without, VM does not reboot) + */ + if (job->common.speed) { + uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, + job->sectors_read); + job->sectors_read = 0; + block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns); + } else { + block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0); + } + + if (block_job_is_cancelled(&job->common)) { + return true; + } + + return false; +} + +static int coroutine_fn backup_run_incremental(BackupBlockJob *job) +{ + bool error_is_read; + int ret = 0; + int clusters_per_iter; + uint32_t granularity; + int64_t sector; + int64_t cluster; + int64_t end; + int64_t last_cluster = -1; + BlockDriverState *bs = job->common.bs; + HBitmapIter hbi; + + granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap); + clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1); + bdrv_dirty_iter_init(job->sync_bitmap, &hbi); + + /* Find the next dirty sector(s) */ + while ((sector = hbitmap_iter_next(&hbi)) != -1) { + cluster = sector / BACKUP_SECTORS_PER_CLUSTER; + + /* Fake progress updates for any clusters we skipped */ + if (cluster != last_cluster + 1) { + job->common.offset += ((cluster - last_cluster - 1) * + BACKUP_CLUSTER_SIZE); + } + + for (end = cluster + clusters_per_iter; cluster < end; cluster++) { + do { + if (yield_and_check(job)) { + return ret; + } + ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER, + BACKUP_SECTORS_PER_CLUSTER, &error_is_read); + if ((ret < 0) && + backup_error_action(job, error_is_read, -ret) == + BLOCK_ERROR_ACTION_REPORT) { + return ret; + } + } while (ret < 0); + } + + /* If the bitmap granularity is smaller than the backup granularity, + * we need to advance the iterator pointer to the next cluster. */ + if (granularity < BACKUP_CLUSTER_SIZE) { + bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER); + } + + last_cluster = cluster - 1; + } + + /* Play some final catchup with the progress meter */ + end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE); + if (last_cluster + 1 < end) { + job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE); + } + + return ret; +} + +static void coroutine_fn backup_run(void *opaque) +{ + BackupBlockJob *job = opaque; + BackupCompleteData *data; + BlockDriverState *bs = job->common.bs; + BlockDriverState *target = job->target; + BlockdevOnError on_target_error = job->on_target_error; + NotifierWithReturn before_write = { + .notify = backup_before_write_notify, + }; + int64_t start, end; + int ret = 0; + + QLIST_INIT(&job->inflight_reqs); + qemu_co_rwlock_init(&job->flush_rwlock); + + start = 0; + end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE); + + job->bitmap = hbitmap_alloc(end, 0); + + bdrv_set_enable_write_cache(target, true); + bdrv_set_on_error(target, on_target_error, on_target_error); + bdrv_iostatus_enable(target); + + bdrv_add_before_write_notifier(bs, &before_write); + + if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { + while (!block_job_is_cancelled(&job->common)) { + /* Yield until the job is cancelled. We just let our before_write + * notify callback service CoW requests. */ + job->common.busy = false; + qemu_coroutine_yield(); + job->common.busy = true; + } + } else if (job->sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) { + ret = backup_run_incremental(job); + } else { + /* Both FULL and TOP SYNC_MODE's require copying.. */ + for (; start < end; start++) { + bool error_is_read; + if (yield_and_check(job)) { + break; + } + + if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { + int i, n; + int alloced = 0; + + /* Check to see if these blocks are already in the + * backing file. */ + + for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) { + /* bdrv_is_allocated() only returns true/false based + * on the first set of sectors it comes across that + * are are all in the same state. + * For that reason we must verify each sector in the + * backup cluster length. We end up copying more than + * needed but at some point that is always the case. */ + alloced = + bdrv_is_allocated(bs, + start * BACKUP_SECTORS_PER_CLUSTER + i, + BACKUP_SECTORS_PER_CLUSTER - i, &n); + i += n; + + if (alloced == 1 || n == 0) { + break; + } + } + + /* If the above loop never found any sectors that are in + * the topmost image, skip this backup. */ + if (alloced == 0) { + continue; + } + } + /* FULL sync mode we copy the whole drive. */ + ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER, + BACKUP_SECTORS_PER_CLUSTER, &error_is_read); + if (ret < 0) { + /* Depending on error action, fail now or retry cluster */ + BlockErrorAction action = + backup_error_action(job, error_is_read, -ret); + if (action == BLOCK_ERROR_ACTION_REPORT) { + break; + } else { + start--; + continue; + } + } + } + } + + notifier_with_return_remove(&before_write); + + /* wait until pending backup_do_cow() calls have completed */ + qemu_co_rwlock_wrlock(&job->flush_rwlock); + qemu_co_rwlock_unlock(&job->flush_rwlock); + + if (job->sync_bitmap) { + BdrvDirtyBitmap *bm; + if (ret < 0) { + /* Merge the successor back into the parent, delete nothing. */ + bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); + assert(bm); + } else { + /* Everything is fine, delete this bitmap and install the backup. */ + bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL); + assert(bm); + } + } + hbitmap_free(job->bitmap); + + bdrv_iostatus_disable(target); + bdrv_op_unblock_all(target, job->common.blocker); + + data = g_malloc(sizeof(*data)); + data->ret = ret; + block_job_defer_to_main_loop(&job->common, backup_complete, data); +} + +void backup_start(BlockDriverState *bs, BlockDriverState *target, + int64_t speed, MirrorSyncMode sync_mode, + BdrvDirtyBitmap *sync_bitmap, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, + BlockCompletionFunc *cb, void *opaque, + Error **errp) +{ + int64_t len; + + assert(bs); + assert(target); + assert(cb); + + if (bs == target) { + error_setg(errp, "Source and target cannot be the same"); + return; + } + + if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || + on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && + !bdrv_iostatus_is_enabled(bs)) { + error_set(errp, QERR_INVALID_PARAMETER, "on-source-error"); + return; + } + + if (!bdrv_is_inserted(bs)) { + error_setg(errp, "Device is not inserted: %s", + bdrv_get_device_name(bs)); + return; + } + + if (!bdrv_is_inserted(target)) { + error_setg(errp, "Device is not inserted: %s", + bdrv_get_device_name(target)); + return; + } + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { + return; + } + + if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { + return; + } + + if (sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) { + if (!sync_bitmap) { + error_setg(errp, "must provide a valid bitmap name for " + "\"dirty-bitmap\" sync mode"); + return; + } + + /* Create a new bitmap, and freeze/disable this one. */ + if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) { + return; + } + } else if (sync_bitmap) { + error_setg(errp, + "a sync_bitmap was provided to backup_run, " + "but received an incompatible sync_mode (%s)", + MirrorSyncMode_lookup[sync_mode]); + return; + } + + len = bdrv_getlength(bs); + if (len < 0) { + error_setg_errno(errp, -len, "unable to get length for '%s'", + bdrv_get_device_name(bs)); + goto error; + } + + BackupBlockJob *job = block_job_create(&backup_job_driver, bs, speed, + cb, opaque, errp); + if (!job) { + goto error; + } + + bdrv_op_block_all(target, job->common.blocker); + + job->on_source_error = on_source_error; + job->on_target_error = on_target_error; + job->target = target; + job->sync_mode = sync_mode; + job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP ? + sync_bitmap : NULL; + job->common.len = len; + job->common.co = qemu_coroutine_create(backup_run); + qemu_coroutine_enter(job->common.co, job); + return; + + error: + if (sync_bitmap) { + bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); + } +} diff --git a/block/blkdebug.c b/block/blkdebug.c new file mode 100644 index 0000000..3c30edb --- /dev/null +++ b/block/blkdebug.c @@ -0,0 +1,807 @@ +/* + * Block protocol for I/O error injection + * + * Copyright (c) 2010 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qstring.h" + +typedef struct BDRVBlkdebugState { + int state; + int new_state; + + QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; + QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; + QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs; +} BDRVBlkdebugState; + +typedef struct BlkdebugAIOCB { + BlockAIOCB common; + QEMUBH *bh; + int ret; +} BlkdebugAIOCB; + +typedef struct BlkdebugSuspendedReq { + Coroutine *co; + char *tag; + QLIST_ENTRY(BlkdebugSuspendedReq) next; +} BlkdebugSuspendedReq; + +static const AIOCBInfo blkdebug_aiocb_info = { + .aiocb_size = sizeof(BlkdebugAIOCB), +}; + +enum { + ACTION_INJECT_ERROR, + ACTION_SET_STATE, + ACTION_SUSPEND, +}; + +typedef struct BlkdebugRule { + BlkDebugEvent event; + int action; + int state; + union { + struct { + int error; + int immediately; + int once; + int64_t sector; + } inject; + struct { + int new_state; + } set_state; + struct { + char *tag; + } suspend; + } options; + QLIST_ENTRY(BlkdebugRule) next; + QSIMPLEQ_ENTRY(BlkdebugRule) active_next; +} BlkdebugRule; + +static QemuOptsList inject_error_opts = { + .name = "inject-error", + .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), + .desc = { + { + .name = "event", + .type = QEMU_OPT_STRING, + }, + { + .name = "state", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "errno", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "sector", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "once", + .type = QEMU_OPT_BOOL, + }, + { + .name = "immediately", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList set_state_opts = { + .name = "set-state", + .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head), + .desc = { + { + .name = "event", + .type = QEMU_OPT_STRING, + }, + { + .name = "state", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "new_state", + .type = QEMU_OPT_NUMBER, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList *config_groups[] = { + &inject_error_opts, + &set_state_opts, + NULL +}; + +static const char *event_names[BLKDBG_EVENT_MAX] = { + [BLKDBG_L1_UPDATE] = "l1_update", + [BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow.alloc_table", + [BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow.write_table", + [BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow.activate_table", + + [BLKDBG_L2_LOAD] = "l2_load", + [BLKDBG_L2_UPDATE] = "l2_update", + [BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed", + [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read", + [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write", + + [BLKDBG_READ_AIO] = "read_aio", + [BLKDBG_READ_BACKING_AIO] = "read_backing_aio", + [BLKDBG_READ_COMPRESSED] = "read_compressed", + + [BLKDBG_WRITE_AIO] = "write_aio", + [BLKDBG_WRITE_COMPRESSED] = "write_compressed", + + [BLKDBG_VMSTATE_LOAD] = "vmstate_load", + [BLKDBG_VMSTATE_SAVE] = "vmstate_save", + + [BLKDBG_COW_READ] = "cow_read", + [BLKDBG_COW_WRITE] = "cow_write", + + [BLKDBG_REFTABLE_LOAD] = "reftable_load", + [BLKDBG_REFTABLE_GROW] = "reftable_grow", + [BLKDBG_REFTABLE_UPDATE] = "reftable_update", + + [BLKDBG_REFBLOCK_LOAD] = "refblock_load", + [BLKDBG_REFBLOCK_UPDATE] = "refblock_update", + [BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part", + [BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc", + [BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc.hookup", + [BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc.write", + [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc.write_blocks", + [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc.write_table", + [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc.switch_table", + + [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", + [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", + [BLKDBG_CLUSTER_FREE] = "cluster_free", + + [BLKDBG_FLUSH_TO_OS] = "flush_to_os", + [BLKDBG_FLUSH_TO_DISK] = "flush_to_disk", + + [BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw.head", + [BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw.after_head", + [BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw.tail", + [BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw.after_tail", + [BLKDBG_PWRITEV] = "pwritev", + [BLKDBG_PWRITEV_ZERO] = "pwritev_zero", + [BLKDBG_PWRITEV_DONE] = "pwritev_done", + + [BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare", +}; + +static int get_event_by_name(const char *name, BlkDebugEvent *event) +{ + int i; + + for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + if (!strcmp(event_names[i], name)) { + *event = i; + return 0; + } + } + + return -1; +} + +struct add_rule_data { + BDRVBlkdebugState *s; + int action; + Error **errp; +}; + +static int add_rule(QemuOpts *opts, void *opaque) +{ + struct add_rule_data *d = opaque; + BDRVBlkdebugState *s = d->s; + const char* event_name; + BlkDebugEvent event; + struct BlkdebugRule *rule; + + /* Find the right event for the rule */ + event_name = qemu_opt_get(opts, "event"); + if (!event_name) { + error_setg(d->errp, "Missing event name for rule"); + return -1; + } else if (get_event_by_name(event_name, &event) < 0) { + error_setg(d->errp, "Invalid event name \"%s\"", event_name); + return -1; + } + + /* Set attributes common for all actions */ + rule = g_malloc0(sizeof(*rule)); + *rule = (struct BlkdebugRule) { + .event = event, + .action = d->action, + .state = qemu_opt_get_number(opts, "state", 0), + }; + + /* Parse action-specific options */ + switch (d->action) { + case ACTION_INJECT_ERROR: + rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO); + rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0); + rule->options.inject.immediately = + qemu_opt_get_bool(opts, "immediately", 0); + rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1); + break; + + case ACTION_SET_STATE: + rule->options.set_state.new_state = + qemu_opt_get_number(opts, "new_state", 0); + break; + + case ACTION_SUSPEND: + rule->options.suspend.tag = + g_strdup(qemu_opt_get(opts, "tag")); + break; + }; + + /* Add the rule */ + QLIST_INSERT_HEAD(&s->rules[event], rule, next); + + return 0; +} + +static void remove_rule(BlkdebugRule *rule) +{ + switch (rule->action) { + case ACTION_INJECT_ERROR: + case ACTION_SET_STATE: + break; + case ACTION_SUSPEND: + g_free(rule->options.suspend.tag); + break; + } + + QLIST_REMOVE(rule, next); + g_free(rule); +} + +static int read_config(BDRVBlkdebugState *s, const char *filename, + QDict *options, Error **errp) +{ + FILE *f = NULL; + int ret; + struct add_rule_data d; + Error *local_err = NULL; + + if (filename) { + f = fopen(filename, "r"); + if (f == NULL) { + error_setg_errno(errp, errno, "Could not read blkdebug config file"); + return -errno; + } + + ret = qemu_config_parse(f, config_groups, filename); + if (ret < 0) { + error_setg(errp, "Could not parse blkdebug config file"); + ret = -EINVAL; + goto fail; + } + } + + qemu_config_parse_qdict(options, config_groups, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + d.s = s; + d.action = ACTION_INJECT_ERROR; + d.errp = &local_err; + qemu_opts_foreach(&inject_error_opts, add_rule, &d, 1); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + d.action = ACTION_SET_STATE; + qemu_opts_foreach(&set_state_opts, add_rule, &d, 1); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + ret = 0; +fail: + qemu_opts_reset(&inject_error_opts); + qemu_opts_reset(&set_state_opts); + if (f) { + fclose(f); + } + return ret; +} + +/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ +static void blkdebug_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + const char *c; + + /* Parse the blkdebug: prefix */ + if (!strstart(filename, "blkdebug:", &filename)) { + /* There was no prefix; therefore, all options have to be already + present in the QDict (except for the filename) */ + qdict_put(options, "x-image", qstring_from_str(filename)); + return; + } + + /* Parse config file path */ + c = strchr(filename, ':'); + if (c == NULL) { + error_setg(errp, "blkdebug requires both config file and image path"); + return; + } + + if (c != filename) { + QString *config_path; + config_path = qstring_from_substr(filename, 0, c - filename - 1); + qdict_put(options, "config", config_path); + } + + /* TODO Allow multi-level nesting and set file.filename here */ + filename = c + 1; + qdict_put(options, "x-image", qstring_from_str(filename)); +} + +static QemuOptsList runtime_opts = { + .name = "blkdebug", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "config", + .type = QEMU_OPT_STRING, + .help = "Path to the configuration file", + }, + { + .name = "x-image", + .type = QEMU_OPT_STRING, + .help = "[internal use only, will be removed]", + }, + { + .name = "align", + .type = QEMU_OPT_SIZE, + .help = "Required alignment in bytes", + }, + { /* end of list */ } + }, +}; + +static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVBlkdebugState *s = bs->opaque; + QemuOpts *opts; + Error *local_err = NULL; + const char *config; + uint64_t align; + int ret; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + /* Read rules from config file or command line options */ + config = qemu_opt_get(opts, "config"); + ret = read_config(s, config, options, errp); + if (ret) { + goto out; + } + + /* Set initial state */ + s->state = 1; + + /* Open the backing file */ + assert(bs->file == NULL); + ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image", + flags | BDRV_O_PROTOCOL, false, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto out; + } + + /* Set request alignment */ + align = qemu_opt_get_size(opts, "align", bs->request_alignment); + if (align > 0 && align < INT_MAX && !(align & (align - 1))) { + bs->request_alignment = align; + } else { + error_setg(errp, "Invalid alignment"); + ret = -EINVAL; + goto fail_unref; + } + + ret = 0; + goto out; + +fail_unref: + bdrv_unref(bs->file); +out: + qemu_opts_del(opts); + return ret; +} + +static void error_callback_bh(void *opaque) +{ + struct BlkdebugAIOCB *acb = opaque; + qemu_bh_delete(acb->bh); + acb->common.cb(acb->common.opaque, acb->ret); + qemu_aio_unref(acb); +} + +static BlockAIOCB *inject_error(BlockDriverState *bs, + BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule) +{ + BDRVBlkdebugState *s = bs->opaque; + int error = rule->options.inject.error; + struct BlkdebugAIOCB *acb; + QEMUBH *bh; + bool immediately = rule->options.inject.immediately; + + if (rule->options.inject.once) { + QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next); + remove_rule(rule); + } + + if (immediately) { + return NULL; + } + + acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque); + acb->ret = -error; + + bh = aio_bh_new(bdrv_get_aio_context(bs), error_callback_bh, acb); + acb->bh = bh; + qemu_bh_schedule(bh); + + return &acb->common; +} + +static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugRule *rule = NULL; + + QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { + if (rule->options.inject.sector == -1 || + (rule->options.inject.sector >= sector_num && + rule->options.inject.sector < sector_num + nb_sectors)) { + break; + } + } + + if (rule && rule->options.inject.error) { + return inject_error(bs, cb, opaque, rule); + } + + return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); +} + +static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugRule *rule = NULL; + + QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { + if (rule->options.inject.sector == -1 || + (rule->options.inject.sector >= sector_num && + rule->options.inject.sector < sector_num + nb_sectors)) { + break; + } + } + + if (rule && rule->options.inject.error) { + return inject_error(bs, cb, opaque, rule); + } + + return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); +} + +static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugRule *rule = NULL; + + QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { + if (rule->options.inject.sector == -1) { + break; + } + } + + if (rule && rule->options.inject.error) { + return inject_error(bs, cb, opaque, rule); + } + + return bdrv_aio_flush(bs->file, cb, opaque); +} + + +static void blkdebug_close(BlockDriverState *bs) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugRule *rule, *next; + int i; + + for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { + remove_rule(rule); + } + } +} + +static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugSuspendedReq r; + + r = (BlkdebugSuspendedReq) { + .co = qemu_coroutine_self(), + .tag = g_strdup(rule->options.suspend.tag), + }; + + remove_rule(rule); + QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next); + + printf("blkdebug: Suspended request '%s'\n", r.tag); + qemu_coroutine_yield(); + printf("blkdebug: Resuming request '%s'\n", r.tag); + + QLIST_REMOVE(&r, next); + g_free(r.tag); +} + +static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, + bool injected) +{ + BDRVBlkdebugState *s = bs->opaque; + + /* Only process rules for the current state */ + if (rule->state && rule->state != s->state) { + return injected; + } + + /* Take the action */ + switch (rule->action) { + case ACTION_INJECT_ERROR: + if (!injected) { + QSIMPLEQ_INIT(&s->active_rules); + injected = true; + } + QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next); + break; + + case ACTION_SET_STATE: + s->new_state = rule->options.set_state.new_state; + break; + + case ACTION_SUSPEND: + suspend_request(bs, rule); + break; + } + return injected; +} + +static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) +{ + BDRVBlkdebugState *s = bs->opaque; + struct BlkdebugRule *rule, *next; + bool injected; + + assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); + + injected = false; + s->new_state = s->state; + QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) { + injected = process_rule(bs, rule, injected); + } + s->state = s->new_state; +} + +static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event, + const char *tag) +{ + BDRVBlkdebugState *s = bs->opaque; + struct BlkdebugRule *rule; + BlkDebugEvent blkdebug_event; + + if (get_event_by_name(event, &blkdebug_event) < 0) { + return -ENOENT; + } + + + rule = g_malloc(sizeof(*rule)); + *rule = (struct BlkdebugRule) { + .event = blkdebug_event, + .action = ACTION_SUSPEND, + .state = 0, + .options.suspend.tag = g_strdup(tag), + }; + + QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next); + + return 0; +} + +static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugSuspendedReq *r, *next; + + QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) { + if (!strcmp(r->tag, tag)) { + qemu_coroutine_enter(r->co, NULL); + return 0; + } + } + return -ENOENT; +} + +static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs, + const char *tag) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugSuspendedReq *r, *r_next; + BlkdebugRule *rule, *next; + int i, ret = -ENOENT; + + for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { + if (rule->action == ACTION_SUSPEND && + !strcmp(rule->options.suspend.tag, tag)) { + remove_rule(rule); + ret = 0; + } + } + } + QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) { + if (!strcmp(r->tag, tag)) { + qemu_coroutine_enter(r->co, NULL); + ret = 0; + } + } + return ret; +} + +static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugSuspendedReq *r; + + QLIST_FOREACH(r, &s->suspended_reqs, next) { + if (!strcmp(r->tag, tag)) { + return true; + } + } + return false; +} + +static int64_t blkdebug_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file); +} + +static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) +{ + return bdrv_truncate(bs->file, offset); +} + +static void blkdebug_refresh_filename(BlockDriverState *bs) +{ + QDict *opts; + const QDictEntry *e; + bool force_json = false; + + for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { + if (strcmp(qdict_entry_key(e), "config") && + strcmp(qdict_entry_key(e), "x-image") && + strcmp(qdict_entry_key(e), "image") && + strncmp(qdict_entry_key(e), "image.", strlen("image."))) + { + force_json = true; + break; + } + } + + if (force_json && !bs->file->full_open_options) { + /* The config file cannot be recreated, so creating a plain filename + * is impossible */ + return; + } + + if (!force_json && bs->file->exact_filename[0]) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "blkdebug:%s:%s", + qdict_get_try_str(bs->options, "config") ?: "", + bs->file->exact_filename); + } + + opts = qdict_new(); + qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug"))); + + QINCREF(bs->file->full_open_options); + qdict_put_obj(opts, "image", QOBJECT(bs->file->full_open_options)); + + for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { + if (strcmp(qdict_entry_key(e), "x-image") && + strcmp(qdict_entry_key(e), "image") && + strncmp(qdict_entry_key(e), "image.", strlen("image."))) + { + qobject_incref(qdict_entry_value(e)); + qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); + } + } + + bs->full_open_options = opts; +} + +static BlockDriver bdrv_blkdebug = { + .format_name = "blkdebug", + .protocol_name = "blkdebug", + .instance_size = sizeof(BDRVBlkdebugState), + + .bdrv_parse_filename = blkdebug_parse_filename, + .bdrv_file_open = blkdebug_open, + .bdrv_close = blkdebug_close, + .bdrv_getlength = blkdebug_getlength, + .bdrv_truncate = blkdebug_truncate, + .bdrv_refresh_filename = blkdebug_refresh_filename, + + .bdrv_aio_readv = blkdebug_aio_readv, + .bdrv_aio_writev = blkdebug_aio_writev, + .bdrv_aio_flush = blkdebug_aio_flush, + + .bdrv_debug_event = blkdebug_debug_event, + .bdrv_debug_breakpoint = blkdebug_debug_breakpoint, + .bdrv_debug_remove_breakpoint + = blkdebug_debug_remove_breakpoint, + .bdrv_debug_resume = blkdebug_debug_resume, + .bdrv_debug_is_suspended = blkdebug_debug_is_suspended, +}; + +static void bdrv_blkdebug_init(void) +{ + bdrv_register(&bdrv_blkdebug); +} + +block_init(bdrv_blkdebug_init); diff --git a/block/blkverify.c b/block/blkverify.c new file mode 100644 index 0000000..438dff8 --- /dev/null +++ b/block/blkverify.c @@ -0,0 +1,360 @@ +/* + * Block protocol for block driver correctness testing + * + * Copyright (C) 2010 IBM, Corp. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include "qemu/sockets.h" /* for EINPROGRESS on Windows */ +#include "block/block_int.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" + +typedef struct { + BlockDriverState *test_file; +} BDRVBlkverifyState; + +typedef struct BlkverifyAIOCB BlkverifyAIOCB; +struct BlkverifyAIOCB { + BlockAIOCB common; + QEMUBH *bh; + + /* Request metadata */ + bool is_write; + int64_t sector_num; + int nb_sectors; + + int ret; /* first completed request's result */ + unsigned int done; /* completion counter */ + + QEMUIOVector *qiov; /* user I/O vector */ + QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */ + void *buf; /* buffer for raw file I/O */ + + void (*verify)(BlkverifyAIOCB *acb); +}; + +static const AIOCBInfo blkverify_aiocb_info = { + .aiocb_size = sizeof(BlkverifyAIOCB), +}; + +static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ", + acb->is_write ? "write" : "read", acb->sector_num, + acb->nb_sectors); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */ +static void blkverify_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + const char *c; + QString *raw_path; + + + /* Parse the blkverify: prefix */ + if (!strstart(filename, "blkverify:", &filename)) { + /* There was no prefix; therefore, all options have to be already + present in the QDict (except for the filename) */ + qdict_put(options, "x-image", qstring_from_str(filename)); + return; + } + + /* Parse the raw image filename */ + c = strchr(filename, ':'); + if (c == NULL) { + error_setg(errp, "blkverify requires raw copy and original image path"); + return; + } + + /* TODO Implement option pass-through and set raw.filename here */ + raw_path = qstring_from_substr(filename, 0, c - filename - 1); + qdict_put(options, "x-raw", raw_path); + + /* TODO Allow multi-level nesting and set file.filename here */ + filename = c + 1; + qdict_put(options, "x-image", qstring_from_str(filename)); +} + +static QemuOptsList runtime_opts = { + .name = "blkverify", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "x-raw", + .type = QEMU_OPT_STRING, + .help = "[internal use only, will be removed]", + }, + { + .name = "x-image", + .type = QEMU_OPT_STRING, + .help = "[internal use only, will be removed]", + }, + { /* end of list */ } + }, +}; + +static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVBlkverifyState *s = bs->opaque; + QemuOpts *opts; + Error *local_err = NULL; + int ret; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + /* Open the raw file */ + assert(bs->file == NULL); + ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options, + "raw", flags | BDRV_O_PROTOCOL, false, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto fail; + } + + /* Open the test file */ + assert(s->test_file == NULL); + ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options, + "test", flags, false, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + s->test_file = NULL; + goto fail; + } + + ret = 0; +fail: + qemu_opts_del(opts); + return ret; +} + +static void blkverify_close(BlockDriverState *bs) +{ + BDRVBlkverifyState *s = bs->opaque; + + bdrv_unref(s->test_file); + s->test_file = NULL; +} + +static int64_t blkverify_getlength(BlockDriverState *bs) +{ + BDRVBlkverifyState *s = bs->opaque; + + return bdrv_getlength(s->test_file); +} + +static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, + int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aiocb_info, bs, cb, opaque); + + acb->bh = NULL; + acb->is_write = is_write; + acb->sector_num = sector_num; + acb->nb_sectors = nb_sectors; + acb->ret = -EINPROGRESS; + acb->done = 0; + acb->qiov = qiov; + acb->buf = NULL; + acb->verify = NULL; + return acb; +} + +static void blkverify_aio_bh(void *opaque) +{ + BlkverifyAIOCB *acb = opaque; + + qemu_bh_delete(acb->bh); + if (acb->buf) { + qemu_iovec_destroy(&acb->raw_qiov); + qemu_vfree(acb->buf); + } + acb->common.cb(acb->common.opaque, acb->ret); + qemu_aio_unref(acb); +} + +static void blkverify_aio_cb(void *opaque, int ret) +{ + BlkverifyAIOCB *acb = opaque; + + switch (++acb->done) { + case 1: + acb->ret = ret; + break; + + case 2: + if (acb->ret != ret) { + blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret); + } + + if (acb->verify) { + acb->verify(acb); + } + + acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs), + blkverify_aio_bh, acb); + qemu_bh_schedule(acb->bh); + break; + } +} + +static void blkverify_verify_readv(BlkverifyAIOCB *acb) +{ + ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov); + if (offset != -1) { + blkverify_err(acb, "contents mismatch in sector %" PRId64, + acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE)); + } +} + +static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVBlkverifyState *s = bs->opaque; + BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov, + nb_sectors, cb, opaque); + + acb->verify = blkverify_verify_readv; + acb->buf = qemu_blockalign(bs->file, qiov->size); + qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); + qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf); + + bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb); + bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, + blkverify_aio_cb, acb); + return &acb->common; +} + +static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVBlkverifyState *s = bs->opaque; + BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, + nb_sectors, cb, opaque); + + bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb); + bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb); + return &acb->common; +} + +static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs, + BlockCompletionFunc *cb, + void *opaque) +{ + BDRVBlkverifyState *s = bs->opaque; + + /* Only flush test file, the raw file is not important */ + return bdrv_aio_flush(s->test_file, cb, opaque); +} + +static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, + BlockDriverState *candidate) +{ + BDRVBlkverifyState *s = bs->opaque; + + bool perm = bdrv_recurse_is_first_non_filter(bs->file, candidate); + + if (perm) { + return true; + } + + return bdrv_recurse_is_first_non_filter(s->test_file, candidate); +} + +/* Propagate AioContext changes to ->test_file */ +static void blkverify_detach_aio_context(BlockDriverState *bs) +{ + BDRVBlkverifyState *s = bs->opaque; + + bdrv_detach_aio_context(s->test_file); +} + +static void blkverify_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVBlkverifyState *s = bs->opaque; + + bdrv_attach_aio_context(s->test_file, new_context); +} + +static void blkverify_refresh_filename(BlockDriverState *bs) +{ + BDRVBlkverifyState *s = bs->opaque; + + /* bs->file has already been refreshed */ + bdrv_refresh_filename(s->test_file); + + if (bs->file->full_open_options && s->test_file->full_open_options) { + QDict *opts = qdict_new(); + qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkverify"))); + + QINCREF(bs->file->full_open_options); + qdict_put_obj(opts, "raw", QOBJECT(bs->file->full_open_options)); + QINCREF(s->test_file->full_open_options); + qdict_put_obj(opts, "test", QOBJECT(s->test_file->full_open_options)); + + bs->full_open_options = opts; + } + + if (bs->file->exact_filename[0] && s->test_file->exact_filename[0]) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "blkverify:%s:%s", + bs->file->exact_filename, s->test_file->exact_filename); + } +} + +static BlockDriver bdrv_blkverify = { + .format_name = "blkverify", + .protocol_name = "blkverify", + .instance_size = sizeof(BDRVBlkverifyState), + + .bdrv_parse_filename = blkverify_parse_filename, + .bdrv_file_open = blkverify_open, + .bdrv_close = blkverify_close, + .bdrv_getlength = blkverify_getlength, + .bdrv_refresh_filename = blkverify_refresh_filename, + + .bdrv_aio_readv = blkverify_aio_readv, + .bdrv_aio_writev = blkverify_aio_writev, + .bdrv_aio_flush = blkverify_aio_flush, + + .bdrv_attach_aio_context = blkverify_attach_aio_context, + .bdrv_detach_aio_context = blkverify_detach_aio_context, + + .is_filter = true, + .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter, +}; + +static void bdrv_blkverify_init(void) +{ + bdrv_register(&bdrv_blkverify); +} + +block_init(bdrv_blkverify_init); diff --git a/block/block-backend.c b/block/block-backend.c new file mode 100644 index 0000000..93e46f3 --- /dev/null +++ b/block/block-backend.c @@ -0,0 +1,915 @@ +/* + * QEMU Block backends + * + * Copyright (C) 2014 Red Hat, Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 + * or later. See the COPYING.LIB file in the top-level directory. + */ + +#include "sysemu/block-backend.h" +#include "block/block_int.h" +#include "sysemu/blockdev.h" +#include "qapi-event.h" + +/* Number of coroutines to reserve per attached device model */ +#define COROUTINE_POOL_RESERVATION 64 + +struct BlockBackend { + char *name; + int refcnt; + BlockDriverState *bs; + DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ + QTAILQ_ENTRY(BlockBackend) link; /* for blk_backends */ + + void *dev; /* attached device model, if any */ + /* TODO change to DeviceState when all users are qdevified */ + const BlockDevOps *dev_ops; + void *dev_opaque; +}; + +typedef struct BlockBackendAIOCB { + BlockAIOCB common; + QEMUBH *bh; + int ret; +} BlockBackendAIOCB; + +static const AIOCBInfo block_backend_aiocb_info = { + .aiocb_size = sizeof(BlockBackendAIOCB), +}; + +static void drive_info_del(DriveInfo *dinfo); + +/* All the BlockBackends (except for hidden ones) */ +static QTAILQ_HEAD(, BlockBackend) blk_backends = + QTAILQ_HEAD_INITIALIZER(blk_backends); + +/* + * Create a new BlockBackend with @name, with a reference count of one. + * @name must not be null or empty. + * Fail if a BlockBackend with this name already exists. + * Store an error through @errp on failure, unless it's null. + * Return the new BlockBackend on success, null on failure. + */ +BlockBackend *blk_new(const char *name, Error **errp) +{ + BlockBackend *blk; + + assert(name && name[0]); + if (!id_wellformed(name)) { + error_setg(errp, "Invalid device name"); + return NULL; + } + if (blk_by_name(name)) { + error_setg(errp, "Device with id '%s' already exists", name); + return NULL; + } + if (bdrv_find_node(name)) { + error_setg(errp, + "Device name '%s' conflicts with an existing node name", + name); + return NULL; + } + + blk = g_new0(BlockBackend, 1); + blk->name = g_strdup(name); + blk->refcnt = 1; + QTAILQ_INSERT_TAIL(&blk_backends, blk, link); + return blk; +} + +/* + * Create a new BlockBackend with a new BlockDriverState attached. + * Otherwise just like blk_new(), which see. + */ +BlockBackend *blk_new_with_bs(const char *name, Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + + blk = blk_new(name, errp); + if (!blk) { + return NULL; + } + + bs = bdrv_new_root(); + blk->bs = bs; + bs->blk = blk; + return blk; +} + +/* + * Calls blk_new_with_bs() and then calls bdrv_open() on the BlockDriverState. + * + * Just as with bdrv_open(), after having called this function the reference to + * @options belongs to the block layer (even on failure). + * + * TODO: Remove @filename and @flags; it should be possible to specify a whole + * BDS tree just by specifying the @options QDict (or @reference, + * alternatively). At the time of adding this function, this is not possible, + * though, so callers of this function have to be able to specify @filename and + * @flags. + */ +BlockBackend *blk_new_open(const char *name, const char *filename, + const char *reference, QDict *options, int flags, + Error **errp) +{ + BlockBackend *blk; + int ret; + + blk = blk_new_with_bs(name, errp); + if (!blk) { + QDECREF(options); + return NULL; + } + + ret = bdrv_open(&blk->bs, filename, reference, options, flags, NULL, errp); + if (ret < 0) { + blk_unref(blk); + return NULL; + } + + return blk; +} + +static void blk_delete(BlockBackend *blk) +{ + assert(!blk->refcnt); + assert(!blk->dev); + if (blk->bs) { + assert(blk->bs->blk == blk); + blk->bs->blk = NULL; + bdrv_unref(blk->bs); + blk->bs = NULL; + } + /* Avoid double-remove after blk_hide_on_behalf_of_hmp_drive_del() */ + if (blk->name[0]) { + QTAILQ_REMOVE(&blk_backends, blk, link); + } + g_free(blk->name); + drive_info_del(blk->legacy_dinfo); + g_free(blk); +} + +static void drive_info_del(DriveInfo *dinfo) +{ + if (!dinfo) { + return; + } + qemu_opts_del(dinfo->opts); + g_free(dinfo->serial); + g_free(dinfo); +} + +/* + * Increment @blk's reference count. + * @blk must not be null. + */ +void blk_ref(BlockBackend *blk) +{ + blk->refcnt++; +} + +/* + * Decrement @blk's reference count. + * If this drops it to zero, destroy @blk. + * For convenience, do nothing if @blk is null. + */ +void blk_unref(BlockBackend *blk) +{ + if (blk) { + assert(blk->refcnt > 0); + if (!--blk->refcnt) { + blk_delete(blk); + } + } +} + +/* + * Return the BlockBackend after @blk. + * If @blk is null, return the first one. + * Else, return @blk's next sibling, which may be null. + * + * To iterate over all BlockBackends, do + * for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + * ... + * } + */ +BlockBackend *blk_next(BlockBackend *blk) +{ + return blk ? QTAILQ_NEXT(blk, link) : QTAILQ_FIRST(&blk_backends); +} + +/* + * Return @blk's name, a non-null string. + * Wart: the name is empty iff @blk has been hidden with + * blk_hide_on_behalf_of_hmp_drive_del(). + */ +const char *blk_name(BlockBackend *blk) +{ + return blk->name; +} + +/* + * Return the BlockBackend with name @name if it exists, else null. + * @name must not be null. + */ +BlockBackend *blk_by_name(const char *name) +{ + BlockBackend *blk; + + assert(name); + QTAILQ_FOREACH(blk, &blk_backends, link) { + if (!strcmp(name, blk->name)) { + return blk; + } + } + return NULL; +} + +/* + * Return the BlockDriverState attached to @blk if any, else null. + */ +BlockDriverState *blk_bs(BlockBackend *blk) +{ + return blk->bs; +} + +/* + * Return @blk's DriveInfo if any, else null. + */ +DriveInfo *blk_legacy_dinfo(BlockBackend *blk) +{ + return blk->legacy_dinfo; +} + +/* + * Set @blk's DriveInfo to @dinfo, and return it. + * @blk must not have a DriveInfo set already. + * No other BlockBackend may have the same DriveInfo set. + */ +DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo) +{ + assert(!blk->legacy_dinfo); + return blk->legacy_dinfo = dinfo; +} + +/* + * Return the BlockBackend with DriveInfo @dinfo. + * It must exist. + */ +BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) +{ + BlockBackend *blk; + + QTAILQ_FOREACH(blk, &blk_backends, link) { + if (blk->legacy_dinfo == dinfo) { + return blk; + } + } + abort(); +} + +/* + * Hide @blk. + * @blk must not have been hidden already. + * Make attached BlockDriverState, if any, anonymous. + * Once hidden, @blk is invisible to all functions that don't receive + * it as argument. For example, blk_by_name() won't return it. + * Strictly for use by do_drive_del(). + * TODO get rid of it! + */ +void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk) +{ + QTAILQ_REMOVE(&blk_backends, blk, link); + blk->name[0] = 0; + if (blk->bs) { + bdrv_make_anon(blk->bs); + } +} + +/* + * Attach device model @dev to @blk. + * Return 0 on success, -EBUSY when a device model is attached already. + */ +int blk_attach_dev(BlockBackend *blk, void *dev) +/* TODO change to DeviceState *dev when all users are qdevified */ +{ + if (blk->dev) { + return -EBUSY; + } + blk_ref(blk); + blk->dev = dev; + bdrv_iostatus_reset(blk->bs); + return 0; +} + +/* + * Attach device model @dev to @blk. + * @blk must not have a device model attached already. + * TODO qdevified devices don't use this, remove when devices are qdevified + */ +void blk_attach_dev_nofail(BlockBackend *blk, void *dev) +{ + if (blk_attach_dev(blk, dev) < 0) { + abort(); + } +} + +/* + * Detach device model @dev from @blk. + * @dev must be currently attached to @blk. + */ +void blk_detach_dev(BlockBackend *blk, void *dev) +/* TODO change to DeviceState *dev when all users are qdevified */ +{ + assert(blk->dev == dev); + blk->dev = NULL; + blk->dev_ops = NULL; + blk->dev_opaque = NULL; + bdrv_set_guest_block_size(blk->bs, 512); + blk_unref(blk); +} + +/* + * Return the device model attached to @blk if any, else null. + */ +void *blk_get_attached_dev(BlockBackend *blk) +/* TODO change to return DeviceState * when all users are qdevified */ +{ + return blk->dev; +} + +/* + * Set @blk's device model callbacks to @ops. + * @opaque is the opaque argument to pass to the callbacks. + * This is for use by device models. + */ +void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, + void *opaque) +{ + blk->dev_ops = ops; + blk->dev_opaque = opaque; +} + +/* + * Notify @blk's attached device model of media change. + * If @load is true, notify of media load. + * Else, notify of media eject. + * Also send DEVICE_TRAY_MOVED events as appropriate. + */ +void blk_dev_change_media_cb(BlockBackend *blk, bool load) +{ + if (blk->dev_ops && blk->dev_ops->change_media_cb) { + bool tray_was_closed = !blk_dev_is_tray_open(blk); + + blk->dev_ops->change_media_cb(blk->dev_opaque, load); + if (tray_was_closed) { + /* tray open */ + qapi_event_send_device_tray_moved(blk_name(blk), + true, &error_abort); + } + if (load) { + /* tray close */ + qapi_event_send_device_tray_moved(blk_name(blk), + false, &error_abort); + } + } +} + +/* + * Does @blk's attached device model have removable media? + * %true if no device model is attached. + */ +bool blk_dev_has_removable_media(BlockBackend *blk) +{ + return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb); +} + +/* + * Notify @blk's attached device model of a media eject request. + * If @force is true, the medium is about to be yanked out forcefully. + */ +void blk_dev_eject_request(BlockBackend *blk, bool force) +{ + if (blk->dev_ops && blk->dev_ops->eject_request_cb) { + blk->dev_ops->eject_request_cb(blk->dev_opaque, force); + } +} + +/* + * Does @blk's attached device model have a tray, and is it open? + */ +bool blk_dev_is_tray_open(BlockBackend *blk) +{ + if (blk->dev_ops && blk->dev_ops->is_tray_open) { + return blk->dev_ops->is_tray_open(blk->dev_opaque); + } + return false; +} + +/* + * Does @blk's attached device model have the medium locked? + * %false if the device model has no such lock. + */ +bool blk_dev_is_medium_locked(BlockBackend *blk) +{ + if (blk->dev_ops && blk->dev_ops->is_medium_locked) { + return blk->dev_ops->is_medium_locked(blk->dev_opaque); + } + return false; +} + +/* + * Notify @blk's attached device model of a backend size change. + */ +void blk_dev_resize_cb(BlockBackend *blk) +{ + if (blk->dev_ops && blk->dev_ops->resize_cb) { + blk->dev_ops->resize_cb(blk->dev_opaque); + } +} + +void blk_iostatus_enable(BlockBackend *blk) +{ + bdrv_iostatus_enable(blk->bs); +} + +static int blk_check_byte_request(BlockBackend *blk, int64_t offset, + size_t size) +{ + int64_t len; + + if (size > INT_MAX) { + return -EIO; + } + + if (!blk_is_inserted(blk)) { + return -ENOMEDIUM; + } + + len = blk_getlength(blk); + if (len < 0) { + return len; + } + + if (offset < 0) { + return -EIO; + } + + if (offset > len || len - offset < size) { + return -EIO; + } + + return 0; +} + +static int blk_check_request(BlockBackend *blk, int64_t sector_num, + int nb_sectors) +{ + if (sector_num < 0 || sector_num > INT64_MAX / BDRV_SECTOR_SIZE) { + return -EIO; + } + + if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) { + return -EIO; + } + + return blk_check_byte_request(blk, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE); +} + +int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_read(blk->bs, sector_num, buf, nb_sectors); +} + +int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_read_unthrottled(blk->bs, sector_num, buf, nb_sectors); +} + +int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, + int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_write(blk->bs, sector_num, buf, nb_sectors); +} + +int blk_write_zeroes(BlockBackend *blk, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_write_zeroes(blk->bs, sector_num, nb_sectors, flags); +} + +static void error_callback_bh(void *opaque) +{ + struct BlockBackendAIOCB *acb = opaque; + qemu_bh_delete(acb->bh); + acb->common.cb(acb->common.opaque, acb->ret); + qemu_aio_unref(acb); +} + +static BlockAIOCB *abort_aio_request(BlockBackend *blk, BlockCompletionFunc *cb, + void *opaque, int ret) +{ + struct BlockBackendAIOCB *acb; + QEMUBH *bh; + + acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); + acb->ret = ret; + + bh = aio_bh_new(blk_get_aio_context(blk), error_callback_bh, acb); + acb->bh = bh; + qemu_bh_schedule(bh); + + return &acb->common; +} + +BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return abort_aio_request(blk, cb, opaque, ret); + } + + return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags, + cb, opaque); +} + +int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) +{ + int ret = blk_check_byte_request(blk, offset, count); + if (ret < 0) { + return ret; + } + + return bdrv_pread(blk->bs, offset, buf, count); +} + +int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count) +{ + int ret = blk_check_byte_request(blk, offset, count); + if (ret < 0) { + return ret; + } + + return bdrv_pwrite(blk->bs, offset, buf, count); +} + +int64_t blk_getlength(BlockBackend *blk) +{ + return bdrv_getlength(blk->bs); +} + +void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) +{ + bdrv_get_geometry(blk->bs, nb_sectors_ptr); +} + +int64_t blk_nb_sectors(BlockBackend *blk) +{ + return bdrv_nb_sectors(blk->bs); +} + +BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num, + QEMUIOVector *iov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return abort_aio_request(blk, cb, opaque, ret); + } + + return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque); +} + +BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num, + QEMUIOVector *iov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return abort_aio_request(blk, cb, opaque, ret); + } + + return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque); +} + +BlockAIOCB *blk_aio_flush(BlockBackend *blk, + BlockCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_flush(blk->bs, cb, opaque); +} + +BlockAIOCB *blk_aio_discard(BlockBackend *blk, + int64_t sector_num, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return abort_aio_request(blk, cb, opaque, ret); + } + + return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque); +} + +void blk_aio_cancel(BlockAIOCB *acb) +{ + bdrv_aio_cancel(acb); +} + +void blk_aio_cancel_async(BlockAIOCB *acb) +{ + bdrv_aio_cancel_async(acb); +} + +int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs) +{ + int i, ret; + + for (i = 0; i < num_reqs; i++) { + ret = blk_check_request(blk, reqs[i].sector, reqs[i].nb_sectors); + if (ret < 0) { + return ret; + } + } + + return bdrv_aio_multiwrite(blk->bs, reqs, num_reqs); +} + +int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +{ + return bdrv_ioctl(blk->bs, req, buf); +} + +BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, + BlockCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_ioctl(blk->bs, req, buf, cb, opaque); +} + +int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_co_discard(blk->bs, sector_num, nb_sectors); +} + +int blk_co_flush(BlockBackend *blk) +{ + return bdrv_co_flush(blk->bs); +} + +int blk_flush(BlockBackend *blk) +{ + return bdrv_flush(blk->bs); +} + +int blk_flush_all(void) +{ + return bdrv_flush_all(); +} + +void blk_drain_all(void) +{ + bdrv_drain_all(); +} + +BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) +{ + return bdrv_get_on_error(blk->bs, is_read); +} + +BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, + int error) +{ + return bdrv_get_error_action(blk->bs, is_read, error); +} + +void blk_error_action(BlockBackend *blk, BlockErrorAction action, + bool is_read, int error) +{ + bdrv_error_action(blk->bs, action, is_read, error); +} + +int blk_is_read_only(BlockBackend *blk) +{ + return bdrv_is_read_only(blk->bs); +} + +int blk_is_sg(BlockBackend *blk) +{ + return bdrv_is_sg(blk->bs); +} + +int blk_enable_write_cache(BlockBackend *blk) +{ + return bdrv_enable_write_cache(blk->bs); +} + +void blk_set_enable_write_cache(BlockBackend *blk, bool wce) +{ + bdrv_set_enable_write_cache(blk->bs, wce); +} + +void blk_invalidate_cache(BlockBackend *blk, Error **errp) +{ + bdrv_invalidate_cache(blk->bs, errp); +} + +int blk_is_inserted(BlockBackend *blk) +{ + return bdrv_is_inserted(blk->bs); +} + +void blk_lock_medium(BlockBackend *blk, bool locked) +{ + bdrv_lock_medium(blk->bs, locked); +} + +void blk_eject(BlockBackend *blk, bool eject_flag) +{ + bdrv_eject(blk->bs, eject_flag); +} + +int blk_get_flags(BlockBackend *blk) +{ + return bdrv_get_flags(blk->bs); +} + +int blk_get_max_transfer_length(BlockBackend *blk) +{ + return blk->bs->bl.max_transfer_length; +} + +void blk_set_guest_block_size(BlockBackend *blk, int align) +{ + bdrv_set_guest_block_size(blk->bs, align); +} + +void *blk_blockalign(BlockBackend *blk, size_t size) +{ + return qemu_blockalign(blk ? blk->bs : NULL, size); +} + +bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) +{ + return bdrv_op_is_blocked(blk->bs, op, errp); +} + +void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) +{ + bdrv_op_unblock(blk->bs, op, reason); +} + +void blk_op_block_all(BlockBackend *blk, Error *reason) +{ + bdrv_op_block_all(blk->bs, reason); +} + +void blk_op_unblock_all(BlockBackend *blk, Error *reason) +{ + bdrv_op_unblock_all(blk->bs, reason); +} + +AioContext *blk_get_aio_context(BlockBackend *blk) +{ + return bdrv_get_aio_context(blk->bs); +} + +void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) +{ + bdrv_set_aio_context(blk->bs, new_context); +} + +void blk_add_aio_context_notifier(BlockBackend *blk, + void (*attached_aio_context)(AioContext *new_context, void *opaque), + void (*detach_aio_context)(void *opaque), void *opaque) +{ + bdrv_add_aio_context_notifier(blk->bs, attached_aio_context, + detach_aio_context, opaque); +} + +void blk_remove_aio_context_notifier(BlockBackend *blk, + void (*attached_aio_context)(AioContext *, + void *), + void (*detach_aio_context)(void *), + void *opaque) +{ + bdrv_remove_aio_context_notifier(blk->bs, attached_aio_context, + detach_aio_context, opaque); +} + +void blk_add_close_notifier(BlockBackend *blk, Notifier *notify) +{ + bdrv_add_close_notifier(blk->bs, notify); +} + +void blk_io_plug(BlockBackend *blk) +{ + bdrv_io_plug(blk->bs); +} + +void blk_io_unplug(BlockBackend *blk) +{ + bdrv_io_unplug(blk->bs); +} + +BlockAcctStats *blk_get_stats(BlockBackend *blk) +{ + return bdrv_get_stats(blk->bs); +} + +void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, + BlockCompletionFunc *cb, void *opaque) +{ + return qemu_aio_get(aiocb_info, blk_bs(blk), cb, opaque); +} + +int coroutine_fn blk_co_write_zeroes(BlockBackend *blk, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_co_write_zeroes(blk->bs, sector_num, nb_sectors, flags); +} + +int blk_write_compressed(BlockBackend *blk, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_write_compressed(blk->bs, sector_num, buf, nb_sectors); +} + +int blk_truncate(BlockBackend *blk, int64_t offset) +{ + return bdrv_truncate(blk->bs, offset); +} + +int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_discard(blk->bs, sector_num, nb_sectors); +} + +int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, + int64_t pos, int size) +{ + return bdrv_save_vmstate(blk->bs, buf, pos, size); +} + +int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) +{ + return bdrv_load_vmstate(blk->bs, buf, pos, size); +} + +int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) +{ + return bdrv_probe_blocksizes(blk->bs, bsz); +} + +int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) +{ + return bdrv_probe_geometry(blk->bs, geo); +} diff --git a/block/bochs.c b/block/bochs.c new file mode 100644 index 0000000..199ac2b --- /dev/null +++ b/block/bochs.c @@ -0,0 +1,277 @@ +/* + * Block driver for the various disk image formats used by Bochs + * Currently only for "growing" type in read-only mode + * + * Copyright (c) 2005 Alex Beregszaszi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" + +/**************************************************************/ + +#define HEADER_MAGIC "Bochs Virtual HD Image" +#define HEADER_VERSION 0x00020000 +#define HEADER_V1 0x00010000 +#define HEADER_SIZE 512 + +#define REDOLOG_TYPE "Redolog" +#define GROWING_TYPE "Growing" + +// not allocated: 0xffffffff + +// always little-endian +struct bochs_header { + char magic[32]; /* "Bochs Virtual HD Image" */ + char type[16]; /* "Redolog" */ + char subtype[16]; /* "Undoable" / "Volatile" / "Growing" */ + uint32_t version; + uint32_t header; /* size of header */ + + uint32_t catalog; /* num of entries */ + uint32_t bitmap; /* bitmap size */ + uint32_t extent; /* extent size */ + + union { + struct { + uint32_t reserved; /* for ??? */ + uint64_t disk; /* disk size */ + char padding[HEADER_SIZE - 64 - 20 - 12]; + } QEMU_PACKED redolog; + struct { + uint64_t disk; /* disk size */ + char padding[HEADER_SIZE - 64 - 20 - 8]; + } QEMU_PACKED redolog_v1; + char padding[HEADER_SIZE - 64 - 20]; + } extra; +} QEMU_PACKED; + +typedef struct BDRVBochsState { + CoMutex lock; + uint32_t *catalog_bitmap; + uint32_t catalog_size; + + uint32_t data_offset; + + uint32_t bitmap_blocks; + uint32_t extent_blocks; + uint32_t extent_size; +} BDRVBochsState; + +static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const struct bochs_header *bochs = (const void *)buf; + + if (buf_size < HEADER_SIZE) + return 0; + + if (!strcmp(bochs->magic, HEADER_MAGIC) && + !strcmp(bochs->type, REDOLOG_TYPE) && + !strcmp(bochs->subtype, GROWING_TYPE) && + ((le32_to_cpu(bochs->version) == HEADER_VERSION) || + (le32_to_cpu(bochs->version) == HEADER_V1))) + return 100; + + return 0; +} + +static int bochs_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVBochsState *s = bs->opaque; + uint32_t i; + struct bochs_header bochs; + int ret; + + bs->read_only = 1; // no write support yet + + ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); + if (ret < 0) { + return ret; + } + + if (strcmp(bochs.magic, HEADER_MAGIC) || + strcmp(bochs.type, REDOLOG_TYPE) || + strcmp(bochs.subtype, GROWING_TYPE) || + ((le32_to_cpu(bochs.version) != HEADER_VERSION) && + (le32_to_cpu(bochs.version) != HEADER_V1))) { + error_setg(errp, "Image not in Bochs format"); + return -EINVAL; + } + + if (le32_to_cpu(bochs.version) == HEADER_V1) { + bs->total_sectors = le64_to_cpu(bochs.extra.redolog_v1.disk) / 512; + } else { + bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; + } + + /* Limit to 1M entries to avoid unbounded allocation. This is what is + * needed for the largest image that bximage can create (~8 TB). */ + s->catalog_size = le32_to_cpu(bochs.catalog); + if (s->catalog_size > 0x100000) { + error_setg(errp, "Catalog size is too large"); + return -EFBIG; + } + + s->catalog_bitmap = g_try_new(uint32_t, s->catalog_size); + if (s->catalog_size && s->catalog_bitmap == NULL) { + error_setg(errp, "Could not allocate memory for catalog"); + return -ENOMEM; + } + + ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, + s->catalog_size * 4); + if (ret < 0) { + goto fail; + } + + for (i = 0; i < s->catalog_size; i++) + le32_to_cpus(&s->catalog_bitmap[i]); + + s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4); + + s->bitmap_blocks = 1 + (le32_to_cpu(bochs.bitmap) - 1) / 512; + s->extent_blocks = 1 + (le32_to_cpu(bochs.extent) - 1) / 512; + + s->extent_size = le32_to_cpu(bochs.extent); + if (s->extent_size < BDRV_SECTOR_SIZE) { + /* bximage actually never creates extents smaller than 4k */ + error_setg(errp, "Extent size must be at least 512"); + ret = -EINVAL; + goto fail; + } else if (!is_power_of_2(s->extent_size)) { + error_setg(errp, "Extent size %" PRIu32 " is not a power of two", + s->extent_size); + ret = -EINVAL; + goto fail; + } else if (s->extent_size > 0x800000) { + error_setg(errp, "Extent size %" PRIu32 " is too large", + s->extent_size); + ret = -EINVAL; + goto fail; + } + + if (s->catalog_size < DIV_ROUND_UP(bs->total_sectors, + s->extent_size / BDRV_SECTOR_SIZE)) + { + error_setg(errp, "Catalog size is too small for this disk size"); + ret = -EINVAL; + goto fail; + } + + qemu_co_mutex_init(&s->lock); + return 0; + +fail: + g_free(s->catalog_bitmap); + return ret; +} + +static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) +{ + BDRVBochsState *s = bs->opaque; + uint64_t offset = sector_num * 512; + uint64_t extent_index, extent_offset, bitmap_offset; + char bitmap_entry; + int ret; + + // seek to sector + extent_index = offset / s->extent_size; + extent_offset = (offset % s->extent_size) / 512; + + if (s->catalog_bitmap[extent_index] == 0xffffffff) { + return 0; /* not allocated */ + } + + bitmap_offset = s->data_offset + + (512 * (uint64_t) s->catalog_bitmap[extent_index] * + (s->extent_blocks + s->bitmap_blocks)); + + /* read in bitmap for current extent */ + ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), + &bitmap_entry, 1); + if (ret < 0) { + return ret; + } + + if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { + return 0; /* not allocated */ + } + + return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); +} + +static int bochs_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret; + + while (nb_sectors > 0) { + int64_t block_offset = seek_to_sector(bs, sector_num); + if (block_offset < 0) { + return block_offset; + } else if (block_offset > 0) { + ret = bdrv_pread(bs->file, block_offset, buf, 512); + if (ret < 0) { + return ret; + } + } else { + memset(buf, 0, 512); + } + nb_sectors--; + sector_num++; + buf += 512; + } + return 0; +} + +static coroutine_fn int bochs_co_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVBochsState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = bochs_read(bs, sector_num, buf, nb_sectors); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static void bochs_close(BlockDriverState *bs) +{ + BDRVBochsState *s = bs->opaque; + g_free(s->catalog_bitmap); +} + +static BlockDriver bdrv_bochs = { + .format_name = "bochs", + .instance_size = sizeof(BDRVBochsState), + .bdrv_probe = bochs_probe, + .bdrv_open = bochs_open, + .bdrv_read = bochs_co_read, + .bdrv_close = bochs_close, +}; + +static void bdrv_bochs_init(void) +{ + bdrv_register(&bdrv_bochs); +} + +block_init(bdrv_bochs_init); diff --git a/block/cloop.c b/block/cloop.c new file mode 100644 index 0000000..f328be0 --- /dev/null +++ b/block/cloop.c @@ -0,0 +1,283 @@ +/* + * QEMU Block driver for CLOOP images + * + * Copyright (c) 2004 Johannes E. Schindelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include + +/* Maximum compressed block size */ +#define MAX_BLOCK_SIZE (64 * 1024 * 1024) + +typedef struct BDRVCloopState { + CoMutex lock; + uint32_t block_size; + uint32_t n_blocks; + uint64_t *offsets; + uint32_t sectors_per_block; + uint32_t current_block; + uint8_t *compressed_block; + uint8_t *uncompressed_block; + z_stream zstream; +} BDRVCloopState; + +static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const char *magic_version_2_0 = "#!/bin/sh\n" + "#V2.0 Format\n" + "modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n"; + int length = strlen(magic_version_2_0); + if (length > buf_size) { + length = buf_size; + } + if (!memcmp(magic_version_2_0, buf, length)) { + return 2; + } + return 0; +} + +static int cloop_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVCloopState *s = bs->opaque; + uint32_t offsets_size, max_compressed_block_size = 1, i; + int ret; + + bs->read_only = 1; + + /* read header */ + ret = bdrv_pread(bs->file, 128, &s->block_size, 4); + if (ret < 0) { + return ret; + } + s->block_size = be32_to_cpu(s->block_size); + if (s->block_size % 512) { + error_setg(errp, "block_size %" PRIu32 " must be a multiple of 512", + s->block_size); + return -EINVAL; + } + if (s->block_size == 0) { + error_setg(errp, "block_size cannot be zero"); + return -EINVAL; + } + + /* cloop's create_compressed_fs.c warns about block sizes beyond 256 KB but + * we can accept more. Prevent ridiculous values like 4 GB - 1 since we + * need a buffer this big. + */ + if (s->block_size > MAX_BLOCK_SIZE) { + error_setg(errp, "block_size %" PRIu32 " must be %u MB or less", + s->block_size, + MAX_BLOCK_SIZE / (1024 * 1024)); + return -EINVAL; + } + + ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); + if (ret < 0) { + return ret; + } + s->n_blocks = be32_to_cpu(s->n_blocks); + + /* read offsets */ + if (s->n_blocks > (UINT32_MAX - 1) / sizeof(uint64_t)) { + /* Prevent integer overflow */ + error_setg(errp, "n_blocks %" PRIu32 " must be %zu or less", + s->n_blocks, + (UINT32_MAX - 1) / sizeof(uint64_t)); + return -EINVAL; + } + offsets_size = (s->n_blocks + 1) * sizeof(uint64_t); + if (offsets_size > 512 * 1024 * 1024) { + /* Prevent ridiculous offsets_size which causes memory allocation to + * fail or overflows bdrv_pread() size. In practice the 512 MB + * offsets[] limit supports 16 TB images at 256 KB block size. + */ + error_setg(errp, "image requires too many offsets, " + "try increasing block size"); + return -EINVAL; + } + + s->offsets = g_try_malloc(offsets_size); + if (s->offsets == NULL) { + error_setg(errp, "Could not allocate offsets table"); + return -ENOMEM; + } + + ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); + if (ret < 0) { + goto fail; + } + + for (i = 0; i < s->n_blocks + 1; i++) { + uint64_t size; + + s->offsets[i] = be64_to_cpu(s->offsets[i]); + if (i == 0) { + continue; + } + + if (s->offsets[i] < s->offsets[i - 1]) { + error_setg(errp, "offsets not monotonically increasing at " + "index %" PRIu32 ", image file is corrupt", i); + ret = -EINVAL; + goto fail; + } + + size = s->offsets[i] - s->offsets[i - 1]; + + /* Compressed blocks should be smaller than the uncompressed block size + * but maybe compression performed poorly so the compressed block is + * actually bigger. Clamp down on unrealistic values to prevent + * ridiculous s->compressed_block allocation. + */ + if (size > 2 * MAX_BLOCK_SIZE) { + error_setg(errp, "invalid compressed block size at index %" PRIu32 + ", image file is corrupt", i); + ret = -EINVAL; + goto fail; + } + + if (size > max_compressed_block_size) { + max_compressed_block_size = size; + } + } + + /* initialize zlib engine */ + s->compressed_block = g_try_malloc(max_compressed_block_size + 1); + if (s->compressed_block == NULL) { + error_setg(errp, "Could not allocate compressed_block"); + ret = -ENOMEM; + goto fail; + } + + s->uncompressed_block = g_try_malloc(s->block_size); + if (s->uncompressed_block == NULL) { + error_setg(errp, "Could not allocate uncompressed_block"); + ret = -ENOMEM; + goto fail; + } + + if (inflateInit(&s->zstream) != Z_OK) { + ret = -EINVAL; + goto fail; + } + s->current_block = s->n_blocks; + + s->sectors_per_block = s->block_size/512; + bs->total_sectors = s->n_blocks * s->sectors_per_block; + qemu_co_mutex_init(&s->lock); + return 0; + +fail: + g_free(s->offsets); + g_free(s->compressed_block); + g_free(s->uncompressed_block); + return ret; +} + +static inline int cloop_read_block(BlockDriverState *bs, int block_num) +{ + BDRVCloopState *s = bs->opaque; + + if (s->current_block != block_num) { + int ret; + uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num]; + + ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block, + bytes); + if (ret != bytes) { + return -1; + } + + s->zstream.next_in = s->compressed_block; + s->zstream.avail_in = bytes; + s->zstream.next_out = s->uncompressed_block; + s->zstream.avail_out = s->block_size; + ret = inflateReset(&s->zstream); + if (ret != Z_OK) { + return -1; + } + ret = inflate(&s->zstream, Z_FINISH); + if (ret != Z_STREAM_END || s->zstream.total_out != s->block_size) { + return -1; + } + + s->current_block = block_num; + } + return 0; +} + +static int cloop_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVCloopState *s = bs->opaque; + int i; + + for (i = 0; i < nb_sectors; i++) { + uint32_t sector_offset_in_block = + ((sector_num + i) % s->sectors_per_block), + block_num = (sector_num + i) / s->sectors_per_block; + if (cloop_read_block(bs, block_num) != 0) { + return -1; + } + memcpy(buf + i * 512, + s->uncompressed_block + sector_offset_in_block * 512, 512); + } + return 0; +} + +static coroutine_fn int cloop_co_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVCloopState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = cloop_read(bs, sector_num, buf, nb_sectors); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static void cloop_close(BlockDriverState *bs) +{ + BDRVCloopState *s = bs->opaque; + g_free(s->offsets); + g_free(s->compressed_block); + g_free(s->uncompressed_block); + inflateEnd(&s->zstream); +} + +static BlockDriver bdrv_cloop = { + .format_name = "cloop", + .instance_size = sizeof(BDRVCloopState), + .bdrv_probe = cloop_probe, + .bdrv_open = cloop_open, + .bdrv_read = cloop_co_read, + .bdrv_close = cloop_close, +}; + +static void bdrv_cloop_init(void) +{ + bdrv_register(&bdrv_cloop); +} + +block_init(bdrv_cloop_init); diff --git a/block/commit.c b/block/commit.c new file mode 100644 index 0000000..cfa2bbe --- /dev/null +++ b/block/commit.c @@ -0,0 +1,273 @@ +/* + * Live block commit + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Jeff Cody + * Based on stream.c by Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "trace.h" +#include "block/block_int.h" +#include "block/blockjob.h" +#include "qemu/ratelimit.h" + +enum { + /* + * Size of data buffer for populating the image file. This should be large + * enough to process multiple clusters in a single call, so that populating + * contiguous regions of the image is efficient. + */ + COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ +}; + +#define SLICE_TIME 100000000ULL /* ns */ + +typedef struct CommitBlockJob { + BlockJob common; + RateLimit limit; + BlockDriverState *active; + BlockDriverState *top; + BlockDriverState *base; + BlockdevOnError on_error; + int base_flags; + int orig_overlay_flags; + char *backing_file_str; +} CommitBlockJob; + +static int coroutine_fn commit_populate(BlockDriverState *bs, + BlockDriverState *base, + int64_t sector_num, int nb_sectors, + void *buf) +{ + int ret = 0; + + ret = bdrv_read(bs, sector_num, buf, nb_sectors); + if (ret) { + return ret; + } + + ret = bdrv_write(base, sector_num, buf, nb_sectors); + if (ret) { + return ret; + } + + return 0; +} + +typedef struct { + int ret; +} CommitCompleteData; + +static void commit_complete(BlockJob *job, void *opaque) +{ + CommitBlockJob *s = container_of(job, CommitBlockJob, common); + CommitCompleteData *data = opaque; + BlockDriverState *active = s->active; + BlockDriverState *top = s->top; + BlockDriverState *base = s->base; + BlockDriverState *overlay_bs; + int ret = data->ret; + + if (!block_job_is_cancelled(&s->common) && ret == 0) { + /* success */ + ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str); + } + + /* restore base open flags here if appropriate (e.g., change the base back + * to r/o). These reopens do not need to be atomic, since we won't abort + * even on failure here */ + if (s->base_flags != bdrv_get_flags(base)) { + bdrv_reopen(base, s->base_flags, NULL); + } + overlay_bs = bdrv_find_overlay(active, top); + if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) { + bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL); + } + g_free(s->backing_file_str); + block_job_completed(&s->common, ret); + g_free(data); +} + +static void coroutine_fn commit_run(void *opaque) +{ + CommitBlockJob *s = opaque; + CommitCompleteData *data; + BlockDriverState *top = s->top; + BlockDriverState *base = s->base; + int64_t sector_num, end; + int ret = 0; + int n = 0; + void *buf = NULL; + int bytes_written = 0; + int64_t base_len; + + ret = s->common.len = bdrv_getlength(top); + + + if (s->common.len < 0) { + goto out; + } + + ret = base_len = bdrv_getlength(base); + if (base_len < 0) { + goto out; + } + + if (base_len < s->common.len) { + ret = bdrv_truncate(base, s->common.len); + if (ret) { + goto out; + } + } + + end = s->common.len >> BDRV_SECTOR_BITS; + buf = qemu_blockalign(top, COMMIT_BUFFER_SIZE); + + for (sector_num = 0; sector_num < end; sector_num += n) { + uint64_t delay_ns = 0; + bool copy; + +wait: + /* Note that even when no rate limit is applied we need to yield + * with no pending I/O here so that bdrv_drain_all() returns. + */ + block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); + if (block_job_is_cancelled(&s->common)) { + break; + } + /* Copy if allocated above the base */ + ret = bdrv_is_allocated_above(top, base, sector_num, + COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE, + &n); + copy = (ret == 1); + trace_commit_one_iteration(s, sector_num, n, ret); + if (copy) { + if (s->common.speed) { + delay_ns = ratelimit_calculate_delay(&s->limit, n); + if (delay_ns > 0) { + goto wait; + } + } + ret = commit_populate(top, base, sector_num, n, buf); + bytes_written += n * BDRV_SECTOR_SIZE; + } + if (ret < 0) { + if (s->on_error == BLOCKDEV_ON_ERROR_STOP || + s->on_error == BLOCKDEV_ON_ERROR_REPORT|| + (s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) { + goto out; + } else { + n = 0; + continue; + } + } + /* Publish progress */ + s->common.offset += n * BDRV_SECTOR_SIZE; + } + + ret = 0; + +out: + qemu_vfree(buf); + + data = g_malloc(sizeof(*data)); + data->ret = ret; + block_job_defer_to_main_loop(&s->common, commit_complete, data); +} + +static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) +{ + CommitBlockJob *s = container_of(job, CommitBlockJob, common); + + if (speed < 0) { + error_set(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } + ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); +} + +static const BlockJobDriver commit_job_driver = { + .instance_size = sizeof(CommitBlockJob), + .job_type = BLOCK_JOB_TYPE_COMMIT, + .set_speed = commit_set_speed, +}; + +void commit_start(BlockDriverState *bs, BlockDriverState *base, + BlockDriverState *top, int64_t speed, + BlockdevOnError on_error, BlockCompletionFunc *cb, + void *opaque, const char *backing_file_str, Error **errp) +{ + CommitBlockJob *s; + BlockReopenQueue *reopen_queue = NULL; + int orig_overlay_flags; + int orig_base_flags; + BlockDriverState *overlay_bs; + Error *local_err = NULL; + + if ((on_error == BLOCKDEV_ON_ERROR_STOP || + on_error == BLOCKDEV_ON_ERROR_ENOSPC) && + !bdrv_iostatus_is_enabled(bs)) { + error_setg(errp, "Invalid parameter combination"); + return; + } + + assert(top != bs); + if (top == base) { + error_setg(errp, "Invalid files for merge: top and base are the same"); + return; + } + + overlay_bs = bdrv_find_overlay(bs, top); + + if (overlay_bs == NULL) { + error_setg(errp, "Could not find overlay image for %s:", top->filename); + return; + } + + orig_base_flags = bdrv_get_flags(base); + orig_overlay_flags = bdrv_get_flags(overlay_bs); + + /* convert base & overlay_bs to r/w, if necessary */ + if (!(orig_base_flags & BDRV_O_RDWR)) { + reopen_queue = bdrv_reopen_queue(reopen_queue, base, + orig_base_flags | BDRV_O_RDWR); + } + if (!(orig_overlay_flags & BDRV_O_RDWR)) { + reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, + orig_overlay_flags | BDRV_O_RDWR); + } + if (reopen_queue) { + bdrv_reopen_multiple(reopen_queue, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + } + + + s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp); + if (!s) { + return; + } + + s->base = base; + s->top = top; + s->active = bs; + + s->base_flags = orig_base_flags; + s->orig_overlay_flags = orig_overlay_flags; + + s->backing_file_str = g_strdup(backing_file_str); + + s->on_error = on_error; + s->common.co = qemu_coroutine_create(commit_run); + + trace_commit_start(bs, base, top, s, s->common.co, opaque); + qemu_coroutine_enter(s->common.co, s); +} diff --git a/block/curl.c b/block/curl.c new file mode 100644 index 0000000..bbee3ca --- /dev/null +++ b/block/curl.c @@ -0,0 +1,811 @@ +/* + * QEMU Block driver for CURL images + * + * Copyright (c) 2009 Alexander Graf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qapi/qmp/qbool.h" +#include + +// #define DEBUG_CURL +// #define DEBUG_VERBOSE + +#ifdef DEBUG_CURL +#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do { } while (0) +#endif + +#if LIBCURL_VERSION_NUM >= 0x071000 +/* The multi interface timer callback was introduced in 7.16.0 */ +#define NEED_CURL_TIMER_CALLBACK +#define HAVE_SOCKET_ACTION +#endif + +#ifndef HAVE_SOCKET_ACTION +/* If curl_multi_socket_action isn't available, define it statically here in + * terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is + * less efficient but still safe. */ +static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, + curl_socket_t sockfd, + int ev_bitmask, + int *running_handles) +{ + return curl_multi_socket(multi_handle, sockfd, running_handles); +} +#define curl_multi_socket_action __curl_multi_socket_action +#endif + +#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ + CURLPROTO_FTP | CURLPROTO_FTPS | \ + CURLPROTO_TFTP) + +#define CURL_NUM_STATES 8 +#define CURL_NUM_ACB 8 +#define SECTOR_SIZE 512 +#define READ_AHEAD_DEFAULT (256 * 1024) +#define CURL_TIMEOUT_DEFAULT 5 +#define CURL_TIMEOUT_MAX 10000 + +#define FIND_RET_NONE 0 +#define FIND_RET_OK 1 +#define FIND_RET_WAIT 2 + +#define CURL_BLOCK_OPT_URL "url" +#define CURL_BLOCK_OPT_READAHEAD "readahead" +#define CURL_BLOCK_OPT_SSLVERIFY "sslverify" +#define CURL_BLOCK_OPT_TIMEOUT "timeout" +#define CURL_BLOCK_OPT_COOKIE "cookie" + +struct BDRVCURLState; + +typedef struct CURLAIOCB { + BlockAIOCB common; + QEMUBH *bh; + QEMUIOVector *qiov; + + int64_t sector_num; + int nb_sectors; + + size_t start; + size_t end; +} CURLAIOCB; + +typedef struct CURLState +{ + struct BDRVCURLState *s; + CURLAIOCB *acb[CURL_NUM_ACB]; + CURL *curl; + curl_socket_t sock_fd; + char *orig_buf; + size_t buf_start; + size_t buf_off; + size_t buf_len; + char range[128]; + char errmsg[CURL_ERROR_SIZE]; + char in_use; +} CURLState; + +typedef struct BDRVCURLState { + CURLM *multi; + QEMUTimer timer; + size_t len; + CURLState states[CURL_NUM_STATES]; + char *url; + size_t readahead_size; + bool sslverify; + uint64_t timeout; + char *cookie; + bool accept_range; + AioContext *aio_context; +} BDRVCURLState; + +static void curl_clean_state(CURLState *s); +static void curl_multi_do(void *arg); +static void curl_multi_read(void *arg); + +#ifdef NEED_CURL_TIMER_CALLBACK +static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) +{ + BDRVCURLState *s = opaque; + + DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); + if (timeout_ms == -1) { + timer_del(&s->timer); + } else { + int64_t timeout_ns = (int64_t)timeout_ms * 1000 * 1000; + timer_mod(&s->timer, + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ns); + } + return 0; +} +#endif + +static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, + void *userp, void *sp) +{ + BDRVCURLState *s; + CURLState *state = NULL; + curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state); + state->sock_fd = fd; + s = state->s; + + DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); + switch (action) { + case CURL_POLL_IN: + aio_set_fd_handler(s->aio_context, fd, curl_multi_read, + NULL, state); + break; + case CURL_POLL_OUT: + aio_set_fd_handler(s->aio_context, fd, NULL, curl_multi_do, state); + break; + case CURL_POLL_INOUT: + aio_set_fd_handler(s->aio_context, fd, curl_multi_read, + curl_multi_do, state); + break; + case CURL_POLL_REMOVE: + aio_set_fd_handler(s->aio_context, fd, NULL, NULL, NULL); + break; + } + + return 0; +} + +static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) +{ + BDRVCURLState *s = opaque; + size_t realsize = size * nmemb; + const char *accept_line = "Accept-Ranges: bytes"; + + if (realsize >= strlen(accept_line) + && strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) { + s->accept_range = true; + } + + return realsize; +} + +static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) +{ + CURLState *s = ((CURLState*)opaque); + size_t realsize = size * nmemb; + int i; + + DPRINTF("CURL: Just reading %zd bytes\n", realsize); + + if (!s || !s->orig_buf) + return 0; + + if (s->buf_off >= s->buf_len) { + /* buffer full, read nothing */ + return 0; + } + realsize = MIN(realsize, s->buf_len - s->buf_off); + memcpy(s->orig_buf + s->buf_off, ptr, realsize); + s->buf_off += realsize; + + for(i=0; iacb[i]; + + if (!acb) + continue; + + if ((s->buf_off >= acb->end)) { + qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start, + acb->end - acb->start); + acb->common.cb(acb->common.opaque, 0); + qemu_aio_unref(acb); + s->acb[i] = NULL; + } + } + + return realsize; +} + +static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, + CURLAIOCB *acb) +{ + int i; + size_t end = start + len; + + for (i=0; istates[i]; + size_t buf_end = (state->buf_start + state->buf_off); + size_t buf_fend = (state->buf_start + state->buf_len); + + if (!state->orig_buf) + continue; + if (!state->buf_off) + continue; + + // Does the existing buffer cover our section? + if ((start >= state->buf_start) && + (start <= buf_end) && + (end >= state->buf_start) && + (end <= buf_end)) + { + char *buf = state->orig_buf + (start - state->buf_start); + + qemu_iovec_from_buf(acb->qiov, 0, buf, len); + acb->common.cb(acb->common.opaque, 0); + + return FIND_RET_OK; + } + + // Wait for unfinished chunks + if (state->in_use && + (start >= state->buf_start) && + (start <= buf_fend) && + (end >= state->buf_start) && + (end <= buf_fend)) + { + int j; + + acb->start = start - state->buf_start; + acb->end = acb->start + len; + + for (j=0; jacb[j]) { + state->acb[j] = acb; + return FIND_RET_WAIT; + } + } + } + } + + return FIND_RET_NONE; +} + +static void curl_multi_check_completion(BDRVCURLState *s) +{ + int msgs_in_queue; + + /* Try to find done transfers, so we can free the easy + * handle again. */ + for (;;) { + CURLMsg *msg; + msg = curl_multi_info_read(s->multi, &msgs_in_queue); + + /* Quit when there are no more completions */ + if (!msg) + break; + + if (msg->msg == CURLMSG_DONE) { + CURLState *state = NULL; + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, + (char **)&state); + + /* ACBs for successful messages get completed in curl_read_cb */ + if (msg->data.result != CURLE_OK) { + int i; + for (i = 0; i < CURL_NUM_ACB; i++) { + CURLAIOCB *acb = state->acb[i]; + + if (acb == NULL) { + continue; + } + + acb->common.cb(acb->common.opaque, -EIO); + qemu_aio_unref(acb); + state->acb[i] = NULL; + } + } + + curl_clean_state(state); + break; + } + } +} + +static void curl_multi_do(void *arg) +{ + CURLState *s = (CURLState *)arg; + int running; + int r; + + if (!s->s->multi) { + return; + } + + do { + r = curl_multi_socket_action(s->s->multi, s->sock_fd, 0, &running); + } while(r == CURLM_CALL_MULTI_PERFORM); + +} + +static void curl_multi_read(void *arg) +{ + CURLState *s = (CURLState *)arg; + + curl_multi_do(arg); + curl_multi_check_completion(s->s); +} + +static void curl_multi_timeout_do(void *arg) +{ +#ifdef NEED_CURL_TIMER_CALLBACK + BDRVCURLState *s = (BDRVCURLState *)arg; + int running; + + if (!s->multi) { + return; + } + + curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); + + curl_multi_check_completion(s); +#else + abort(); +#endif +} + +static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s) +{ + CURLState *state = NULL; + int i, j; + + do { + for (i=0; istates[i].acb[j]) + continue; + if (s->states[i].in_use) + continue; + + state = &s->states[i]; + state->in_use = 1; + break; + } + if (!state) { + aio_poll(bdrv_get_aio_context(bs), true); + } + } while(!state); + + if (!state->curl) { + state->curl = curl_easy_init(); + if (!state->curl) { + return NULL; + } + curl_easy_setopt(state->curl, CURLOPT_URL, s->url); + curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, + (long) s->sslverify); + if (s->cookie) { + curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); + } + curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout); + curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, + (void *)curl_read_cb); + curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state); + curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state); + curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1); + curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg); + curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1); + + /* Restrict supported protocols to avoid security issues in the more + * obscure protocols. For example, do not allow POP3/SMTP/IMAP see + * CVE-2013-0249. + * + * Restricting protocols is only supported from 7.19.4 upwards. + */ +#if LIBCURL_VERSION_NUM >= 0x071304 + curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS); + curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS); +#endif + +#ifdef DEBUG_VERBOSE + curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1); +#endif + } + + state->s = s; + + return state; +} + +static void curl_clean_state(CURLState *s) +{ + if (s->s->multi) + curl_multi_remove_handle(s->s->multi, s->curl); + s->in_use = 0; +} + +static void curl_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename)); +} + +static void curl_detach_aio_context(BlockDriverState *bs) +{ + BDRVCURLState *s = bs->opaque; + int i; + + for (i = 0; i < CURL_NUM_STATES; i++) { + if (s->states[i].in_use) { + curl_clean_state(&s->states[i]); + } + if (s->states[i].curl) { + curl_easy_cleanup(s->states[i].curl); + s->states[i].curl = NULL; + } + g_free(s->states[i].orig_buf); + s->states[i].orig_buf = NULL; + } + if (s->multi) { + curl_multi_cleanup(s->multi); + s->multi = NULL; + } + + timer_del(&s->timer); +} + +static void curl_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVCURLState *s = bs->opaque; + + aio_timer_init(new_context, &s->timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + curl_multi_timeout_do, s); + + assert(!s->multi); + s->multi = curl_multi_init(); + s->aio_context = new_context; + curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); +#ifdef NEED_CURL_TIMER_CALLBACK + curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); + curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); +#endif +} + +static QemuOptsList runtime_opts = { + .name = "curl", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = CURL_BLOCK_OPT_URL, + .type = QEMU_OPT_STRING, + .help = "URL to open", + }, + { + .name = CURL_BLOCK_OPT_READAHEAD, + .type = QEMU_OPT_SIZE, + .help = "Readahead size", + }, + { + .name = CURL_BLOCK_OPT_SSLVERIFY, + .type = QEMU_OPT_BOOL, + .help = "Verify SSL certificate" + }, + { + .name = CURL_BLOCK_OPT_TIMEOUT, + .type = QEMU_OPT_NUMBER, + .help = "Curl timeout" + }, + { + .name = CURL_BLOCK_OPT_COOKIE, + .type = QEMU_OPT_STRING, + .help = "Pass the cookie or list of cookies with each request" + }, + { /* end of list */ } + }, +}; + +static int curl_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVCURLState *s = bs->opaque; + CURLState *state = NULL; + QemuOpts *opts; + Error *local_err = NULL; + const char *file; + const char *cookie; + double d; + + static int inited = 0; + + if (flags & BDRV_O_RDWR) { + error_setg(errp, "curl block device does not support writes"); + return -EROFS; + } + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out_noclean; + } + + s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD, + READ_AHEAD_DEFAULT); + if ((s->readahead_size & 0x1ff) != 0) { + error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", + s->readahead_size); + goto out_noclean; + } + + s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT, + CURL_TIMEOUT_DEFAULT); + if (s->timeout > CURL_TIMEOUT_MAX) { + error_setg(errp, "timeout parameter is too large or negative"); + goto out_noclean; + } + + s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true); + + cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE); + s->cookie = g_strdup(cookie); + + file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL); + if (file == NULL) { + error_setg(errp, "curl block driver requires an 'url' option"); + goto out_noclean; + } + + if (!inited) { + curl_global_init(CURL_GLOBAL_ALL); + inited = 1; + } + + DPRINTF("CURL: Opening %s\n", file); + s->aio_context = bdrv_get_aio_context(bs); + s->url = g_strdup(file); + state = curl_init_state(bs, s); + if (!state) + goto out_noclean; + + // Get file size + + s->accept_range = false; + curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1); + curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, + curl_header_cb); + curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s); + if (curl_easy_perform(state->curl)) + goto out; + curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d); + if (d) + s->len = (size_t)d; + else if(!s->len) + goto out; + if ((!strncasecmp(s->url, "http://", strlen("http://")) + || !strncasecmp(s->url, "https://", strlen("https://"))) + && !s->accept_range) { + pstrcpy(state->errmsg, CURL_ERROR_SIZE, + "Server does not support 'range' (byte ranges)."); + goto out; + } + DPRINTF("CURL: Size = %zd\n", s->len); + + curl_clean_state(state); + curl_easy_cleanup(state->curl); + state->curl = NULL; + + curl_attach_aio_context(bs, bdrv_get_aio_context(bs)); + + qemu_opts_del(opts); + return 0; + +out: + error_setg(errp, "CURL: Error opening file: %s", state->errmsg); + curl_easy_cleanup(state->curl); + state->curl = NULL; +out_noclean: + g_free(s->cookie); + g_free(s->url); + qemu_opts_del(opts); + return -EINVAL; +} + +static const AIOCBInfo curl_aiocb_info = { + .aiocb_size = sizeof(CURLAIOCB), +}; + + +static void curl_readv_bh_cb(void *p) +{ + CURLState *state; + int running; + + CURLAIOCB *acb = p; + BDRVCURLState *s = acb->common.bs->opaque; + + qemu_bh_delete(acb->bh); + acb->bh = NULL; + + size_t start = acb->sector_num * SECTOR_SIZE; + size_t end; + + // In case we have the requested data already (e.g. read-ahead), + // we can just call the callback and be done. + switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) { + case FIND_RET_OK: + qemu_aio_unref(acb); + // fall through + case FIND_RET_WAIT: + return; + default: + break; + } + + // No cache found, so let's start a new request + state = curl_init_state(acb->common.bs, s); + if (!state) { + acb->common.cb(acb->common.opaque, -EIO); + qemu_aio_unref(acb); + return; + } + + acb->start = 0; + acb->end = (acb->nb_sectors * SECTOR_SIZE); + + state->buf_off = 0; + g_free(state->orig_buf); + state->buf_start = start; + state->buf_len = acb->end + s->readahead_size; + end = MIN(start + state->buf_len, s->len) - 1; + state->orig_buf = g_try_malloc(state->buf_len); + if (state->buf_len && state->orig_buf == NULL) { + curl_clean_state(state); + acb->common.cb(acb->common.opaque, -ENOMEM); + qemu_aio_unref(acb); + return; + } + state->acb[0] = acb; + + snprintf(state->range, 127, "%zd-%zd", start, end); + DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n", + (acb->nb_sectors * SECTOR_SIZE), start, state->range); + curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); + + curl_multi_add_handle(s->multi, state->curl); + + /* Tell curl it needs to kick things off */ + curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); +} + +static BlockAIOCB *curl_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + CURLAIOCB *acb; + + acb = qemu_aio_get(&curl_aiocb_info, bs, cb, opaque); + + acb->qiov = qiov; + acb->sector_num = sector_num; + acb->nb_sectors = nb_sectors; + + acb->bh = aio_bh_new(bdrv_get_aio_context(bs), curl_readv_bh_cb, acb); + qemu_bh_schedule(acb->bh); + return &acb->common; +} + +static void curl_close(BlockDriverState *bs) +{ + BDRVCURLState *s = bs->opaque; + + DPRINTF("CURL: Close\n"); + curl_detach_aio_context(bs); + + g_free(s->cookie); + g_free(s->url); +} + +static int64_t curl_getlength(BlockDriverState *bs) +{ + BDRVCURLState *s = bs->opaque; + return s->len; +} + +static BlockDriver bdrv_http = { + .format_name = "http", + .protocol_name = "http", + + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, + + .bdrv_aio_readv = curl_aio_readv, + + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, +}; + +static BlockDriver bdrv_https = { + .format_name = "https", + .protocol_name = "https", + + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, + + .bdrv_aio_readv = curl_aio_readv, + + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, +}; + +static BlockDriver bdrv_ftp = { + .format_name = "ftp", + .protocol_name = "ftp", + + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, + + .bdrv_aio_readv = curl_aio_readv, + + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, +}; + +static BlockDriver bdrv_ftps = { + .format_name = "ftps", + .protocol_name = "ftps", + + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, + + .bdrv_aio_readv = curl_aio_readv, + + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, +}; + +static BlockDriver bdrv_tftp = { + .format_name = "tftp", + .protocol_name = "tftp", + + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, + + .bdrv_aio_readv = curl_aio_readv, + + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, +}; + +static void curl_block_init(void) +{ + bdrv_register(&bdrv_http); + bdrv_register(&bdrv_https); + bdrv_register(&bdrv_ftp); + bdrv_register(&bdrv_ftps); + bdrv_register(&bdrv_tftp); +} + +block_init(curl_block_init); diff --git a/block/dmg.c b/block/dmg.c new file mode 100644 index 0000000..825c49d --- /dev/null +++ b/block/dmg.c @@ -0,0 +1,724 @@ +/* + * QEMU Block driver for DMG images + * + * Copyright (c) 2004 Johannes E. Schindelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/bswap.h" +#include "qemu/module.h" +#include +#ifdef CONFIG_BZIP2 +#include +#endif +#include + +enum { + /* Limit chunk sizes to prevent unreasonable amounts of memory being used + * or truncating when converting to 32-bit types + */ + DMG_LENGTHS_MAX = 64 * 1024 * 1024, /* 64 MB */ + DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, +}; + +typedef struct BDRVDMGState { + CoMutex lock; + /* each chunk contains a certain number of sectors, + * offsets[i] is the offset in the .dmg file, + * lengths[i] is the length of the compressed chunk, + * sectors[i] is the sector beginning at offsets[i], + * sectorcounts[i] is the number of sectors in that chunk, + * the sectors array is ordered + * 0<=i 4 && !strcmp(filename + len - 4, ".dmg")) { + return 2; + } + return 0; +} + +static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) +{ + uint64_t buffer; + int ret; + + ret = bdrv_pread(bs->file, offset, &buffer, 8); + if (ret < 0) { + return ret; + } + + *result = be64_to_cpu(buffer); + return 0; +} + +static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) +{ + uint32_t buffer; + int ret; + + ret = bdrv_pread(bs->file, offset, &buffer, 4); + if (ret < 0) { + return ret; + } + + *result = be32_to_cpu(buffer); + return 0; +} + +static inline uint64_t buff_read_uint64(const uint8_t *buffer, int64_t offset) +{ + return be64_to_cpu(*(uint64_t *)&buffer[offset]); +} + +static inline uint32_t buff_read_uint32(const uint8_t *buffer, int64_t offset) +{ + return be32_to_cpu(*(uint32_t *)&buffer[offset]); +} + +/* Increase max chunk sizes, if necessary. This function is used to calculate + * the buffer sizes needed for compressed/uncompressed chunk I/O. + */ +static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, + uint32_t *max_compressed_size, + uint32_t *max_sectors_per_chunk) +{ + uint32_t compressed_size = 0; + uint32_t uncompressed_sectors = 0; + + switch (s->types[chunk]) { + case 0x80000005: /* zlib compressed */ + case 0x80000006: /* bzip2 compressed */ + compressed_size = s->lengths[chunk]; + uncompressed_sectors = s->sectorcounts[chunk]; + break; + case 1: /* copy */ + uncompressed_sectors = (s->lengths[chunk] + 511) / 512; + break; + case 2: /* zero */ + /* as the all-zeroes block may be large, it is treated specially: the + * sector is not copied from a large buffer, a simple memset is used + * instead. Therefore uncompressed_sectors does not need to be set. */ + break; + } + + if (compressed_size > *max_compressed_size) { + *max_compressed_size = compressed_size; + } + if (uncompressed_sectors > *max_sectors_per_chunk) { + *max_sectors_per_chunk = uncompressed_sectors; + } +} + +static int64_t dmg_find_koly_offset(BlockDriverState *file_bs, Error **errp) +{ + int64_t length; + int64_t offset = 0; + uint8_t buffer[515]; + int i, ret; + + /* bdrv_getlength returns a multiple of block size (512), rounded up. Since + * dmg images can have odd sizes, try to look for the "koly" magic which + * marks the begin of the UDIF trailer (512 bytes). This magic can be found + * in the last 511 bytes of the second-last sector or the first 4 bytes of + * the last sector (search space: 515 bytes) */ + length = bdrv_getlength(file_bs); + if (length < 0) { + error_setg_errno(errp, -length, + "Failed to get file size while reading UDIF trailer"); + return length; + } else if (length < 512) { + error_setg(errp, "dmg file must be at least 512 bytes long"); + return -EINVAL; + } + if (length > 511 + 512) { + offset = length - 511 - 512; + } + length = length < 515 ? length : 515; + ret = bdrv_pread(file_bs, offset, buffer, length); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed while reading UDIF trailer"); + return ret; + } + for (i = 0; i < length - 3; i++) { + if (buffer[i] == 'k' && buffer[i+1] == 'o' && + buffer[i+2] == 'l' && buffer[i+3] == 'y') { + return offset + i; + } + } + error_setg(errp, "Could not locate UDIF trailer in dmg file"); + return -EINVAL; +} + +/* used when building the sector table */ +typedef struct DmgHeaderState { + /* used internally by dmg_read_mish_block to remember offsets of blocks + * across calls */ + uint64_t data_fork_offset; + /* exported for dmg_open */ + uint32_t max_compressed_size; + uint32_t max_sectors_per_chunk; +} DmgHeaderState; + +static bool dmg_is_known_block_type(uint32_t entry_type) +{ + switch (entry_type) { + case 0x00000001: /* uncompressed */ + case 0x00000002: /* zeroes */ + case 0x80000005: /* zlib */ +#ifdef CONFIG_BZIP2 + case 0x80000006: /* bzip2 */ +#endif + return true; + default: + return false; + } +} + +static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, + uint8_t *buffer, uint32_t count) +{ + uint32_t type, i; + int ret; + size_t new_size; + uint32_t chunk_count; + int64_t offset = 0; + uint64_t data_offset; + uint64_t in_offset = ds->data_fork_offset; + uint64_t out_offset; + + type = buff_read_uint32(buffer, offset); + /* skip data that is not a valid MISH block (invalid magic or too small) */ + if (type != 0x6d697368 || count < 244) { + /* assume success for now */ + return 0; + } + + /* chunk offsets are relative to this sector number */ + out_offset = buff_read_uint64(buffer, offset + 8); + + /* location in data fork for (compressed) blob (in bytes) */ + data_offset = buff_read_uint64(buffer, offset + 0x18); + in_offset += data_offset; + + /* move to begin of chunk entries */ + offset += 204; + + chunk_count = (count - 204) / 40; + new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); + s->types = g_realloc(s->types, new_size / 2); + s->offsets = g_realloc(s->offsets, new_size); + s->lengths = g_realloc(s->lengths, new_size); + s->sectors = g_realloc(s->sectors, new_size); + s->sectorcounts = g_realloc(s->sectorcounts, new_size); + + for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) { + s->types[i] = buff_read_uint32(buffer, offset); + if (!dmg_is_known_block_type(s->types[i])) { + chunk_count--; + i--; + offset += 40; + continue; + } + + /* sector number */ + s->sectors[i] = buff_read_uint64(buffer, offset + 8); + s->sectors[i] += out_offset; + + /* sector count */ + s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); + + /* all-zeroes sector (type 2) does not need to be "uncompressed" and can + * therefore be unbounded. */ + if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { + error_report("sector count %" PRIu64 " for chunk %" PRIu32 + " is larger than max (%u)", + s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); + ret = -EINVAL; + goto fail; + } + + /* offset in (compressed) data fork */ + s->offsets[i] = buff_read_uint64(buffer, offset + 0x18); + s->offsets[i] += in_offset; + + /* length in (compressed) data fork */ + s->lengths[i] = buff_read_uint64(buffer, offset + 0x20); + + if (s->lengths[i] > DMG_LENGTHS_MAX) { + error_report("length %" PRIu64 " for chunk %" PRIu32 + " is larger than max (%u)", + s->lengths[i], i, DMG_LENGTHS_MAX); + ret = -EINVAL; + goto fail; + } + + update_max_chunk_size(s, i, &ds->max_compressed_size, + &ds->max_sectors_per_chunk); + offset += 40; + } + s->n_chunks += chunk_count; + return 0; + +fail: + return ret; +} + +static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, + uint64_t info_begin, uint64_t info_length) +{ + BDRVDMGState *s = bs->opaque; + int ret; + uint32_t count, rsrc_data_offset; + uint8_t *buffer = NULL; + uint64_t info_end; + uint64_t offset; + + /* read offset from begin of resource fork (info_begin) to resource data */ + ret = read_uint32(bs, info_begin, &rsrc_data_offset); + if (ret < 0) { + goto fail; + } else if (rsrc_data_offset > info_length) { + ret = -EINVAL; + goto fail; + } + + /* read length of resource data */ + ret = read_uint32(bs, info_begin + 8, &count); + if (ret < 0) { + goto fail; + } else if (count == 0 || rsrc_data_offset + count > info_length) { + ret = -EINVAL; + goto fail; + } + + /* begin of resource data (consisting of one or more resources) */ + offset = info_begin + rsrc_data_offset; + + /* end of resource data (there is possibly a following resource map + * which will be ignored). */ + info_end = offset + count; + + /* read offsets (mish blocks) from one or more resources in resource data */ + while (offset < info_end) { + /* size of following resource */ + ret = read_uint32(bs, offset, &count); + if (ret < 0) { + goto fail; + } else if (count == 0 || count > info_end - offset) { + ret = -EINVAL; + goto fail; + } + offset += 4; + + buffer = g_realloc(buffer, count); + ret = bdrv_pread(bs->file, offset, buffer, count); + if (ret < 0) { + goto fail; + } + + ret = dmg_read_mish_block(s, ds, buffer, count); + if (ret < 0) { + goto fail; + } + /* advance offset by size of resource */ + offset += count; + } + ret = 0; + +fail: + g_free(buffer); + return ret; +} + +static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, + uint64_t info_begin, uint64_t info_length) +{ + BDRVDMGState *s = bs->opaque; + int ret; + uint8_t *buffer = NULL; + char *data_begin, *data_end; + + /* Have at least some length to avoid NULL for g_malloc. Attempt to set a + * safe upper cap on the data length. A test sample had a XML length of + * about 1 MiB. */ + if (info_length == 0 || info_length > 16 * 1024 * 1024) { + ret = -EINVAL; + goto fail; + } + + buffer = g_malloc(info_length + 1); + buffer[info_length] = '\0'; + ret = bdrv_pread(bs->file, info_begin, buffer, info_length); + if (ret != info_length) { + ret = -EINVAL; + goto fail; + } + + /* look for .... The data is 284 (0x11c) bytes after base64 + * decode. The actual data element has 431 (0x1af) bytes which includes tabs + * and line feeds. */ + data_end = (char *)buffer; + while ((data_begin = strstr(data_end, "")) != NULL) { + guchar *mish; + gsize out_len = 0; + + data_begin += 6; + data_end = strstr(data_begin, ""); + /* malformed XML? */ + if (data_end == NULL) { + ret = -EINVAL; + goto fail; + } + *data_end++ = '\0'; + mish = g_base64_decode(data_begin, &out_len); + ret = dmg_read_mish_block(s, ds, mish, (uint32_t)out_len); + g_free(mish); + if (ret < 0) { + goto fail; + } + } + ret = 0; + +fail: + g_free(buffer); + return ret; +} + +static int dmg_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVDMGState *s = bs->opaque; + DmgHeaderState ds; + uint64_t rsrc_fork_offset, rsrc_fork_length; + uint64_t plist_xml_offset, plist_xml_length; + int64_t offset; + int ret; + + bs->read_only = 1; + s->n_chunks = 0; + s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; + /* used by dmg_read_mish_block to keep track of the current I/O position */ + ds.data_fork_offset = 0; + ds.max_compressed_size = 1; + ds.max_sectors_per_chunk = 1; + + /* locate the UDIF trailer */ + offset = dmg_find_koly_offset(bs->file, errp); + if (offset < 0) { + ret = offset; + goto fail; + } + + /* offset of data fork (DataForkOffset) */ + ret = read_uint64(bs, offset + 0x18, &ds.data_fork_offset); + if (ret < 0) { + goto fail; + } else if (ds.data_fork_offset > offset) { + ret = -EINVAL; + goto fail; + } + + /* offset of resource fork (RsrcForkOffset) */ + ret = read_uint64(bs, offset + 0x28, &rsrc_fork_offset); + if (ret < 0) { + goto fail; + } + ret = read_uint64(bs, offset + 0x30, &rsrc_fork_length); + if (ret < 0) { + goto fail; + } + if (rsrc_fork_offset >= offset || + rsrc_fork_length > offset - rsrc_fork_offset) { + ret = -EINVAL; + goto fail; + } + /* offset of property list (XMLOffset) */ + ret = read_uint64(bs, offset + 0xd8, &plist_xml_offset); + if (ret < 0) { + goto fail; + } + ret = read_uint64(bs, offset + 0xe0, &plist_xml_length); + if (ret < 0) { + goto fail; + } + if (plist_xml_offset >= offset || + plist_xml_length > offset - plist_xml_offset) { + ret = -EINVAL; + goto fail; + } + ret = read_uint64(bs, offset + 0x1ec, (uint64_t *)&bs->total_sectors); + if (ret < 0) { + goto fail; + } + if (bs->total_sectors < 0) { + ret = -EINVAL; + goto fail; + } + if (rsrc_fork_length != 0) { + ret = dmg_read_resource_fork(bs, &ds, + rsrc_fork_offset, rsrc_fork_length); + if (ret < 0) { + goto fail; + } + } else if (plist_xml_length != 0) { + ret = dmg_read_plist_xml(bs, &ds, plist_xml_offset, plist_xml_length); + if (ret < 0) { + goto fail; + } + } else { + ret = -EINVAL; + goto fail; + } + + /* initialize zlib engine */ + s->compressed_chunk = qemu_try_blockalign(bs->file, + ds.max_compressed_size + 1); + s->uncompressed_chunk = qemu_try_blockalign(bs->file, + 512 * ds.max_sectors_per_chunk); + if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) { + ret = -ENOMEM; + goto fail; + } + + if (inflateInit(&s->zstream) != Z_OK) { + ret = -EINVAL; + goto fail; + } + + s->current_chunk = s->n_chunks; + + qemu_co_mutex_init(&s->lock); + return 0; + +fail: + g_free(s->types); + g_free(s->offsets); + g_free(s->lengths); + g_free(s->sectors); + g_free(s->sectorcounts); + qemu_vfree(s->compressed_chunk); + qemu_vfree(s->uncompressed_chunk); + return ret; +} + +static inline int is_sector_in_chunk(BDRVDMGState* s, + uint32_t chunk_num, uint64_t sector_num) +{ + if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num || + s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) { + return 0; + } else { + return -1; + } +} + +static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) +{ + /* binary search */ + uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; + while (chunk1 != chunk2) { + chunk3 = (chunk1 + chunk2) / 2; + if (s->sectors[chunk3] > sector_num) { + chunk2 = chunk3; + } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { + return chunk3; + } else { + chunk1 = chunk3; + } + } + return s->n_chunks; /* error */ +} + +static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) +{ + BDRVDMGState *s = bs->opaque; + + if (!is_sector_in_chunk(s, s->current_chunk, sector_num)) { + int ret; + uint32_t chunk = search_chunk(s, sector_num); +#ifdef CONFIG_BZIP2 + uint64_t total_out; +#endif + + if (chunk >= s->n_chunks) { + return -1; + } + + s->current_chunk = s->n_chunks; + switch (s->types[chunk]) { /* block entry type */ + case 0x80000005: { /* zlib compressed */ + /* we need to buffer, because only the chunk as whole can be + * inflated. */ + ret = bdrv_pread(bs->file, s->offsets[chunk], + s->compressed_chunk, s->lengths[chunk]); + if (ret != s->lengths[chunk]) { + return -1; + } + + s->zstream.next_in = s->compressed_chunk; + s->zstream.avail_in = s->lengths[chunk]; + s->zstream.next_out = s->uncompressed_chunk; + s->zstream.avail_out = 512 * s->sectorcounts[chunk]; + ret = inflateReset(&s->zstream); + if (ret != Z_OK) { + return -1; + } + ret = inflate(&s->zstream, Z_FINISH); + if (ret != Z_STREAM_END || + s->zstream.total_out != 512 * s->sectorcounts[chunk]) { + return -1; + } + break; } +#ifdef CONFIG_BZIP2 + case 0x80000006: /* bzip2 compressed */ + /* we need to buffer, because only the chunk as whole can be + * inflated. */ + ret = bdrv_pread(bs->file, s->offsets[chunk], + s->compressed_chunk, s->lengths[chunk]); + if (ret != s->lengths[chunk]) { + return -1; + } + + ret = BZ2_bzDecompressInit(&s->bzstream, 0, 0); + if (ret != BZ_OK) { + return -1; + } + s->bzstream.next_in = (char *)s->compressed_chunk; + s->bzstream.avail_in = (unsigned int) s->lengths[chunk]; + s->bzstream.next_out = (char *)s->uncompressed_chunk; + s->bzstream.avail_out = (unsigned int) 512 * s->sectorcounts[chunk]; + ret = BZ2_bzDecompress(&s->bzstream); + total_out = ((uint64_t)s->bzstream.total_out_hi32 << 32) + + s->bzstream.total_out_lo32; + BZ2_bzDecompressEnd(&s->bzstream); + if (ret != BZ_STREAM_END || + total_out != 512 * s->sectorcounts[chunk]) { + return -1; + } + break; +#endif /* CONFIG_BZIP2 */ + case 1: /* copy */ + ret = bdrv_pread(bs->file, s->offsets[chunk], + s->uncompressed_chunk, s->lengths[chunk]); + if (ret != s->lengths[chunk]) { + return -1; + } + break; + case 2: /* zero */ + /* see dmg_read, it is treated specially. No buffer needs to be + * pre-filled, the zeroes can be set directly. */ + break; + } + s->current_chunk = chunk; + } + return 0; +} + +static int dmg_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVDMGState *s = bs->opaque; + int i; + + for (i = 0; i < nb_sectors; i++) { + uint32_t sector_offset_in_chunk; + if (dmg_read_chunk(bs, sector_num + i) != 0) { + return -1; + } + /* Special case: current chunk is all zeroes. Do not perform a memcpy as + * s->uncompressed_chunk may be too small to cover the large all-zeroes + * section. dmg_read_chunk is called to find s->current_chunk */ + if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */ + memset(buf + i * 512, 0, 512); + continue; + } + sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk]; + memcpy(buf + i * 512, + s->uncompressed_chunk + sector_offset_in_chunk * 512, 512); + } + return 0; +} + +static coroutine_fn int dmg_co_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVDMGState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = dmg_read(bs, sector_num, buf, nb_sectors); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static void dmg_close(BlockDriverState *bs) +{ + BDRVDMGState *s = bs->opaque; + + g_free(s->types); + g_free(s->offsets); + g_free(s->lengths); + g_free(s->sectors); + g_free(s->sectorcounts); + qemu_vfree(s->compressed_chunk); + qemu_vfree(s->uncompressed_chunk); + + inflateEnd(&s->zstream); +} + +static BlockDriver bdrv_dmg = { + .format_name = "dmg", + .instance_size = sizeof(BDRVDMGState), + .bdrv_probe = dmg_probe, + .bdrv_open = dmg_open, + .bdrv_read = dmg_co_read, + .bdrv_close = dmg_close, +}; + +static void bdrv_dmg_init(void) +{ + bdrv_register(&bdrv_dmg); +} + +block_init(bdrv_dmg_init); diff --git a/block/gluster.c b/block/gluster.c new file mode 100644 index 0000000..1eb3a8c --- /dev/null +++ b/block/gluster.c @@ -0,0 +1,833 @@ +/* + * GlusterFS backend for QEMU + * + * Copyright (C) 2012 Bharata B Rao + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#include +#include "block/block_int.h" +#include "qemu/uri.h" + +typedef struct GlusterAIOCB { + int64_t size; + int ret; + QEMUBH *bh; + Coroutine *coroutine; + AioContext *aio_context; +} GlusterAIOCB; + +typedef struct BDRVGlusterState { + struct glfs *glfs; + struct glfs_fd *fd; +} BDRVGlusterState; + +typedef struct GlusterConf { + char *server; + int port; + char *volname; + char *image; + char *transport; +} GlusterConf; + +static void qemu_gluster_gconf_free(GlusterConf *gconf) +{ + if (gconf) { + g_free(gconf->server); + g_free(gconf->volname); + g_free(gconf->image); + g_free(gconf->transport); + g_free(gconf); + } +} + +static int parse_volume_options(GlusterConf *gconf, char *path) +{ + char *p, *q; + + if (!path) { + return -EINVAL; + } + + /* volume */ + p = q = path + strspn(path, "/"); + p += strcspn(p, "/"); + if (*p == '\0') { + return -EINVAL; + } + gconf->volname = g_strndup(q, p - q); + + /* image */ + p += strspn(p, "/"); + if (*p == '\0') { + return -EINVAL; + } + gconf->image = g_strdup(p); + return 0; +} + +/* + * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...] + * + * 'gluster' is the protocol. + * + * 'transport' specifies the transport type used to connect to gluster + * management daemon (glusterd). Valid transport types are + * tcp, unix and rdma. If a transport type isn't specified, then tcp + * type is assumed. + * + * 'server' specifies the server where the volume file specification for + * the given volume resides. This can be either hostname, ipv4 address + * or ipv6 address. ipv6 address needs to be within square brackets [ ]. + * If transport type is 'unix', then 'server' field should not be specified. + * The 'socket' field needs to be populated with the path to unix domain + * socket. + * + * 'port' is the port number on which glusterd is listening. This is optional + * and if not specified, QEMU will send 0 which will make gluster to use the + * default port. If the transport type is unix, then 'port' should not be + * specified. + * + * 'volname' is the name of the gluster volume which contains the VM image. + * + * 'image' is the path to the actual VM image that resides on gluster volume. + * + * Examples: + * + * file=gluster://1.2.3.4/testvol/a.img + * file=gluster+tcp://1.2.3.4/testvol/a.img + * file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img + * file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img + * file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img + * file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img + * file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket + * file=gluster+rdma://1.2.3.4:24007/testvol/a.img + */ +static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename) +{ + URI *uri; + QueryParams *qp = NULL; + bool is_unix = false; + int ret = 0; + + uri = uri_parse(filename); + if (!uri) { + return -EINVAL; + } + + /* transport */ + if (!uri->scheme || !strcmp(uri->scheme, "gluster")) { + gconf->transport = g_strdup("tcp"); + } else if (!strcmp(uri->scheme, "gluster+tcp")) { + gconf->transport = g_strdup("tcp"); + } else if (!strcmp(uri->scheme, "gluster+unix")) { + gconf->transport = g_strdup("unix"); + is_unix = true; + } else if (!strcmp(uri->scheme, "gluster+rdma")) { + gconf->transport = g_strdup("rdma"); + } else { + ret = -EINVAL; + goto out; + } + + ret = parse_volume_options(gconf, uri->path); + if (ret < 0) { + goto out; + } + + qp = query_params_parse(uri->query); + if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { + ret = -EINVAL; + goto out; + } + + if (is_unix) { + if (uri->server || uri->port) { + ret = -EINVAL; + goto out; + } + if (strcmp(qp->p[0].name, "socket")) { + ret = -EINVAL; + goto out; + } + gconf->server = g_strdup(qp->p[0].value); + } else { + gconf->server = g_strdup(uri->server ? uri->server : "localhost"); + gconf->port = uri->port; + } + +out: + if (qp) { + query_params_free(qp); + } + uri_free(uri); + return ret; +} + +static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename, + Error **errp) +{ + struct glfs *glfs = NULL; + int ret; + int old_errno; + + ret = qemu_gluster_parseuri(gconf, filename); + if (ret < 0) { + error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/" + "volname/image[?socket=...]"); + errno = -ret; + goto out; + } + + glfs = glfs_new(gconf->volname); + if (!glfs) { + goto out; + } + + ret = glfs_set_volfile_server(glfs, gconf->transport, gconf->server, + gconf->port); + if (ret < 0) { + goto out; + } + + /* + * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when + * GlusterFS makes GF_LOG_* macros available to libgfapi users. + */ + ret = glfs_set_logging(glfs, "-", 4); + if (ret < 0) { + goto out; + } + + ret = glfs_init(glfs); + if (ret) { + error_setg_errno(errp, errno, + "Gluster connection failed for server=%s port=%d " + "volume=%s image=%s transport=%s", gconf->server, + gconf->port, gconf->volname, gconf->image, + gconf->transport); + + /* glfs_init sometimes doesn't set errno although docs suggest that */ + if (errno == 0) + errno = EINVAL; + + goto out; + } + return glfs; + +out: + if (glfs) { + old_errno = errno; + glfs_fini(glfs); + errno = old_errno; + } + return NULL; +} + +static void qemu_gluster_complete_aio(void *opaque) +{ + GlusterAIOCB *acb = (GlusterAIOCB *)opaque; + + qemu_bh_delete(acb->bh); + acb->bh = NULL; + qemu_coroutine_enter(acb->coroutine, NULL); +} + +/* + * AIO callback routine called from GlusterFS thread. + */ +static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg) +{ + GlusterAIOCB *acb = (GlusterAIOCB *)arg; + + if (!ret || ret == acb->size) { + acb->ret = 0; /* Success */ + } else if (ret < 0) { + acb->ret = ret; /* Read/Write failed */ + } else { + acb->ret = -EIO; /* Partial read/write - fail it */ + } + + acb->bh = aio_bh_new(acb->aio_context, qemu_gluster_complete_aio, acb); + qemu_bh_schedule(acb->bh); +} + +/* TODO Convert to fine grained options */ +static QemuOptsList runtime_opts = { + .name = "gluster", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "URL to the gluster image", + }, + { /* end of list */ } + }, +}; + +static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags) +{ + assert(open_flags != NULL); + + *open_flags |= O_BINARY; + + if (bdrv_flags & BDRV_O_RDWR) { + *open_flags |= O_RDWR; + } else { + *open_flags |= O_RDONLY; + } + + if ((bdrv_flags & BDRV_O_NOCACHE)) { + *open_flags |= O_DIRECT; + } +} + +static int qemu_gluster_open(BlockDriverState *bs, QDict *options, + int bdrv_flags, Error **errp) +{ + BDRVGlusterState *s = bs->opaque; + int open_flags = 0; + int ret = 0; + GlusterConf *gconf = g_new0(GlusterConf, 1); + QemuOpts *opts; + Error *local_err = NULL; + const char *filename; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + filename = qemu_opt_get(opts, "filename"); + + s->glfs = qemu_gluster_init(gconf, filename, errp); + if (!s->glfs) { + ret = -errno; + goto out; + } + + qemu_gluster_parse_flags(bdrv_flags, &open_flags); + + s->fd = glfs_open(s->glfs, gconf->image, open_flags); + if (!s->fd) { + ret = -errno; + } + +out: + qemu_opts_del(opts); + qemu_gluster_gconf_free(gconf); + if (!ret) { + return ret; + } + if (s->fd) { + glfs_close(s->fd); + } + if (s->glfs) { + glfs_fini(s->glfs); + } + return ret; +} + +typedef struct BDRVGlusterReopenState { + struct glfs *glfs; + struct glfs_fd *fd; +} BDRVGlusterReopenState; + + +static int qemu_gluster_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + int ret = 0; + BDRVGlusterReopenState *reop_s; + GlusterConf *gconf = NULL; + int open_flags = 0; + + assert(state != NULL); + assert(state->bs != NULL); + + state->opaque = g_new0(BDRVGlusterReopenState, 1); + reop_s = state->opaque; + + qemu_gluster_parse_flags(state->flags, &open_flags); + + gconf = g_new0(GlusterConf, 1); + + reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, errp); + if (reop_s->glfs == NULL) { + ret = -errno; + goto exit; + } + + reop_s->fd = glfs_open(reop_s->glfs, gconf->image, open_flags); + if (reop_s->fd == NULL) { + /* reops->glfs will be cleaned up in _abort */ + ret = -errno; + goto exit; + } + +exit: + /* state->opaque will be freed in either the _abort or _commit */ + qemu_gluster_gconf_free(gconf); + return ret; +} + +static void qemu_gluster_reopen_commit(BDRVReopenState *state) +{ + BDRVGlusterReopenState *reop_s = state->opaque; + BDRVGlusterState *s = state->bs->opaque; + + + /* close the old */ + if (s->fd) { + glfs_close(s->fd); + } + if (s->glfs) { + glfs_fini(s->glfs); + } + + /* use the newly opened image / connection */ + s->fd = reop_s->fd; + s->glfs = reop_s->glfs; + + g_free(state->opaque); + state->opaque = NULL; + + return; +} + + +static void qemu_gluster_reopen_abort(BDRVReopenState *state) +{ + BDRVGlusterReopenState *reop_s = state->opaque; + + if (reop_s == NULL) { + return; + } + + if (reop_s->fd) { + glfs_close(reop_s->fd); + } + + if (reop_s->glfs) { + glfs_fini(reop_s->glfs); + } + + g_free(state->opaque); + state->opaque = NULL; + + return; +} + +#ifdef CONFIG_GLUSTERFS_ZEROFILL +static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) +{ + int ret; + GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); + BDRVGlusterState *s = bs->opaque; + off_t size = nb_sectors * BDRV_SECTOR_SIZE; + off_t offset = sector_num * BDRV_SECTOR_SIZE; + + acb->size = size; + acb->ret = 0; + acb->coroutine = qemu_coroutine_self(); + acb->aio_context = bdrv_get_aio_context(bs); + + ret = glfs_zerofill_async(s->fd, offset, size, &gluster_finish_aiocb, acb); + if (ret < 0) { + ret = -errno; + goto out; + } + + qemu_coroutine_yield(); + ret = acb->ret; + +out: + g_slice_free(GlusterAIOCB, acb); + return ret; +} + +static inline bool gluster_supports_zerofill(void) +{ + return 1; +} + +static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset, + int64_t size) +{ + return glfs_zerofill(fd, offset, size); +} + +#else +static inline bool gluster_supports_zerofill(void) +{ + return 0; +} + +static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset, + int64_t size) +{ + return 0; +} +#endif + +static int qemu_gluster_create(const char *filename, + QemuOpts *opts, Error **errp) +{ + struct glfs *glfs; + struct glfs_fd *fd; + int ret = 0; + int prealloc = 0; + int64_t total_size = 0; + char *tmp = NULL; + GlusterConf *gconf = g_new0(GlusterConf, 1); + + glfs = qemu_gluster_init(gconf, filename, errp); + if (!glfs) { + ret = -errno; + goto out; + } + + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + + tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); + if (!tmp || !strcmp(tmp, "off")) { + prealloc = 0; + } else if (!strcmp(tmp, "full") && + gluster_supports_zerofill()) { + prealloc = 1; + } else { + error_setg(errp, "Invalid preallocation mode: '%s'" + " or GlusterFS doesn't support zerofill API", + tmp); + ret = -EINVAL; + goto out; + } + + fd = glfs_creat(glfs, gconf->image, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR); + if (!fd) { + ret = -errno; + } else { + if (!glfs_ftruncate(fd, total_size)) { + if (prealloc && qemu_gluster_zerofill(fd, 0, total_size)) { + ret = -errno; + } + } else { + ret = -errno; + } + + if (glfs_close(fd) != 0) { + ret = -errno; + } + } +out: + g_free(tmp); + qemu_gluster_gconf_free(gconf); + if (glfs) { + glfs_fini(glfs); + } + return ret; +} + +static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write) +{ + int ret; + GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); + BDRVGlusterState *s = bs->opaque; + size_t size = nb_sectors * BDRV_SECTOR_SIZE; + off_t offset = sector_num * BDRV_SECTOR_SIZE; + + acb->size = size; + acb->ret = 0; + acb->coroutine = qemu_coroutine_self(); + acb->aio_context = bdrv_get_aio_context(bs); + + if (write) { + ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0, + &gluster_finish_aiocb, acb); + } else { + ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0, + &gluster_finish_aiocb, acb); + } + + if (ret < 0) { + ret = -errno; + goto out; + } + + qemu_coroutine_yield(); + ret = acb->ret; + +out: + g_slice_free(GlusterAIOCB, acb); + return ret; +} + +static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset) +{ + int ret; + BDRVGlusterState *s = bs->opaque; + + ret = glfs_ftruncate(s->fd, offset); + if (ret < 0) { + return -errno; + } + + return 0; +} + +static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 0); +} + +static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); +} + +static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs) +{ + int ret; + GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); + BDRVGlusterState *s = bs->opaque; + + acb->size = 0; + acb->ret = 0; + acb->coroutine = qemu_coroutine_self(); + acb->aio_context = bdrv_get_aio_context(bs); + + ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb); + if (ret < 0) { + ret = -errno; + goto out; + } + + qemu_coroutine_yield(); + ret = acb->ret; + +out: + g_slice_free(GlusterAIOCB, acb); + return ret; +} + +#ifdef CONFIG_GLUSTERFS_DISCARD +static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors) +{ + int ret; + GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); + BDRVGlusterState *s = bs->opaque; + size_t size = nb_sectors * BDRV_SECTOR_SIZE; + off_t offset = sector_num * BDRV_SECTOR_SIZE; + + acb->size = 0; + acb->ret = 0; + acb->coroutine = qemu_coroutine_self(); + acb->aio_context = bdrv_get_aio_context(bs); + + ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb); + if (ret < 0) { + ret = -errno; + goto out; + } + + qemu_coroutine_yield(); + ret = acb->ret; + +out: + g_slice_free(GlusterAIOCB, acb); + return ret; +} +#endif + +static int64_t qemu_gluster_getlength(BlockDriverState *bs) +{ + BDRVGlusterState *s = bs->opaque; + int64_t ret; + + ret = glfs_lseek(s->fd, 0, SEEK_END); + if (ret < 0) { + return -errno; + } else { + return ret; + } +} + +static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs) +{ + BDRVGlusterState *s = bs->opaque; + struct stat st; + int ret; + + ret = glfs_fstat(s->fd, &st); + if (ret < 0) { + return -errno; + } else { + return st.st_blocks * 512; + } +} + +static void qemu_gluster_close(BlockDriverState *bs) +{ + BDRVGlusterState *s = bs->opaque; + + if (s->fd) { + glfs_close(s->fd); + s->fd = NULL; + } + glfs_fini(s->glfs); +} + +static int qemu_gluster_has_zero_init(BlockDriverState *bs) +{ + /* GlusterFS volume could be backed by a block device */ + return 0; +} + +static QemuOptsList qemu_gluster_create_opts = { + .name = "qemu-gluster-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(qemu_gluster_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_PREALLOC, + .type = QEMU_OPT_STRING, + .help = "Preallocation mode (allowed values: off, full)" + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_gluster = { + .format_name = "gluster", + .protocol_name = "gluster", + .instance_size = sizeof(BDRVGlusterState), + .bdrv_needs_filename = true, + .bdrv_file_open = qemu_gluster_open, + .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, + .bdrv_reopen_commit = qemu_gluster_reopen_commit, + .bdrv_reopen_abort = qemu_gluster_reopen_abort, + .bdrv_close = qemu_gluster_close, + .bdrv_create = qemu_gluster_create, + .bdrv_getlength = qemu_gluster_getlength, + .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_readv = qemu_gluster_co_readv, + .bdrv_co_writev = qemu_gluster_co_writev, + .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, + .bdrv_has_zero_init = qemu_gluster_has_zero_init, +#ifdef CONFIG_GLUSTERFS_DISCARD + .bdrv_co_discard = qemu_gluster_co_discard, +#endif +#ifdef CONFIG_GLUSTERFS_ZEROFILL + .bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes, +#endif + .create_opts = &qemu_gluster_create_opts, +}; + +static BlockDriver bdrv_gluster_tcp = { + .format_name = "gluster", + .protocol_name = "gluster+tcp", + .instance_size = sizeof(BDRVGlusterState), + .bdrv_needs_filename = true, + .bdrv_file_open = qemu_gluster_open, + .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, + .bdrv_reopen_commit = qemu_gluster_reopen_commit, + .bdrv_reopen_abort = qemu_gluster_reopen_abort, + .bdrv_close = qemu_gluster_close, + .bdrv_create = qemu_gluster_create, + .bdrv_getlength = qemu_gluster_getlength, + .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_readv = qemu_gluster_co_readv, + .bdrv_co_writev = qemu_gluster_co_writev, + .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, + .bdrv_has_zero_init = qemu_gluster_has_zero_init, +#ifdef CONFIG_GLUSTERFS_DISCARD + .bdrv_co_discard = qemu_gluster_co_discard, +#endif +#ifdef CONFIG_GLUSTERFS_ZEROFILL + .bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes, +#endif + .create_opts = &qemu_gluster_create_opts, +}; + +static BlockDriver bdrv_gluster_unix = { + .format_name = "gluster", + .protocol_name = "gluster+unix", + .instance_size = sizeof(BDRVGlusterState), + .bdrv_needs_filename = true, + .bdrv_file_open = qemu_gluster_open, + .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, + .bdrv_reopen_commit = qemu_gluster_reopen_commit, + .bdrv_reopen_abort = qemu_gluster_reopen_abort, + .bdrv_close = qemu_gluster_close, + .bdrv_create = qemu_gluster_create, + .bdrv_getlength = qemu_gluster_getlength, + .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_readv = qemu_gluster_co_readv, + .bdrv_co_writev = qemu_gluster_co_writev, + .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, + .bdrv_has_zero_init = qemu_gluster_has_zero_init, +#ifdef CONFIG_GLUSTERFS_DISCARD + .bdrv_co_discard = qemu_gluster_co_discard, +#endif +#ifdef CONFIG_GLUSTERFS_ZEROFILL + .bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes, +#endif + .create_opts = &qemu_gluster_create_opts, +}; + +static BlockDriver bdrv_gluster_rdma = { + .format_name = "gluster", + .protocol_name = "gluster+rdma", + .instance_size = sizeof(BDRVGlusterState), + .bdrv_needs_filename = true, + .bdrv_file_open = qemu_gluster_open, + .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, + .bdrv_reopen_commit = qemu_gluster_reopen_commit, + .bdrv_reopen_abort = qemu_gluster_reopen_abort, + .bdrv_close = qemu_gluster_close, + .bdrv_create = qemu_gluster_create, + .bdrv_getlength = qemu_gluster_getlength, + .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_readv = qemu_gluster_co_readv, + .bdrv_co_writev = qemu_gluster_co_writev, + .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, + .bdrv_has_zero_init = qemu_gluster_has_zero_init, +#ifdef CONFIG_GLUSTERFS_DISCARD + .bdrv_co_discard = qemu_gluster_co_discard, +#endif +#ifdef CONFIG_GLUSTERFS_ZEROFILL + .bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes, +#endif + .create_opts = &qemu_gluster_create_opts, +}; + +static void bdrv_gluster_init(void) +{ + bdrv_register(&bdrv_gluster_rdma); + bdrv_register(&bdrv_gluster_unix); + bdrv_register(&bdrv_gluster_tcp); + bdrv_register(&bdrv_gluster); +} + +block_init(bdrv_gluster_init); diff --git a/block/io.c b/block/io.c new file mode 100644 index 0000000..e394d92 --- /dev/null +++ b/block/io.c @@ -0,0 +1,2603 @@ +/* + * Block layer I/O functions + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "trace.h" +#include "sysemu/qtest.h" +#include "block/blockjob.h" +#include "block/block_int.h" + +#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ + +static BlockAIOCB *bdrv_aio_readv_em(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque); +static BlockAIOCB *bdrv_aio_writev_em(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque); +static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov); +static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov); +static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); +static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); +static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BdrvRequestFlags flags, + BlockCompletionFunc *cb, + void *opaque, + bool is_write); +static void coroutine_fn bdrv_co_do_rw(void *opaque); +static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, BdrvRequestFlags flags); + +/* throttling disk I/O limits */ +void bdrv_set_io_limits(BlockDriverState *bs, + ThrottleConfig *cfg) +{ + int i; + + throttle_config(&bs->throttle_state, cfg); + + for (i = 0; i < 2; i++) { + qemu_co_enter_next(&bs->throttled_reqs[i]); + } +} + +/* this function drain all the throttled IOs */ +static bool bdrv_start_throttled_reqs(BlockDriverState *bs) +{ + bool drained = false; + bool enabled = bs->io_limits_enabled; + int i; + + bs->io_limits_enabled = false; + + for (i = 0; i < 2; i++) { + while (qemu_co_enter_next(&bs->throttled_reqs[i])) { + drained = true; + } + } + + bs->io_limits_enabled = enabled; + + return drained; +} + +void bdrv_io_limits_disable(BlockDriverState *bs) +{ + bs->io_limits_enabled = false; + + bdrv_start_throttled_reqs(bs); + + throttle_destroy(&bs->throttle_state); +} + +static void bdrv_throttle_read_timer_cb(void *opaque) +{ + BlockDriverState *bs = opaque; + qemu_co_enter_next(&bs->throttled_reqs[0]); +} + +static void bdrv_throttle_write_timer_cb(void *opaque) +{ + BlockDriverState *bs = opaque; + qemu_co_enter_next(&bs->throttled_reqs[1]); +} + +/* should be called before bdrv_set_io_limits if a limit is set */ +void bdrv_io_limits_enable(BlockDriverState *bs) +{ + int clock_type = QEMU_CLOCK_REALTIME; + + if (qtest_enabled()) { + /* For testing block IO throttling only */ + clock_type = QEMU_CLOCK_VIRTUAL; + } + assert(!bs->io_limits_enabled); + throttle_init(&bs->throttle_state, + bdrv_get_aio_context(bs), + clock_type, + bdrv_throttle_read_timer_cb, + bdrv_throttle_write_timer_cb, + bs); + bs->io_limits_enabled = true; +} + +/* This function makes an IO wait if needed + * + * @nb_sectors: the number of sectors of the IO + * @is_write: is the IO a write + */ +static void bdrv_io_limits_intercept(BlockDriverState *bs, + unsigned int bytes, + bool is_write) +{ + /* does this io must wait */ + bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write); + + /* if must wait or any request of this type throttled queue the IO */ + if (must_wait || + !qemu_co_queue_empty(&bs->throttled_reqs[is_write])) { + qemu_co_queue_wait(&bs->throttled_reqs[is_write]); + } + + /* the IO will be executed, do the accounting */ + throttle_account(&bs->throttle_state, is_write, bytes); + + + /* if the next request must wait -> do nothing */ + if (throttle_schedule_timer(&bs->throttle_state, is_write)) { + return; + } + + /* else queue next request for execution */ + qemu_co_queue_next(&bs->throttled_reqs[is_write]); +} + +void bdrv_setup_io_funcs(BlockDriver *bdrv) +{ + /* Block drivers without coroutine functions need emulation */ + if (!bdrv->bdrv_co_readv) { + bdrv->bdrv_co_readv = bdrv_co_readv_em; + bdrv->bdrv_co_writev = bdrv_co_writev_em; + + /* bdrv_co_readv_em()/brdv_co_writev_em() work in terms of aio, so if + * the block driver lacks aio we need to emulate that too. + */ + if (!bdrv->bdrv_aio_readv) { + /* add AIO emulation layer */ + bdrv->bdrv_aio_readv = bdrv_aio_readv_em; + bdrv->bdrv_aio_writev = bdrv_aio_writev_em; + } + } +} + +void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BlockDriver *drv = bs->drv; + Error *local_err = NULL; + + memset(&bs->bl, 0, sizeof(bs->bl)); + + if (!drv) { + return; + } + + /* Take some limits from the children as a default */ + if (bs->file) { + bdrv_refresh_limits(bs->file, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + bs->bl.opt_transfer_length = bs->file->bl.opt_transfer_length; + bs->bl.max_transfer_length = bs->file->bl.max_transfer_length; + bs->bl.min_mem_alignment = bs->file->bl.min_mem_alignment; + bs->bl.opt_mem_alignment = bs->file->bl.opt_mem_alignment; + } else { + bs->bl.min_mem_alignment = 512; + bs->bl.opt_mem_alignment = getpagesize(); + } + + if (bs->backing_hd) { + bdrv_refresh_limits(bs->backing_hd, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + bs->bl.opt_transfer_length = + MAX(bs->bl.opt_transfer_length, + bs->backing_hd->bl.opt_transfer_length); + bs->bl.max_transfer_length = + MIN_NON_ZERO(bs->bl.max_transfer_length, + bs->backing_hd->bl.max_transfer_length); + bs->bl.opt_mem_alignment = + MAX(bs->bl.opt_mem_alignment, + bs->backing_hd->bl.opt_mem_alignment); + bs->bl.min_mem_alignment = + MAX(bs->bl.min_mem_alignment, + bs->backing_hd->bl.min_mem_alignment); + } + + /* Then let the driver override it */ + if (drv->bdrv_refresh_limits) { + drv->bdrv_refresh_limits(bs, errp); + } +} + +/** + * The copy-on-read flag is actually a reference count so multiple users may + * use the feature without worrying about clobbering its previous state. + * Copy-on-read stays enabled until all users have called to disable it. + */ +void bdrv_enable_copy_on_read(BlockDriverState *bs) +{ + bs->copy_on_read++; +} + +void bdrv_disable_copy_on_read(BlockDriverState *bs) +{ + assert(bs->copy_on_read > 0); + bs->copy_on_read--; +} + +/* Check if any requests are in-flight (including throttled requests) */ +static bool bdrv_requests_pending(BlockDriverState *bs) +{ + if (!QLIST_EMPTY(&bs->tracked_requests)) { + return true; + } + if (!qemu_co_queue_empty(&bs->throttled_reqs[0])) { + return true; + } + if (!qemu_co_queue_empty(&bs->throttled_reqs[1])) { + return true; + } + if (bs->file && bdrv_requests_pending(bs->file)) { + return true; + } + if (bs->backing_hd && bdrv_requests_pending(bs->backing_hd)) { + return true; + } + return false; +} + +static bool bdrv_drain_one(BlockDriverState *bs) +{ + bool bs_busy; + + bdrv_flush_io_queue(bs); + bdrv_start_throttled_reqs(bs); + bs_busy = bdrv_requests_pending(bs); + bs_busy |= aio_poll(bdrv_get_aio_context(bs), bs_busy); + return bs_busy; +} + +/* + * Wait for pending requests to complete on a single BlockDriverState subtree + * + * See the warning in bdrv_drain_all(). This function can only be called if + * you are sure nothing can generate I/O because you have op blockers + * installed. + * + * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState + * AioContext. + */ +void bdrv_drain(BlockDriverState *bs) +{ + while (bdrv_drain_one(bs)) { + /* Keep iterating */ + } +} + +/* + * Wait for pending requests to complete across all BlockDriverStates + * + * This function does not flush data to disk, use bdrv_flush_all() for that + * after calling this function. + * + * Note that completion of an asynchronous I/O operation can trigger any + * number of other I/O operations on other devices---for example a coroutine + * can be arbitrarily complex and a constant flow of I/O can come until the + * coroutine is complete. Because of this, it is not possible to have a + * function to drain a single device's I/O queue. + */ +void bdrv_drain_all(void) +{ + /* Always run first iteration so any pending completion BHs run */ + bool busy = true; + BlockDriverState *bs = NULL; + + while ((bs = bdrv_next(bs))) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + if (bs->job) { + block_job_pause(bs->job); + } + aio_context_release(aio_context); + } + + while (busy) { + busy = false; + bs = NULL; + + while ((bs = bdrv_next(bs))) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + busy |= bdrv_drain_one(bs); + aio_context_release(aio_context); + } + } + + bs = NULL; + while ((bs = bdrv_next(bs))) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + if (bs->job) { + block_job_resume(bs->job); + } + aio_context_release(aio_context); + } +} + +/** + * Remove an active request from the tracked requests list + * + * This function should be called when a tracked request is completing. + */ +static void tracked_request_end(BdrvTrackedRequest *req) +{ + if (req->serialising) { + req->bs->serialising_in_flight--; + } + + QLIST_REMOVE(req, list); + qemu_co_queue_restart_all(&req->wait_queue); +} + +/** + * Add an active request to the tracked requests list + */ +static void tracked_request_begin(BdrvTrackedRequest *req, + BlockDriverState *bs, + int64_t offset, + unsigned int bytes, bool is_write) +{ + *req = (BdrvTrackedRequest){ + .bs = bs, + .offset = offset, + .bytes = bytes, + .is_write = is_write, + .co = qemu_coroutine_self(), + .serialising = false, + .overlap_offset = offset, + .overlap_bytes = bytes, + }; + + qemu_co_queue_init(&req->wait_queue); + + QLIST_INSERT_HEAD(&bs->tracked_requests, req, list); +} + +static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) +{ + int64_t overlap_offset = req->offset & ~(align - 1); + unsigned int overlap_bytes = ROUND_UP(req->offset + req->bytes, align) + - overlap_offset; + + if (!req->serialising) { + req->bs->serialising_in_flight++; + req->serialising = true; + } + + req->overlap_offset = MIN(req->overlap_offset, overlap_offset); + req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); +} + +/** + * Round a region to cluster boundaries + */ +void bdrv_round_to_clusters(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + int64_t *cluster_sector_num, + int *cluster_nb_sectors) +{ + BlockDriverInfo bdi; + + if (bdrv_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) { + *cluster_sector_num = sector_num; + *cluster_nb_sectors = nb_sectors; + } else { + int64_t c = bdi.cluster_size / BDRV_SECTOR_SIZE; + *cluster_sector_num = QEMU_ALIGN_DOWN(sector_num, c); + *cluster_nb_sectors = QEMU_ALIGN_UP(sector_num - *cluster_sector_num + + nb_sectors, c); + } +} + +static int bdrv_get_cluster_size(BlockDriverState *bs) +{ + BlockDriverInfo bdi; + int ret; + + ret = bdrv_get_info(bs, &bdi); + if (ret < 0 || bdi.cluster_size == 0) { + return bs->request_alignment; + } else { + return bdi.cluster_size; + } +} + +static bool tracked_request_overlaps(BdrvTrackedRequest *req, + int64_t offset, unsigned int bytes) +{ + /* aaaa bbbb */ + if (offset >= req->overlap_offset + req->overlap_bytes) { + return false; + } + /* bbbb aaaa */ + if (req->overlap_offset >= offset + bytes) { + return false; + } + return true; +} + +static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self) +{ + BlockDriverState *bs = self->bs; + BdrvTrackedRequest *req; + bool retry; + bool waited = false; + + if (!bs->serialising_in_flight) { + return false; + } + + do { + retry = false; + QLIST_FOREACH(req, &bs->tracked_requests, list) { + if (req == self || (!req->serialising && !self->serialising)) { + continue; + } + if (tracked_request_overlaps(req, self->overlap_offset, + self->overlap_bytes)) + { + /* Hitting this means there was a reentrant request, for + * example, a block driver issuing nested requests. This must + * never happen since it means deadlock. + */ + assert(qemu_coroutine_self() != req->co); + + /* If the request is already (indirectly) waiting for us, or + * will wait for us as soon as it wakes up, then just go on + * (instead of producing a deadlock in the former case). */ + if (!req->waiting_for) { + self->waiting_for = req; + qemu_co_queue_wait(&req->wait_queue); + self->waiting_for = NULL; + retry = true; + waited = true; + break; + } + } + } + } while (retry); + + return waited; +} + +static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, + size_t size) +{ + if (size > BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) { + return -EIO; + } + + if (!bdrv_is_inserted(bs)) { + return -ENOMEDIUM; + } + + if (offset < 0) { + return -EIO; + } + + return 0; +} + +static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EIO; + } + + return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE); +} + +typedef struct RwCo { + BlockDriverState *bs; + int64_t offset; + QEMUIOVector *qiov; + bool is_write; + int ret; + BdrvRequestFlags flags; +} RwCo; + +static void coroutine_fn bdrv_rw_co_entry(void *opaque) +{ + RwCo *rwco = opaque; + + if (!rwco->is_write) { + rwco->ret = bdrv_co_do_preadv(rwco->bs, rwco->offset, + rwco->qiov->size, rwco->qiov, + rwco->flags); + } else { + rwco->ret = bdrv_co_do_pwritev(rwco->bs, rwco->offset, + rwco->qiov->size, rwco->qiov, + rwco->flags); + } +} + +/* + * Process a vectored synchronous request using coroutines + */ +static int bdrv_prwv_co(BlockDriverState *bs, int64_t offset, + QEMUIOVector *qiov, bool is_write, + BdrvRequestFlags flags) +{ + Coroutine *co; + RwCo rwco = { + .bs = bs, + .offset = offset, + .qiov = qiov, + .is_write = is_write, + .ret = NOT_DONE, + .flags = flags, + }; + + /** + * In sync call context, when the vcpu is blocked, this throttling timer + * will not fire; so the I/O throttling function has to be disabled here + * if it has been enabled. + */ + if (bs->io_limits_enabled) { + fprintf(stderr, "Disabling I/O throttling on '%s' due " + "to synchronous I/O.\n", bdrv_get_device_name(bs)); + bdrv_io_limits_disable(bs); + } + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_rw_co_entry(&rwco); + } else { + AioContext *aio_context = bdrv_get_aio_context(bs); + + co = qemu_coroutine_create(bdrv_rw_co_entry); + qemu_coroutine_enter(co, &rwco); + while (rwco.ret == NOT_DONE) { + aio_poll(aio_context, true); + } + } + return rwco.ret; +} + +/* + * Process a synchronous request using coroutines + */ +static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, + int nb_sectors, bool is_write, BdrvRequestFlags flags) +{ + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = nb_sectors * BDRV_SECTOR_SIZE, + }; + + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EINVAL; + } + + qemu_iovec_init_external(&qiov, &iov, 1); + return bdrv_prwv_co(bs, sector_num << BDRV_SECTOR_BITS, + &qiov, is_write, flags); +} + +/* return < 0 if error. See bdrv_write() for the return codes */ +int bdrv_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false, 0); +} + +/* Just like bdrv_read(), but with I/O throttling temporarily disabled */ +int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + bool enabled; + int ret; + + enabled = bs->io_limits_enabled; + bs->io_limits_enabled = false; + ret = bdrv_read(bs, sector_num, buf, nb_sectors); + bs->io_limits_enabled = enabled; + return ret; +} + +/* Return < 0 if error. Important errors are: + -EIO generic I/O error (may happen for all errors) + -ENOMEDIUM No media inserted. + -EINVAL Invalid sector number or nb_sectors + -EACCES Trying to write a read-only device +*/ +int bdrv_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + return bdrv_rw_co(bs, sector_num, (uint8_t *)buf, nb_sectors, true, 0); +} + +int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags) +{ + return bdrv_rw_co(bs, sector_num, NULL, nb_sectors, true, + BDRV_REQ_ZERO_WRITE | flags); +} + +/* + * Completely zero out a block device with the help of bdrv_write_zeroes. + * The operation is sped up by checking the block status and only writing + * zeroes to the device if they currently do not return zeroes. Optional + * flags are passed through to bdrv_write_zeroes (e.g. BDRV_REQ_MAY_UNMAP). + * + * Returns < 0 on error, 0 on success. For error codes see bdrv_write(). + */ +int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) +{ + int64_t target_sectors, ret, nb_sectors, sector_num = 0; + int n; + + target_sectors = bdrv_nb_sectors(bs); + if (target_sectors < 0) { + return target_sectors; + } + + for (;;) { + nb_sectors = MIN(target_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); + if (nb_sectors <= 0) { + return 0; + } + ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n); + if (ret < 0) { + error_report("error getting block status at sector %" PRId64 ": %s", + sector_num, strerror(-ret)); + return ret; + } + if (ret & BDRV_BLOCK_ZERO) { + sector_num += n; + continue; + } + ret = bdrv_write_zeroes(bs, sector_num, n, flags); + if (ret < 0) { + error_report("error writing zeroes at sector %" PRId64 ": %s", + sector_num, strerror(-ret)); + return ret; + } + sector_num += n; + } +} + +int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int bytes) +{ + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = bytes, + }; + int ret; + + if (bytes < 0) { + return -EINVAL; + } + + qemu_iovec_init_external(&qiov, &iov, 1); + ret = bdrv_prwv_co(bs, offset, &qiov, false, 0); + if (ret < 0) { + return ret; + } + + return bytes; +} + +int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov) +{ + int ret; + + ret = bdrv_prwv_co(bs, offset, qiov, true, 0); + if (ret < 0) { + return ret; + } + + return qiov->size; +} + +int bdrv_pwrite(BlockDriverState *bs, int64_t offset, + const void *buf, int bytes) +{ + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *) buf, + .iov_len = bytes, + }; + + if (bytes < 0) { + return -EINVAL; + } + + qemu_iovec_init_external(&qiov, &iov, 1); + return bdrv_pwritev(bs, offset, &qiov); +} + +/* + * Writes to the file and ensures that no writes are reordered across this + * request (acts as a barrier) + * + * Returns 0 on success, -errno in error cases. + */ +int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, + const void *buf, int count) +{ + int ret; + + ret = bdrv_pwrite(bs, offset, buf, count); + if (ret < 0) { + return ret; + } + + /* No flush needed for cache modes that already do it */ + if (bs->enable_write_cache) { + bdrv_flush(bs); + } + + return 0; +} + +static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + /* Perform I/O through a temporary buffer so that users who scribble over + * their read buffer while the operation is in progress do not end up + * modifying the image file. This is critical for zero-copy guest I/O + * where anything might happen inside guest memory. + */ + void *bounce_buffer; + + BlockDriver *drv = bs->drv; + struct iovec iov; + QEMUIOVector bounce_qiov; + int64_t cluster_sector_num; + int cluster_nb_sectors; + size_t skip_bytes; + int ret; + + /* Cover entire cluster so no additional backing file I/O is required when + * allocating cluster in the image file. + */ + bdrv_round_to_clusters(bs, sector_num, nb_sectors, + &cluster_sector_num, &cluster_nb_sectors); + + trace_bdrv_co_do_copy_on_readv(bs, sector_num, nb_sectors, + cluster_sector_num, cluster_nb_sectors); + + iov.iov_len = cluster_nb_sectors * BDRV_SECTOR_SIZE; + iov.iov_base = bounce_buffer = qemu_try_blockalign(bs, iov.iov_len); + if (bounce_buffer == NULL) { + ret = -ENOMEM; + goto err; + } + + qemu_iovec_init_external(&bounce_qiov, &iov, 1); + + ret = drv->bdrv_co_readv(bs, cluster_sector_num, cluster_nb_sectors, + &bounce_qiov); + if (ret < 0) { + goto err; + } + + if (drv->bdrv_co_write_zeroes && + buffer_is_zero(bounce_buffer, iov.iov_len)) { + ret = bdrv_co_do_write_zeroes(bs, cluster_sector_num, + cluster_nb_sectors, 0); + } else { + /* This does not change the data on the disk, it is not necessary + * to flush even in cache=writethrough mode. + */ + ret = drv->bdrv_co_writev(bs, cluster_sector_num, cluster_nb_sectors, + &bounce_qiov); + } + + if (ret < 0) { + /* It might be okay to ignore write errors for guest requests. If this + * is a deliberate copy-on-read then we don't want to ignore the error. + * Simply report it in all cases. + */ + goto err; + } + + skip_bytes = (sector_num - cluster_sector_num) * BDRV_SECTOR_SIZE; + qemu_iovec_from_buf(qiov, 0, bounce_buffer + skip_bytes, + nb_sectors * BDRV_SECTOR_SIZE); + +err: + qemu_vfree(bounce_buffer); + return ret; +} + +/* + * Forwards an already correctly aligned request to the BlockDriver. This + * handles copy on read and zeroing after EOF; any other features must be + * implemented by the caller. + */ +static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, + BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, + int64_t align, QEMUIOVector *qiov, int flags) +{ + BlockDriver *drv = bs->drv; + int ret; + + int64_t sector_num = offset >> BDRV_SECTOR_BITS; + unsigned int nb_sectors = bytes >> BDRV_SECTOR_BITS; + + assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); + assert(!qiov || bytes == qiov->size); + + /* Handle Copy on Read and associated serialisation */ + if (flags & BDRV_REQ_COPY_ON_READ) { + /* If we touch the same cluster it counts as an overlap. This + * guarantees that allocating writes will be serialized and not race + * with each other for the same cluster. For example, in copy-on-read + * it ensures that the CoR read and write operations are atomic and + * guest writes cannot interleave between them. */ + mark_request_serialising(req, bdrv_get_cluster_size(bs)); + } + + wait_serialising_requests(req); + + if (flags & BDRV_REQ_COPY_ON_READ) { + int pnum; + + ret = bdrv_is_allocated(bs, sector_num, nb_sectors, &pnum); + if (ret < 0) { + goto out; + } + + if (!ret || pnum != nb_sectors) { + ret = bdrv_co_do_copy_on_readv(bs, sector_num, nb_sectors, qiov); + goto out; + } + } + + /* Forward the request to the BlockDriver */ + if (!bs->zero_beyond_eof) { + ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); + } else { + /* Read zeros after EOF */ + int64_t total_sectors, max_nb_sectors; + + total_sectors = bdrv_nb_sectors(bs); + if (total_sectors < 0) { + ret = total_sectors; + goto out; + } + + max_nb_sectors = ROUND_UP(MAX(0, total_sectors - sector_num), + align >> BDRV_SECTOR_BITS); + if (nb_sectors < max_nb_sectors) { + ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); + } else if (max_nb_sectors > 0) { + QEMUIOVector local_qiov; + + qemu_iovec_init(&local_qiov, qiov->niov); + qemu_iovec_concat(&local_qiov, qiov, 0, + max_nb_sectors * BDRV_SECTOR_SIZE); + + ret = drv->bdrv_co_readv(bs, sector_num, max_nb_sectors, + &local_qiov); + + qemu_iovec_destroy(&local_qiov); + } else { + ret = 0; + } + + /* Reading beyond end of file is supposed to produce zeroes */ + if (ret == 0 && total_sectors < sector_num + nb_sectors) { + uint64_t offset = MAX(0, total_sectors - sector_num); + uint64_t bytes = (sector_num + nb_sectors - offset) * + BDRV_SECTOR_SIZE; + qemu_iovec_memset(qiov, offset * BDRV_SECTOR_SIZE, 0, bytes); + } + } + +out: + return ret; +} + +/* + * Handle a read request in coroutine context + */ +static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + BlockDriver *drv = bs->drv; + BdrvTrackedRequest req; + + /* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */ + uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment); + uint8_t *head_buf = NULL; + uint8_t *tail_buf = NULL; + QEMUIOVector local_qiov; + bool use_local_qiov = false; + int ret; + + if (!drv) { + return -ENOMEDIUM; + } + + ret = bdrv_check_byte_request(bs, offset, bytes); + if (ret < 0) { + return ret; + } + + if (bs->copy_on_read) { + flags |= BDRV_REQ_COPY_ON_READ; + } + + /* throttling disk I/O */ + if (bs->io_limits_enabled) { + bdrv_io_limits_intercept(bs, bytes, false); + } + + /* Align read if necessary by padding qiov */ + if (offset & (align - 1)) { + head_buf = qemu_blockalign(bs, align); + qemu_iovec_init(&local_qiov, qiov->niov + 2); + qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1)); + qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size); + use_local_qiov = true; + + bytes += offset & (align - 1); + offset = offset & ~(align - 1); + } + + if ((offset + bytes) & (align - 1)) { + if (!use_local_qiov) { + qemu_iovec_init(&local_qiov, qiov->niov + 1); + qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size); + use_local_qiov = true; + } + tail_buf = qemu_blockalign(bs, align); + qemu_iovec_add(&local_qiov, tail_buf, + align - ((offset + bytes) & (align - 1))); + + bytes = ROUND_UP(bytes, align); + } + + tracked_request_begin(&req, bs, offset, bytes, false); + ret = bdrv_aligned_preadv(bs, &req, offset, bytes, align, + use_local_qiov ? &local_qiov : qiov, + flags); + tracked_request_end(&req); + + if (use_local_qiov) { + qemu_iovec_destroy(&local_qiov); + qemu_vfree(head_buf); + qemu_vfree(tail_buf); + } + + return ret; +} + +static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EINVAL; + } + + return bdrv_co_do_preadv(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors << BDRV_SECTOR_BITS, qiov, flags); +} + +int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + trace_bdrv_co_readv(bs, sector_num, nb_sectors); + + return bdrv_co_do_readv(bs, sector_num, nb_sectors, qiov, 0); +} + +int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + trace_bdrv_co_copy_on_readv(bs, sector_num, nb_sectors); + + return bdrv_co_do_readv(bs, sector_num, nb_sectors, qiov, + BDRV_REQ_COPY_ON_READ); +} + +#define MAX_WRITE_ZEROES_BOUNCE_BUFFER 32768 + +static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) +{ + BlockDriver *drv = bs->drv; + QEMUIOVector qiov; + struct iovec iov = {0}; + int ret = 0; + + int max_write_zeroes = MIN_NON_ZERO(bs->bl.max_write_zeroes, + BDRV_REQUEST_MAX_SECTORS); + + while (nb_sectors > 0 && !ret) { + int num = nb_sectors; + + /* Align request. Block drivers can expect the "bulk" of the request + * to be aligned. + */ + if (bs->bl.write_zeroes_alignment + && num > bs->bl.write_zeroes_alignment) { + if (sector_num % bs->bl.write_zeroes_alignment != 0) { + /* Make a small request up to the first aligned sector. */ + num = bs->bl.write_zeroes_alignment; + num -= sector_num % bs->bl.write_zeroes_alignment; + } else if ((sector_num + num) % bs->bl.write_zeroes_alignment != 0) { + /* Shorten the request to the last aligned sector. num cannot + * underflow because num > bs->bl.write_zeroes_alignment. + */ + num -= (sector_num + num) % bs->bl.write_zeroes_alignment; + } + } + + /* limit request size */ + if (num > max_write_zeroes) { + num = max_write_zeroes; + } + + ret = -ENOTSUP; + /* First try the efficient write zeroes operation */ + if (drv->bdrv_co_write_zeroes) { + ret = drv->bdrv_co_write_zeroes(bs, sector_num, num, flags); + } + + if (ret == -ENOTSUP) { + /* Fall back to bounce buffer if write zeroes is unsupported */ + int max_xfer_len = MIN_NON_ZERO(bs->bl.max_transfer_length, + MAX_WRITE_ZEROES_BOUNCE_BUFFER); + num = MIN(num, max_xfer_len); + iov.iov_len = num * BDRV_SECTOR_SIZE; + if (iov.iov_base == NULL) { + iov.iov_base = qemu_try_blockalign(bs, num * BDRV_SECTOR_SIZE); + if (iov.iov_base == NULL) { + ret = -ENOMEM; + goto fail; + } + memset(iov.iov_base, 0, num * BDRV_SECTOR_SIZE); + } + qemu_iovec_init_external(&qiov, &iov, 1); + + ret = drv->bdrv_co_writev(bs, sector_num, num, &qiov); + + /* Keep bounce buffer around if it is big enough for all + * all future requests. + */ + if (num < max_xfer_len) { + qemu_vfree(iov.iov_base); + iov.iov_base = NULL; + } + } + + sector_num += num; + nb_sectors -= num; + } + +fail: + qemu_vfree(iov.iov_base); + return ret; +} + +/* + * Forwards an already correctly aligned write request to the BlockDriver. + */ +static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, + BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, + QEMUIOVector *qiov, int flags) +{ + BlockDriver *drv = bs->drv; + bool waited; + int ret; + + int64_t sector_num = offset >> BDRV_SECTOR_BITS; + unsigned int nb_sectors = bytes >> BDRV_SECTOR_BITS; + + assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); + assert(!qiov || bytes == qiov->size); + + waited = wait_serialising_requests(req); + assert(!waited || !req->serialising); + assert(req->overlap_offset <= offset); + assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); + + ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); + + if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF && + !(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_write_zeroes && + qemu_iovec_is_zero(qiov)) { + flags |= BDRV_REQ_ZERO_WRITE; + if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) { + flags |= BDRV_REQ_MAY_UNMAP; + } + } + + if (ret < 0) { + /* Do nothing, write notifier decided to fail this request */ + } else if (flags & BDRV_REQ_ZERO_WRITE) { + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_ZERO); + ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors, flags); + } else { + BLKDBG_EVENT(bs, BLKDBG_PWRITEV); + ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov); + } + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_DONE); + + if (ret == 0 && !bs->enable_write_cache) { + ret = bdrv_co_flush(bs); + } + + bdrv_set_dirty(bs, sector_num, nb_sectors); + + block_acct_highest_sector(&bs->stats, sector_num, nb_sectors); + + if (ret >= 0) { + bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors); + } + + return ret; +} + +static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs, + int64_t offset, + unsigned int bytes, + BdrvRequestFlags flags, + BdrvTrackedRequest *req) +{ + uint8_t *buf = NULL; + QEMUIOVector local_qiov; + struct iovec iov; + uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment); + unsigned int head_padding_bytes, tail_padding_bytes; + int ret = 0; + + head_padding_bytes = offset & (align - 1); + tail_padding_bytes = align - ((offset + bytes) & (align - 1)); + + + assert(flags & BDRV_REQ_ZERO_WRITE); + if (head_padding_bytes || tail_padding_bytes) { + buf = qemu_blockalign(bs, align); + iov = (struct iovec) { + .iov_base = buf, + .iov_len = align, + }; + qemu_iovec_init_external(&local_qiov, &iov, 1); + } + if (head_padding_bytes) { + uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes); + + /* RMW the unaligned part before head. */ + mark_request_serialising(req, align); + wait_serialising_requests(req); + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD); + ret = bdrv_aligned_preadv(bs, req, offset & ~(align - 1), align, + align, &local_qiov, 0); + if (ret < 0) { + goto fail; + } + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + + memset(buf + head_padding_bytes, 0, zero_bytes); + ret = bdrv_aligned_pwritev(bs, req, offset & ~(align - 1), align, + &local_qiov, + flags & ~BDRV_REQ_ZERO_WRITE); + if (ret < 0) { + goto fail; + } + offset += zero_bytes; + bytes -= zero_bytes; + } + + assert(!bytes || (offset & (align - 1)) == 0); + if (bytes >= align) { + /* Write the aligned part in the middle. */ + uint64_t aligned_bytes = bytes & ~(align - 1); + ret = bdrv_aligned_pwritev(bs, req, offset, aligned_bytes, + NULL, flags); + if (ret < 0) { + goto fail; + } + bytes -= aligned_bytes; + offset += aligned_bytes; + } + + assert(!bytes || (offset & (align - 1)) == 0); + if (bytes) { + assert(align == tail_padding_bytes + bytes); + /* RMW the unaligned part after tail. */ + mark_request_serialising(req, align); + wait_serialising_requests(req); + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL); + ret = bdrv_aligned_preadv(bs, req, offset, align, + align, &local_qiov, 0); + if (ret < 0) { + goto fail; + } + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + + memset(buf, 0, bytes); + ret = bdrv_aligned_pwritev(bs, req, offset, align, + &local_qiov, flags & ~BDRV_REQ_ZERO_WRITE); + } +fail: + qemu_vfree(buf); + return ret; + +} + +/* + * Handle a write request in coroutine context + */ +static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + BdrvTrackedRequest req; + /* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */ + uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment); + uint8_t *head_buf = NULL; + uint8_t *tail_buf = NULL; + QEMUIOVector local_qiov; + bool use_local_qiov = false; + int ret; + + if (!bs->drv) { + return -ENOMEDIUM; + } + if (bs->read_only) { + return -EPERM; + } + + ret = bdrv_check_byte_request(bs, offset, bytes); + if (ret < 0) { + return ret; + } + + /* throttling disk I/O */ + if (bs->io_limits_enabled) { + bdrv_io_limits_intercept(bs, bytes, true); + } + + /* + * Align write if necessary by performing a read-modify-write cycle. + * Pad qiov with the read parts and be sure to have a tracked request not + * only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle. + */ + tracked_request_begin(&req, bs, offset, bytes, true); + + if (!qiov) { + ret = bdrv_co_do_zero_pwritev(bs, offset, bytes, flags, &req); + goto out; + } + + if (offset & (align - 1)) { + QEMUIOVector head_qiov; + struct iovec head_iov; + + mark_request_serialising(&req, align); + wait_serialising_requests(&req); + + head_buf = qemu_blockalign(bs, align); + head_iov = (struct iovec) { + .iov_base = head_buf, + .iov_len = align, + }; + qemu_iovec_init_external(&head_qiov, &head_iov, 1); + + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD); + ret = bdrv_aligned_preadv(bs, &req, offset & ~(align - 1), align, + align, &head_qiov, 0); + if (ret < 0) { + goto fail; + } + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + + qemu_iovec_init(&local_qiov, qiov->niov + 2); + qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1)); + qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size); + use_local_qiov = true; + + bytes += offset & (align - 1); + offset = offset & ~(align - 1); + } + + if ((offset + bytes) & (align - 1)) { + QEMUIOVector tail_qiov; + struct iovec tail_iov; + size_t tail_bytes; + bool waited; + + mark_request_serialising(&req, align); + waited = wait_serialising_requests(&req); + assert(!waited || !use_local_qiov); + + tail_buf = qemu_blockalign(bs, align); + tail_iov = (struct iovec) { + .iov_base = tail_buf, + .iov_len = align, + }; + qemu_iovec_init_external(&tail_qiov, &tail_iov, 1); + + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL); + ret = bdrv_aligned_preadv(bs, &req, (offset + bytes) & ~(align - 1), align, + align, &tail_qiov, 0); + if (ret < 0) { + goto fail; + } + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + + if (!use_local_qiov) { + qemu_iovec_init(&local_qiov, qiov->niov + 1); + qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size); + use_local_qiov = true; + } + + tail_bytes = (offset + bytes) & (align - 1); + qemu_iovec_add(&local_qiov, tail_buf + tail_bytes, align - tail_bytes); + + bytes = ROUND_UP(bytes, align); + } + + ret = bdrv_aligned_pwritev(bs, &req, offset, bytes, + use_local_qiov ? &local_qiov : qiov, + flags); + +fail: + + if (use_local_qiov) { + qemu_iovec_destroy(&local_qiov); + } + qemu_vfree(head_buf); + qemu_vfree(tail_buf); +out: + tracked_request_end(&req); + return ret; +} + +static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EINVAL; + } + + return bdrv_co_do_pwritev(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors << BDRV_SECTOR_BITS, qiov, flags); +} + +int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + trace_bdrv_co_writev(bs, sector_num, nb_sectors); + + return bdrv_co_do_writev(bs, sector_num, nb_sectors, qiov, 0); +} + +int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + BdrvRequestFlags flags) +{ + trace_bdrv_co_write_zeroes(bs, sector_num, nb_sectors, flags); + + if (!(bs->open_flags & BDRV_O_UNMAP)) { + flags &= ~BDRV_REQ_MAY_UNMAP; + } + + return bdrv_co_do_writev(bs, sector_num, nb_sectors, NULL, + BDRV_REQ_ZERO_WRITE | flags); +} + +int bdrv_flush_all(void) +{ + BlockDriverState *bs = NULL; + int result = 0; + + while ((bs = bdrv_next(bs))) { + AioContext *aio_context = bdrv_get_aio_context(bs); + int ret; + + aio_context_acquire(aio_context); + ret = bdrv_flush(bs); + if (ret < 0 && !result) { + result = ret; + } + aio_context_release(aio_context); + } + + return result; +} + +typedef struct BdrvCoGetBlockStatusData { + BlockDriverState *bs; + BlockDriverState *base; + int64_t sector_num; + int nb_sectors; + int *pnum; + int64_t ret; + bool done; +} BdrvCoGetBlockStatusData; + +/* + * Returns the allocation status of the specified sectors. + * Drivers not implementing the functionality are assumed to not support + * backing files, hence all their sectors are reported as allocated. + * + * If 'sector_num' is beyond the end of the disk image the return value is 0 + * and 'pnum' is set to 0. + * + * 'pnum' is set to the number of sectors (including and immediately following + * the specified sector) that are known to be in the same + * allocated/unallocated state. + * + * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes + * beyond the end of the disk image it will be clamped. + */ +static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + int64_t total_sectors; + int64_t n; + int64_t ret, ret2; + + total_sectors = bdrv_nb_sectors(bs); + if (total_sectors < 0) { + return total_sectors; + } + + if (sector_num >= total_sectors) { + *pnum = 0; + return 0; + } + + n = total_sectors - sector_num; + if (n < nb_sectors) { + nb_sectors = n; + } + + if (!bs->drv->bdrv_co_get_block_status) { + *pnum = nb_sectors; + ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; + if (bs->drv->protocol_name) { + ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE); + } + return ret; + } + + ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum); + if (ret < 0) { + *pnum = 0; + return ret; + } + + if (ret & BDRV_BLOCK_RAW) { + assert(ret & BDRV_BLOCK_OFFSET_VALID); + return bdrv_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS, + *pnum, pnum); + } + + if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { + ret |= BDRV_BLOCK_ALLOCATED; + } else { + if (bdrv_unallocated_blocks_are_zero(bs)) { + ret |= BDRV_BLOCK_ZERO; + } else if (bs->backing_hd) { + BlockDriverState *bs2 = bs->backing_hd; + int64_t nb_sectors2 = bdrv_nb_sectors(bs2); + if (nb_sectors2 >= 0 && sector_num >= nb_sectors2) { + ret |= BDRV_BLOCK_ZERO; + } + } + } + + if (bs->file && + (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && + (ret & BDRV_BLOCK_OFFSET_VALID)) { + int file_pnum; + + ret2 = bdrv_co_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS, + *pnum, &file_pnum); + if (ret2 >= 0) { + /* Ignore errors. This is just providing extra information, it + * is useful but not necessary. + */ + if (!file_pnum) { + /* !file_pnum indicates an offset at or beyond the EOF; it is + * perfectly valid for the format block driver to point to such + * offsets, so catch it and mark everything as zero */ + ret |= BDRV_BLOCK_ZERO; + } else { + /* Limit request to the range reported by the protocol driver */ + *pnum = file_pnum; + ret |= (ret2 & BDRV_BLOCK_ZERO); + } + } + } + + return ret; +} + +/* Coroutine wrapper for bdrv_get_block_status() */ +static void coroutine_fn bdrv_get_block_status_co_entry(void *opaque) +{ + BdrvCoGetBlockStatusData *data = opaque; + BlockDriverState *bs = data->bs; + + data->ret = bdrv_co_get_block_status(bs, data->sector_num, data->nb_sectors, + data->pnum); + data->done = true; +} + +/* + * Synchronous wrapper around bdrv_co_get_block_status(). + * + * See bdrv_co_get_block_status() for details. + */ +int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + Coroutine *co; + BdrvCoGetBlockStatusData data = { + .bs = bs, + .sector_num = sector_num, + .nb_sectors = nb_sectors, + .pnum = pnum, + .done = false, + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_get_block_status_co_entry(&data); + } else { + AioContext *aio_context = bdrv_get_aio_context(bs); + + co = qemu_coroutine_create(bdrv_get_block_status_co_entry); + qemu_coroutine_enter(co, &data); + while (!data.done) { + aio_poll(aio_context, true); + } + } + return data.ret; +} + +int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum); + if (ret < 0) { + return ret; + } + return !!(ret & BDRV_BLOCK_ALLOCATED); +} + +/* + * Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP] + * + * Return true if the given sector is allocated in any image between + * BASE and TOP (inclusive). BASE can be NULL to check if the given + * sector is allocated in any image of the chain. Return false otherwise. + * + * 'pnum' is set to the number of sectors (including and immediately following + * the specified sector) that are known to be in the same + * allocated/unallocated state. + * + */ +int bdrv_is_allocated_above(BlockDriverState *top, + BlockDriverState *base, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + BlockDriverState *intermediate; + int ret, n = nb_sectors; + + intermediate = top; + while (intermediate && intermediate != base) { + int pnum_inter; + ret = bdrv_is_allocated(intermediate, sector_num, nb_sectors, + &pnum_inter); + if (ret < 0) { + return ret; + } else if (ret) { + *pnum = pnum_inter; + return 1; + } + + /* + * [sector_num, nb_sectors] is unallocated on top but intermediate + * might have + * + * [sector_num+x, nr_sectors] allocated. + */ + if (n > pnum_inter && + (intermediate == top || + sector_num + pnum_inter < intermediate->total_sectors)) { + n = pnum_inter; + } + + intermediate = intermediate->backing_hd; + } + + *pnum = n; + return 0; +} + +int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BlockDriver *drv = bs->drv; + int ret; + + if (!drv) { + return -ENOMEDIUM; + } + if (!drv->bdrv_write_compressed) { + return -ENOTSUP; + } + ret = bdrv_check_request(bs, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + assert(QLIST_EMPTY(&bs->dirty_bitmaps)); + + return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors); +} + +int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, + int64_t pos, int size) +{ + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *) buf, + .iov_len = size, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + return bdrv_writev_vmstate(bs, &qiov, pos); +} + +int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) +{ + BlockDriver *drv = bs->drv; + + if (!drv) { + return -ENOMEDIUM; + } else if (drv->bdrv_save_vmstate) { + return drv->bdrv_save_vmstate(bs, qiov, pos); + } else if (bs->file) { + return bdrv_writev_vmstate(bs->file, qiov, pos); + } + + return -ENOTSUP; +} + +int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, + int64_t pos, int size) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (drv->bdrv_load_vmstate) + return drv->bdrv_load_vmstate(bs, buf, pos, size); + if (bs->file) + return bdrv_load_vmstate(bs->file, buf, pos, size); + return -ENOTSUP; +} + +/**************************************************************/ +/* async I/Os */ + +BlockAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque); + + return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, 0, + cb, opaque, false); +} + +BlockAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque); + + return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, 0, + cb, opaque, true); +} + +BlockAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque) +{ + trace_bdrv_aio_write_zeroes(bs, sector_num, nb_sectors, flags, opaque); + + return bdrv_co_aio_rw_vector(bs, sector_num, NULL, nb_sectors, + BDRV_REQ_ZERO_WRITE | flags, + cb, opaque, true); +} + + +typedef struct MultiwriteCB { + int error; + int num_requests; + int num_callbacks; + struct { + BlockCompletionFunc *cb; + void *opaque; + QEMUIOVector *free_qiov; + } callbacks[]; +} MultiwriteCB; + +static void multiwrite_user_cb(MultiwriteCB *mcb) +{ + int i; + + for (i = 0; i < mcb->num_callbacks; i++) { + mcb->callbacks[i].cb(mcb->callbacks[i].opaque, mcb->error); + if (mcb->callbacks[i].free_qiov) { + qemu_iovec_destroy(mcb->callbacks[i].free_qiov); + } + g_free(mcb->callbacks[i].free_qiov); + } +} + +static void multiwrite_cb(void *opaque, int ret) +{ + MultiwriteCB *mcb = opaque; + + trace_multiwrite_cb(mcb, ret); + + if (ret < 0 && !mcb->error) { + mcb->error = ret; + } + + mcb->num_requests--; + if (mcb->num_requests == 0) { + multiwrite_user_cb(mcb); + g_free(mcb); + } +} + +static int multiwrite_req_compare(const void *a, const void *b) +{ + const BlockRequest *req1 = a, *req2 = b; + + /* + * Note that we can't simply subtract req2->sector from req1->sector + * here as that could overflow the return value. + */ + if (req1->sector > req2->sector) { + return 1; + } else if (req1->sector < req2->sector) { + return -1; + } else { + return 0; + } +} + +/* + * Takes a bunch of requests and tries to merge them. Returns the number of + * requests that remain after merging. + */ +static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs, + int num_reqs, MultiwriteCB *mcb) +{ + int i, outidx; + + // Sort requests by start sector + qsort(reqs, num_reqs, sizeof(*reqs), &multiwrite_req_compare); + + // Check if adjacent requests touch the same clusters. If so, combine them, + // filling up gaps with zero sectors. + outidx = 0; + for (i = 1; i < num_reqs; i++) { + int merge = 0; + int64_t oldreq_last = reqs[outidx].sector + reqs[outidx].nb_sectors; + + // Handle exactly sequential writes and overlapping writes. + if (reqs[i].sector <= oldreq_last) { + merge = 1; + } + + if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > IOV_MAX) { + merge = 0; + } + + if (bs->bl.max_transfer_length && reqs[outidx].nb_sectors + + reqs[i].nb_sectors > bs->bl.max_transfer_length) { + merge = 0; + } + + if (merge) { + size_t size; + QEMUIOVector *qiov = g_malloc0(sizeof(*qiov)); + qemu_iovec_init(qiov, + reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1); + + // Add the first request to the merged one. If the requests are + // overlapping, drop the last sectors of the first request. + size = (reqs[i].sector - reqs[outidx].sector) << 9; + qemu_iovec_concat(qiov, reqs[outidx].qiov, 0, size); + + // We should need to add any zeros between the two requests + assert (reqs[i].sector <= oldreq_last); + + // Add the second request + qemu_iovec_concat(qiov, reqs[i].qiov, 0, reqs[i].qiov->size); + + // Add tail of first request, if necessary + if (qiov->size < reqs[outidx].qiov->size) { + qemu_iovec_concat(qiov, reqs[outidx].qiov, qiov->size, + reqs[outidx].qiov->size - qiov->size); + } + + reqs[outidx].nb_sectors = qiov->size >> 9; + reqs[outidx].qiov = qiov; + + mcb->callbacks[i].free_qiov = reqs[outidx].qiov; + } else { + outidx++; + reqs[outidx].sector = reqs[i].sector; + reqs[outidx].nb_sectors = reqs[i].nb_sectors; + reqs[outidx].qiov = reqs[i].qiov; + } + } + + block_acct_merge_done(&bs->stats, BLOCK_ACCT_WRITE, num_reqs - outidx - 1); + + return outidx + 1; +} + +/* + * Submit multiple AIO write requests at once. + * + * On success, the function returns 0 and all requests in the reqs array have + * been submitted. In error case this function returns -1, and any of the + * requests may or may not be submitted yet. In particular, this means that the + * callback will be called for some of the requests, for others it won't. The + * caller must check the error field of the BlockRequest to wait for the right + * callbacks (if error != 0, no callback will be called). + * + * The implementation may modify the contents of the reqs array, e.g. to merge + * requests. However, the fields opaque and error are left unmodified as they + * are used to signal failure for a single request to the caller. + */ +int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs) +{ + MultiwriteCB *mcb; + int i; + + /* don't submit writes if we don't have a medium */ + if (bs->drv == NULL) { + for (i = 0; i < num_reqs; i++) { + reqs[i].error = -ENOMEDIUM; + } + return -1; + } + + if (num_reqs == 0) { + return 0; + } + + // Create MultiwriteCB structure + mcb = g_malloc0(sizeof(*mcb) + num_reqs * sizeof(*mcb->callbacks)); + mcb->num_requests = 0; + mcb->num_callbacks = num_reqs; + + for (i = 0; i < num_reqs; i++) { + mcb->callbacks[i].cb = reqs[i].cb; + mcb->callbacks[i].opaque = reqs[i].opaque; + } + + // Check for mergable requests + num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb); + + trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs); + + /* Run the aio requests. */ + mcb->num_requests = num_reqs; + for (i = 0; i < num_reqs; i++) { + bdrv_co_aio_rw_vector(bs, reqs[i].sector, reqs[i].qiov, + reqs[i].nb_sectors, reqs[i].flags, + multiwrite_cb, mcb, + true); + } + + return 0; +} + +void bdrv_aio_cancel(BlockAIOCB *acb) +{ + qemu_aio_ref(acb); + bdrv_aio_cancel_async(acb); + while (acb->refcnt > 1) { + if (acb->aiocb_info->get_aio_context) { + aio_poll(acb->aiocb_info->get_aio_context(acb), true); + } else if (acb->bs) { + aio_poll(bdrv_get_aio_context(acb->bs), true); + } else { + abort(); + } + } + qemu_aio_unref(acb); +} + +/* Async version of aio cancel. The caller is not blocked if the acb implements + * cancel_async, otherwise we do nothing and let the request normally complete. + * In either case the completion callback must be called. */ +void bdrv_aio_cancel_async(BlockAIOCB *acb) +{ + if (acb->aiocb_info->cancel_async) { + acb->aiocb_info->cancel_async(acb); + } +} + +/**************************************************************/ +/* async block device emulation */ + +typedef struct BlockAIOCBSync { + BlockAIOCB common; + QEMUBH *bh; + int ret; + /* vector translation state */ + QEMUIOVector *qiov; + uint8_t *bounce; + int is_write; +} BlockAIOCBSync; + +static const AIOCBInfo bdrv_em_aiocb_info = { + .aiocb_size = sizeof(BlockAIOCBSync), +}; + +static void bdrv_aio_bh_cb(void *opaque) +{ + BlockAIOCBSync *acb = opaque; + + if (!acb->is_write && acb->ret >= 0) { + qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size); + } + qemu_vfree(acb->bounce); + acb->common.cb(acb->common.opaque, acb->ret); + qemu_bh_delete(acb->bh); + acb->bh = NULL; + qemu_aio_unref(acb); +} + +static BlockAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque, + int is_write) + +{ + BlockAIOCBSync *acb; + + acb = qemu_aio_get(&bdrv_em_aiocb_info, bs, cb, opaque); + acb->is_write = is_write; + acb->qiov = qiov; + acb->bounce = qemu_try_blockalign(bs, qiov->size); + acb->bh = aio_bh_new(bdrv_get_aio_context(bs), bdrv_aio_bh_cb, acb); + + if (acb->bounce == NULL) { + acb->ret = -ENOMEM; + } else if (is_write) { + qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size); + acb->ret = bs->drv->bdrv_write(bs, sector_num, acb->bounce, nb_sectors); + } else { + acb->ret = bs->drv->bdrv_read(bs, sector_num, acb->bounce, nb_sectors); + } + + qemu_bh_schedule(acb->bh); + + return &acb->common; +} + +static BlockAIOCB *bdrv_aio_readv_em(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); +} + +static BlockAIOCB *bdrv_aio_writev_em(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); +} + + +typedef struct BlockAIOCBCoroutine { + BlockAIOCB common; + BlockRequest req; + bool is_write; + bool need_bh; + bool *done; + QEMUBH* bh; +} BlockAIOCBCoroutine; + +static const AIOCBInfo bdrv_em_co_aiocb_info = { + .aiocb_size = sizeof(BlockAIOCBCoroutine), +}; + +static void bdrv_co_complete(BlockAIOCBCoroutine *acb) +{ + if (!acb->need_bh) { + acb->common.cb(acb->common.opaque, acb->req.error); + qemu_aio_unref(acb); + } +} + +static void bdrv_co_em_bh(void *opaque) +{ + BlockAIOCBCoroutine *acb = opaque; + + assert(!acb->need_bh); + qemu_bh_delete(acb->bh); + bdrv_co_complete(acb); +} + +static void bdrv_co_maybe_schedule_bh(BlockAIOCBCoroutine *acb) +{ + acb->need_bh = false; + if (acb->req.error != -EINPROGRESS) { + BlockDriverState *bs = acb->common.bs; + + acb->bh = aio_bh_new(bdrv_get_aio_context(bs), bdrv_co_em_bh, acb); + qemu_bh_schedule(acb->bh); + } +} + +/* Invoke bdrv_co_do_readv/bdrv_co_do_writev */ +static void coroutine_fn bdrv_co_do_rw(void *opaque) +{ + BlockAIOCBCoroutine *acb = opaque; + BlockDriverState *bs = acb->common.bs; + + if (!acb->is_write) { + acb->req.error = bdrv_co_do_readv(bs, acb->req.sector, + acb->req.nb_sectors, acb->req.qiov, acb->req.flags); + } else { + acb->req.error = bdrv_co_do_writev(bs, acb->req.sector, + acb->req.nb_sectors, acb->req.qiov, acb->req.flags); + } + + bdrv_co_complete(acb); +} + +static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BdrvRequestFlags flags, + BlockCompletionFunc *cb, + void *opaque, + bool is_write) +{ + Coroutine *co; + BlockAIOCBCoroutine *acb; + + acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque); + acb->need_bh = true; + acb->req.error = -EINPROGRESS; + acb->req.sector = sector_num; + acb->req.nb_sectors = nb_sectors; + acb->req.qiov = qiov; + acb->req.flags = flags; + acb->is_write = is_write; + + co = qemu_coroutine_create(bdrv_co_do_rw); + qemu_coroutine_enter(co, acb); + + bdrv_co_maybe_schedule_bh(acb); + return &acb->common; +} + +static void coroutine_fn bdrv_aio_flush_co_entry(void *opaque) +{ + BlockAIOCBCoroutine *acb = opaque; + BlockDriverState *bs = acb->common.bs; + + acb->req.error = bdrv_co_flush(bs); + bdrv_co_complete(acb); +} + +BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, + BlockCompletionFunc *cb, void *opaque) +{ + trace_bdrv_aio_flush(bs, opaque); + + Coroutine *co; + BlockAIOCBCoroutine *acb; + + acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque); + acb->need_bh = true; + acb->req.error = -EINPROGRESS; + + co = qemu_coroutine_create(bdrv_aio_flush_co_entry); + qemu_coroutine_enter(co, acb); + + bdrv_co_maybe_schedule_bh(acb); + return &acb->common; +} + +static void coroutine_fn bdrv_aio_discard_co_entry(void *opaque) +{ + BlockAIOCBCoroutine *acb = opaque; + BlockDriverState *bs = acb->common.bs; + + acb->req.error = bdrv_co_discard(bs, acb->req.sector, acb->req.nb_sectors); + bdrv_co_complete(acb); +} + +BlockAIOCB *bdrv_aio_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + Coroutine *co; + BlockAIOCBCoroutine *acb; + + trace_bdrv_aio_discard(bs, sector_num, nb_sectors, opaque); + + acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque); + acb->need_bh = true; + acb->req.error = -EINPROGRESS; + acb->req.sector = sector_num; + acb->req.nb_sectors = nb_sectors; + co = qemu_coroutine_create(bdrv_aio_discard_co_entry); + qemu_coroutine_enter(co, acb); + + bdrv_co_maybe_schedule_bh(acb); + return &acb->common; +} + +void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, + BlockCompletionFunc *cb, void *opaque) +{ + BlockAIOCB *acb; + + acb = g_slice_alloc(aiocb_info->aiocb_size); + acb->aiocb_info = aiocb_info; + acb->bs = bs; + acb->cb = cb; + acb->opaque = opaque; + acb->refcnt = 1; + return acb; +} + +void qemu_aio_ref(void *p) +{ + BlockAIOCB *acb = p; + acb->refcnt++; +} + +void qemu_aio_unref(void *p) +{ + BlockAIOCB *acb = p; + assert(acb->refcnt > 0); + if (--acb->refcnt == 0) { + g_slice_free1(acb->aiocb_info->aiocb_size, acb); + } +} + +/**************************************************************/ +/* Coroutine block device emulation */ + +typedef struct CoroutineIOCompletion { + Coroutine *coroutine; + int ret; +} CoroutineIOCompletion; + +static void bdrv_co_io_em_complete(void *opaque, int ret) +{ + CoroutineIOCompletion *co = opaque; + + co->ret = ret; + qemu_coroutine_enter(co->coroutine, NULL); +} + +static int coroutine_fn bdrv_co_io_em(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *iov, + bool is_write) +{ + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + BlockAIOCB *acb; + + if (is_write) { + acb = bs->drv->bdrv_aio_writev(bs, sector_num, iov, nb_sectors, + bdrv_co_io_em_complete, &co); + } else { + acb = bs->drv->bdrv_aio_readv(bs, sector_num, iov, nb_sectors, + bdrv_co_io_em_complete, &co); + } + + trace_bdrv_co_io_em(bs, sector_num, nb_sectors, is_write, acb); + if (!acb) { + return -EIO; + } + qemu_coroutine_yield(); + + return co.ret; +} + +static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, false); +} + +static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, true); +} + +static void coroutine_fn bdrv_flush_co_entry(void *opaque) +{ + RwCo *rwco = opaque; + + rwco->ret = bdrv_co_flush(rwco->bs); +} + +int coroutine_fn bdrv_co_flush(BlockDriverState *bs) +{ + int ret; + + if (!bs || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { + return 0; + } + + /* Write back cached data to the OS even with cache=unsafe */ + BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS); + if (bs->drv->bdrv_co_flush_to_os) { + ret = bs->drv->bdrv_co_flush_to_os(bs); + if (ret < 0) { + return ret; + } + } + + /* But don't actually force it to the disk with cache=unsafe */ + if (bs->open_flags & BDRV_O_NO_FLUSH) { + goto flush_parent; + } + + BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK); + if (bs->drv->bdrv_co_flush_to_disk) { + ret = bs->drv->bdrv_co_flush_to_disk(bs); + } else if (bs->drv->bdrv_aio_flush) { + BlockAIOCB *acb; + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + + acb = bs->drv->bdrv_aio_flush(bs, bdrv_co_io_em_complete, &co); + if (acb == NULL) { + ret = -EIO; + } else { + qemu_coroutine_yield(); + ret = co.ret; + } + } else { + /* + * Some block drivers always operate in either writethrough or unsafe + * mode and don't support bdrv_flush therefore. Usually qemu doesn't + * know how the server works (because the behaviour is hardcoded or + * depends on server-side configuration), so we can't ensure that + * everything is safe on disk. Returning an error doesn't work because + * that would break guests even if the server operates in writethrough + * mode. + * + * Let's hope the user knows what he's doing. + */ + ret = 0; + } + if (ret < 0) { + return ret; + } + + /* Now flush the underlying protocol. It will also have BDRV_O_NO_FLUSH + * in the case of cache=unsafe, so there are no useless flushes. + */ +flush_parent: + return bdrv_co_flush(bs->file); +} + +int bdrv_flush(BlockDriverState *bs) +{ + Coroutine *co; + RwCo rwco = { + .bs = bs, + .ret = NOT_DONE, + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_flush_co_entry(&rwco); + } else { + AioContext *aio_context = bdrv_get_aio_context(bs); + + co = qemu_coroutine_create(bdrv_flush_co_entry); + qemu_coroutine_enter(co, &rwco); + while (rwco.ret == NOT_DONE) { + aio_poll(aio_context, true); + } + } + + return rwco.ret; +} + +typedef struct DiscardCo { + BlockDriverState *bs; + int64_t sector_num; + int nb_sectors; + int ret; +} DiscardCo; +static void coroutine_fn bdrv_discard_co_entry(void *opaque) +{ + DiscardCo *rwco = opaque; + + rwco->ret = bdrv_co_discard(rwco->bs, rwco->sector_num, rwco->nb_sectors); +} + +int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + int max_discard, ret; + + if (!bs->drv) { + return -ENOMEDIUM; + } + + ret = bdrv_check_request(bs, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } else if (bs->read_only) { + return -EPERM; + } + + bdrv_reset_dirty(bs, sector_num, nb_sectors); + + /* Do nothing if disabled. */ + if (!(bs->open_flags & BDRV_O_UNMAP)) { + return 0; + } + + if (!bs->drv->bdrv_co_discard && !bs->drv->bdrv_aio_discard) { + return 0; + } + + max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS); + while (nb_sectors > 0) { + int ret; + int num = nb_sectors; + + /* align request */ + if (bs->bl.discard_alignment && + num >= bs->bl.discard_alignment && + sector_num % bs->bl.discard_alignment) { + if (num > bs->bl.discard_alignment) { + num = bs->bl.discard_alignment; + } + num -= sector_num % bs->bl.discard_alignment; + } + + /* limit request size */ + if (num > max_discard) { + num = max_discard; + } + + if (bs->drv->bdrv_co_discard) { + ret = bs->drv->bdrv_co_discard(bs, sector_num, num); + } else { + BlockAIOCB *acb; + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + + acb = bs->drv->bdrv_aio_discard(bs, sector_num, nb_sectors, + bdrv_co_io_em_complete, &co); + if (acb == NULL) { + return -EIO; + } else { + qemu_coroutine_yield(); + ret = co.ret; + } + } + if (ret && ret != -ENOTSUP) { + return ret; + } + + sector_num += num; + nb_sectors -= num; + } + return 0; +} + +int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) +{ + Coroutine *co; + DiscardCo rwco = { + .bs = bs, + .sector_num = sector_num, + .nb_sectors = nb_sectors, + .ret = NOT_DONE, + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_discard_co_entry(&rwco); + } else { + AioContext *aio_context = bdrv_get_aio_context(bs); + + co = qemu_coroutine_create(bdrv_discard_co_entry); + qemu_coroutine_enter(co, &rwco); + while (rwco.ret == NOT_DONE) { + aio_poll(aio_context, true); + } + } + + return rwco.ret; +} + +/* needed for generic scsi interface */ + +int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_ioctl) + return drv->bdrv_ioctl(bs, req, buf); + return -ENOTSUP; +} + +BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, + unsigned long int req, void *buf, + BlockCompletionFunc *cb, void *opaque) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_aio_ioctl) + return drv->bdrv_aio_ioctl(bs, req, buf, cb, opaque); + return NULL; +} + +void *qemu_blockalign(BlockDriverState *bs, size_t size) +{ + return qemu_memalign(bdrv_opt_mem_align(bs), size); +} + +void *qemu_blockalign0(BlockDriverState *bs, size_t size) +{ + return memset(qemu_blockalign(bs, size), 0, size); +} + +void *qemu_try_blockalign(BlockDriverState *bs, size_t size) +{ + size_t align = bdrv_opt_mem_align(bs); + + /* Ensure that NULL is never returned on success */ + assert(align > 0); + if (size == 0) { + size = align; + } + + return qemu_try_memalign(align, size); +} + +void *qemu_try_blockalign0(BlockDriverState *bs, size_t size) +{ + void *mem = qemu_try_blockalign(bs, size); + + if (mem) { + memset(mem, 0, size); + } + + return mem; +} + +/* + * Check if all memory in this vector is sector aligned. + */ +bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) +{ + int i; + size_t alignment = bdrv_min_mem_align(bs); + + for (i = 0; i < qiov->niov; i++) { + if ((uintptr_t) qiov->iov[i].iov_base % alignment) { + return false; + } + if (qiov->iov[i].iov_len % alignment) { + return false; + } + } + + return true; +} + +void bdrv_add_before_write_notifier(BlockDriverState *bs, + NotifierWithReturn *notifier) +{ + notifier_with_return_list_add(&bs->before_write_notifiers, notifier); +} + +void bdrv_io_plug(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (drv && drv->bdrv_io_plug) { + drv->bdrv_io_plug(bs); + } else if (bs->file) { + bdrv_io_plug(bs->file); + } +} + +void bdrv_io_unplug(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (drv && drv->bdrv_io_unplug) { + drv->bdrv_io_unplug(bs); + } else if (bs->file) { + bdrv_io_unplug(bs->file); + } +} + +void bdrv_flush_io_queue(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (drv && drv->bdrv_flush_io_queue) { + drv->bdrv_flush_io_queue(bs); + } else if (bs->file) { + bdrv_flush_io_queue(bs->file); + } +} diff --git a/block/iscsi.c b/block/iscsi.c new file mode 100644 index 0000000..8fca1d3 --- /dev/null +++ b/block/iscsi.c @@ -0,0 +1,1760 @@ +/* + * QEMU Block driver for iSCSI images + * + * Copyright (c) 2010-2011 Ronnie Sahlberg + * Copyright (c) 2012-2015 Peter Lieven + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config-host.h" + +#include +#include +#include +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "qemu/bitops.h" +#include "qemu/bitmap.h" +#include "block/block_int.h" +#include "block/scsi.h" +#include "qemu/iov.h" +#include "sysemu/sysemu.h" +#include "qmp-commands.h" + +#include +#include + +#ifdef __linux__ +#include +#include +#endif + +typedef struct IscsiLun { + struct iscsi_context *iscsi; + AioContext *aio_context; + int lun; + enum scsi_inquiry_peripheral_device_type type; + int block_size; + uint64_t num_blocks; + int events; + QEMUTimer *nop_timer; + QEMUTimer *event_timer; + struct scsi_inquiry_logical_block_provisioning lbp; + struct scsi_inquiry_block_limits bl; + unsigned char *zeroblock; + unsigned long *allocationmap; + int cluster_sectors; + bool use_16_for_rw; + bool write_protected; + bool lbpme; + bool lbprz; + bool dpofua; + bool has_write_same; + bool force_next_flush; +} IscsiLun; + +typedef struct IscsiTask { + int status; + int complete; + int retries; + int do_retry; + struct scsi_task *task; + Coroutine *co; + QEMUBH *bh; + IscsiLun *iscsilun; + QEMUTimer retry_timer; + bool force_next_flush; +} IscsiTask; + +typedef struct IscsiAIOCB { + BlockAIOCB common; + QEMUIOVector *qiov; + QEMUBH *bh; + IscsiLun *iscsilun; + struct scsi_task *task; + uint8_t *buf; + int status; + int64_t sector_num; + int nb_sectors; +#ifdef __linux__ + sg_io_hdr_t *ioh; +#endif +} IscsiAIOCB; + +#define EVENT_INTERVAL 250 +#define NOP_INTERVAL 5000 +#define MAX_NOP_FAILURES 3 +#define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times) +static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048, 8192, 32768}; + +/* this threshold is a trade-off knob to choose between + * the potential additional overhead of an extra GET_LBA_STATUS request + * vs. unnecessarily reading a lot of zero sectors over the wire. + * If a read request is greater or equal than ISCSI_CHECKALLOC_THRES + * sectors we check the allocation status of the area covered by the + * request first if the allocationmap indicates that the area might be + * unallocated. */ +#define ISCSI_CHECKALLOC_THRES 64 + +static void +iscsi_bh_cb(void *p) +{ + IscsiAIOCB *acb = p; + + qemu_bh_delete(acb->bh); + + g_free(acb->buf); + acb->buf = NULL; + + acb->common.cb(acb->common.opaque, acb->status); + + if (acb->task != NULL) { + scsi_free_scsi_task(acb->task); + acb->task = NULL; + } + + qemu_aio_unref(acb); +} + +static void +iscsi_schedule_bh(IscsiAIOCB *acb) +{ + if (acb->bh) { + return; + } + acb->bh = aio_bh_new(acb->iscsilun->aio_context, iscsi_bh_cb, acb); + qemu_bh_schedule(acb->bh); +} + +static void iscsi_co_generic_bh_cb(void *opaque) +{ + struct IscsiTask *iTask = opaque; + iTask->complete = 1; + qemu_bh_delete(iTask->bh); + qemu_coroutine_enter(iTask->co, NULL); +} + +static void iscsi_retry_timer_expired(void *opaque) +{ + struct IscsiTask *iTask = opaque; + iTask->complete = 1; + if (iTask->co) { + qemu_coroutine_enter(iTask->co, NULL); + } +} + +static inline unsigned exp_random(double mean) +{ + return -mean * log((double)rand() / RAND_MAX); +} + +static void +iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, + void *command_data, void *opaque) +{ + struct IscsiTask *iTask = opaque; + struct scsi_task *task = command_data; + + iTask->status = status; + iTask->do_retry = 0; + iTask->task = task; + + if (status != SCSI_STATUS_GOOD) { + if (iTask->retries++ < ISCSI_CMD_RETRIES) { + if (status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { + error_report("iSCSI CheckCondition: %s", + iscsi_get_error(iscsi)); + iTask->do_retry = 1; + goto out; + } + /* status 0x28 is SCSI_TASK_SET_FULL. It was first introduced + * in libiscsi 1.10.0. Hardcode this value here to avoid + * the need to bump the libiscsi requirement to 1.10.0 */ + if (status == SCSI_STATUS_BUSY || status == 0x28) { + unsigned retry_time = + exp_random(iscsi_retry_times[iTask->retries - 1]); + error_report("iSCSI Busy/TaskSetFull (retry #%u in %u ms): %s", + iTask->retries, retry_time, + iscsi_get_error(iscsi)); + aio_timer_init(iTask->iscsilun->aio_context, + &iTask->retry_timer, QEMU_CLOCK_REALTIME, + SCALE_MS, iscsi_retry_timer_expired, iTask); + timer_mod(&iTask->retry_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time); + iTask->do_retry = 1; + return; + } + } + error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); + } else { + iTask->iscsilun->force_next_flush |= iTask->force_next_flush; + } + +out: + if (iTask->co) { + iTask->bh = aio_bh_new(iTask->iscsilun->aio_context, + iscsi_co_generic_bh_cb, iTask); + qemu_bh_schedule(iTask->bh); + } else { + iTask->complete = 1; + } +} + +static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) +{ + *iTask = (struct IscsiTask) { + .co = qemu_coroutine_self(), + .iscsilun = iscsilun, + }; +} + +static void +iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, + void *private_data) +{ + IscsiAIOCB *acb = private_data; + + acb->status = -ECANCELED; + iscsi_schedule_bh(acb); +} + +static void +iscsi_aio_cancel(BlockAIOCB *blockacb) +{ + IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; + IscsiLun *iscsilun = acb->iscsilun; + + if (acb->status != -EINPROGRESS) { + return; + } + + /* send a task mgmt call to the target to cancel the task on the target */ + iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, + iscsi_abort_task_cb, acb); + +} + +static const AIOCBInfo iscsi_aiocb_info = { + .aiocb_size = sizeof(IscsiAIOCB), + .cancel_async = iscsi_aio_cancel, +}; + + +static void iscsi_process_read(void *arg); +static void iscsi_process_write(void *arg); + +static void +iscsi_set_events(IscsiLun *iscsilun) +{ + struct iscsi_context *iscsi = iscsilun->iscsi; + int ev = iscsi_which_events(iscsi); + + if (ev != iscsilun->events) { + aio_set_fd_handler(iscsilun->aio_context, + iscsi_get_fd(iscsi), + (ev & POLLIN) ? iscsi_process_read : NULL, + (ev & POLLOUT) ? iscsi_process_write : NULL, + iscsilun); + iscsilun->events = ev; + } + + /* newer versions of libiscsi may return zero events. In this + * case start a timer to ensure we are able to return to service + * once this situation changes. */ + if (!ev) { + timer_mod(iscsilun->event_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); + } +} + +static void iscsi_timed_set_events(void *opaque) +{ + IscsiLun *iscsilun = opaque; + iscsi_set_events(iscsilun); +} + +static void +iscsi_process_read(void *arg) +{ + IscsiLun *iscsilun = arg; + struct iscsi_context *iscsi = iscsilun->iscsi; + + iscsi_service(iscsi, POLLIN); + iscsi_set_events(iscsilun); +} + +static void +iscsi_process_write(void *arg) +{ + IscsiLun *iscsilun = arg; + struct iscsi_context *iscsi = iscsilun->iscsi; + + iscsi_service(iscsi, POLLOUT); + iscsi_set_events(iscsilun); +} + +static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun) +{ + return sector * iscsilun->block_size / BDRV_SECTOR_SIZE; +} + +static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun) +{ + return sector * BDRV_SECTOR_SIZE / iscsilun->block_size; +} + +static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors, + IscsiLun *iscsilun) +{ + if ((sector_num * BDRV_SECTOR_SIZE) % iscsilun->block_size || + (nb_sectors * BDRV_SECTOR_SIZE) % iscsilun->block_size) { + error_report("iSCSI misaligned request: " + "iscsilun->block_size %u, sector_num %" PRIi64 + ", nb_sectors %d", + iscsilun->block_size, sector_num, nb_sectors); + return 0; + } + return 1; +} + +static unsigned long *iscsi_allocationmap_init(IscsiLun *iscsilun) +{ + return bitmap_try_new(DIV_ROUND_UP(sector_lun2qemu(iscsilun->num_blocks, + iscsilun), + iscsilun->cluster_sectors)); +} + +static void iscsi_allocationmap_set(IscsiLun *iscsilun, int64_t sector_num, + int nb_sectors) +{ + if (iscsilun->allocationmap == NULL) { + return; + } + bitmap_set(iscsilun->allocationmap, + sector_num / iscsilun->cluster_sectors, + DIV_ROUND_UP(nb_sectors, iscsilun->cluster_sectors)); +} + +static void iscsi_allocationmap_clear(IscsiLun *iscsilun, int64_t sector_num, + int nb_sectors) +{ + int64_t cluster_num, nb_clusters; + if (iscsilun->allocationmap == NULL) { + return; + } + cluster_num = DIV_ROUND_UP(sector_num, iscsilun->cluster_sectors); + nb_clusters = (sector_num + nb_sectors) / iscsilun->cluster_sectors + - cluster_num; + if (nb_clusters > 0) { + bitmap_clear(iscsilun->allocationmap, cluster_num, nb_clusters); + } +} + +static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + IscsiLun *iscsilun = bs->opaque; + struct IscsiTask iTask; + uint64_t lba; + uint32_t num_sectors; + int fua; + + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + return -EINVAL; + } + + if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) { + error_report("iSCSI Error: Write of %d sectors exceeds max_xfer_len " + "of %d sectors", nb_sectors, bs->bl.max_transfer_length); + return -EINVAL; + } + + lba = sector_qemu2lun(sector_num, iscsilun); + num_sectors = sector_qemu2lun(nb_sectors, iscsilun); + iscsi_co_init_iscsitask(iscsilun, &iTask); +retry: + fua = iscsilun->dpofua && !bs->enable_write_cache; + iTask.force_next_flush = !fua; + if (iscsilun->use_16_for_rw) { + iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba, + NULL, num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, fua, 0, 0, + iscsi_co_generic_cb, &iTask); + } else { + iTask.task = iscsi_write10_task(iscsilun->iscsi, iscsilun->lun, lba, + NULL, num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, fua, 0, 0, + iscsi_co_generic_cb, &iTask); + } + if (iTask.task == NULL) { + return -ENOMEM; + } + scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, + iov->niov); + while (!iTask.complete) { + iscsi_set_events(iscsilun); + qemu_coroutine_yield(); + } + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; + } + + if (iTask.do_retry) { + iTask.complete = 0; + goto retry; + } + + if (iTask.status != SCSI_STATUS_GOOD) { + return -EIO; + } + + iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors); + + return 0; +} + + +static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun, + int64_t sector_num, int nb_sectors) +{ + unsigned long size; + if (iscsilun->allocationmap == NULL) { + return true; + } + size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors); + return !(find_next_bit(iscsilun->allocationmap, size, + sector_num / iscsilun->cluster_sectors) == size); +} + +static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + IscsiLun *iscsilun = bs->opaque; + struct scsi_get_lba_status *lbas = NULL; + struct scsi_lba_status_descriptor *lbasd = NULL; + struct IscsiTask iTask; + int64_t ret; + + iscsi_co_init_iscsitask(iscsilun, &iTask); + + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + ret = -EINVAL; + goto out; + } + + /* default to all sectors allocated */ + ret = BDRV_BLOCK_DATA; + ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID; + *pnum = nb_sectors; + + /* LUN does not support logical block provisioning */ + if (!iscsilun->lbpme) { + goto out; + } + +retry: + if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun, + sector_qemu2lun(sector_num, iscsilun), + 8 + 16, iscsi_co_generic_cb, + &iTask) == NULL) { + ret = -ENOMEM; + goto out; + } + + while (!iTask.complete) { + iscsi_set_events(iscsilun); + qemu_coroutine_yield(); + } + + if (iTask.do_retry) { + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; + } + iTask.complete = 0; + goto retry; + } + + if (iTask.status != SCSI_STATUS_GOOD) { + /* in case the get_lba_status_callout fails (i.e. + * because the device is busy or the cmd is not + * supported) we pretend all blocks are allocated + * for backwards compatibility */ + goto out; + } + + lbas = scsi_datain_unmarshall(iTask.task); + if (lbas == NULL) { + ret = -EIO; + goto out; + } + + lbasd = &lbas->descriptors[0]; + + if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) { + ret = -EIO; + goto out; + } + + *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun); + + if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || + lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { + ret &= ~BDRV_BLOCK_DATA; + if (iscsilun->lbprz) { + ret |= BDRV_BLOCK_ZERO; + } + } + + if (ret & BDRV_BLOCK_ZERO) { + iscsi_allocationmap_clear(iscsilun, sector_num, *pnum); + } else { + iscsi_allocationmap_set(iscsilun, sector_num, *pnum); + } + + if (*pnum > nb_sectors) { + *pnum = nb_sectors; + } +out: + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + } + return ret; +} + +static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + IscsiLun *iscsilun = bs->opaque; + struct IscsiTask iTask; + uint64_t lba; + uint32_t num_sectors; + + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + return -EINVAL; + } + + if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) { + error_report("iSCSI Error: Read of %d sectors exceeds max_xfer_len " + "of %d sectors", nb_sectors, bs->bl.max_transfer_length); + return -EINVAL; + } + + if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES && + !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { + int64_t ret; + int pnum; + ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum); + if (ret < 0) { + return ret; + } + if (ret & BDRV_BLOCK_ZERO && pnum >= nb_sectors) { + qemu_iovec_memset(iov, 0, 0x00, iov->size); + return 0; + } + } + + lba = sector_qemu2lun(sector_num, iscsilun); + num_sectors = sector_qemu2lun(nb_sectors, iscsilun); + + iscsi_co_init_iscsitask(iscsilun, &iTask); +retry: + if (iscsilun->use_16_for_rw) { + iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba, + num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, 0, 0, 0, + iscsi_co_generic_cb, &iTask); + } else { + iTask.task = iscsi_read10_task(iscsilun->iscsi, iscsilun->lun, lba, + num_sectors * iscsilun->block_size, + iscsilun->block_size, + 0, 0, 0, 0, 0, + iscsi_co_generic_cb, &iTask); + } + if (iTask.task == NULL) { + return -ENOMEM; + } + scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); + + while (!iTask.complete) { + iscsi_set_events(iscsilun); + qemu_coroutine_yield(); + } + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; + } + + if (iTask.do_retry) { + iTask.complete = 0; + goto retry; + } + + if (iTask.status != SCSI_STATUS_GOOD) { + return -EIO; + } + + return 0; +} + +static int coroutine_fn iscsi_co_flush(BlockDriverState *bs) +{ + IscsiLun *iscsilun = bs->opaque; + struct IscsiTask iTask; + + if (bs->sg) { + return 0; + } + + if (!iscsilun->force_next_flush) { + return 0; + } + iscsilun->force_next_flush = false; + + iscsi_co_init_iscsitask(iscsilun, &iTask); +retry: + if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0, + 0, iscsi_co_generic_cb, &iTask) == NULL) { + return -ENOMEM; + } + + while (!iTask.complete) { + iscsi_set_events(iscsilun); + qemu_coroutine_yield(); + } + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; + } + + if (iTask.do_retry) { + iTask.complete = 0; + goto retry; + } + + if (iTask.status != SCSI_STATUS_GOOD) { + return -EIO; + } + + return 0; +} + +#ifdef __linux__ +static void +iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, + void *command_data, void *opaque) +{ + IscsiAIOCB *acb = opaque; + + g_free(acb->buf); + acb->buf = NULL; + + acb->status = 0; + if (status < 0) { + error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s", + iscsi_get_error(iscsi)); + acb->status = -EIO; + } + + acb->ioh->driver_status = 0; + acb->ioh->host_status = 0; + acb->ioh->resid = 0; + +#define SG_ERR_DRIVER_SENSE 0x08 + + if (status == SCSI_STATUS_CHECK_CONDITION && acb->task->datain.size >= 2) { + int ss; + + acb->ioh->driver_status |= SG_ERR_DRIVER_SENSE; + + acb->ioh->sb_len_wr = acb->task->datain.size - 2; + ss = (acb->ioh->mx_sb_len >= acb->ioh->sb_len_wr) ? + acb->ioh->mx_sb_len : acb->ioh->sb_len_wr; + memcpy(acb->ioh->sbp, &acb->task->datain.data[2], ss); + } + + iscsi_schedule_bh(acb); +} + +static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, + unsigned long int req, void *buf, + BlockCompletionFunc *cb, void *opaque) +{ + IscsiLun *iscsilun = bs->opaque; + struct iscsi_context *iscsi = iscsilun->iscsi; + struct iscsi_data data; + IscsiAIOCB *acb; + + assert(req == SG_IO); + + acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); + + acb->iscsilun = iscsilun; + acb->bh = NULL; + acb->status = -EINPROGRESS; + acb->buf = NULL; + acb->ioh = buf; + + acb->task = malloc(sizeof(struct scsi_task)); + if (acb->task == NULL) { + error_report("iSCSI: Failed to allocate task for scsi command. %s", + iscsi_get_error(iscsi)); + qemu_aio_unref(acb); + return NULL; + } + memset(acb->task, 0, sizeof(struct scsi_task)); + + switch (acb->ioh->dxfer_direction) { + case SG_DXFER_TO_DEV: + acb->task->xfer_dir = SCSI_XFER_WRITE; + break; + case SG_DXFER_FROM_DEV: + acb->task->xfer_dir = SCSI_XFER_READ; + break; + default: + acb->task->xfer_dir = SCSI_XFER_NONE; + break; + } + + acb->task->cdb_size = acb->ioh->cmd_len; + memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len); + acb->task->expxferlen = acb->ioh->dxfer_len; + + data.size = 0; + if (acb->task->xfer_dir == SCSI_XFER_WRITE) { + if (acb->ioh->iovec_count == 0) { + data.data = acb->ioh->dxferp; + data.size = acb->ioh->dxfer_len; + } else { + scsi_task_set_iov_out(acb->task, + (struct scsi_iovec *) acb->ioh->dxferp, + acb->ioh->iovec_count); + } + } + + if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, + iscsi_aio_ioctl_cb, + (data.size > 0) ? &data : NULL, + acb) != 0) { + scsi_free_scsi_task(acb->task); + qemu_aio_unref(acb); + return NULL; + } + + /* tell libiscsi to read straight into the buffer we got from ioctl */ + if (acb->task->xfer_dir == SCSI_XFER_READ) { + if (acb->ioh->iovec_count == 0) { + scsi_task_add_data_in_buffer(acb->task, + acb->ioh->dxfer_len, + acb->ioh->dxferp); + } else { + scsi_task_set_iov_in(acb->task, + (struct scsi_iovec *) acb->ioh->dxferp, + acb->ioh->iovec_count); + } + } + + iscsi_set_events(iscsilun); + + return &acb->common; +} + +static void ioctl_cb(void *opaque, int status) +{ + int *p_status = opaque; + *p_status = status; +} + +static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +{ + IscsiLun *iscsilun = bs->opaque; + int status; + + switch (req) { + case SG_GET_VERSION_NUM: + *(int *)buf = 30000; + break; + case SG_GET_SCSI_ID: + ((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type; + break; + case SG_IO: + status = -EINPROGRESS; + iscsi_aio_ioctl(bs, req, buf, ioctl_cb, &status); + + while (status == -EINPROGRESS) { + aio_poll(iscsilun->aio_context, true); + } + + return 0; + default: + return -1; + } + return 0; +} +#endif + +static int64_t +iscsi_getlength(BlockDriverState *bs) +{ + IscsiLun *iscsilun = bs->opaque; + int64_t len; + + len = iscsilun->num_blocks; + len *= iscsilun->block_size; + + return len; +} + +static int +coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + IscsiLun *iscsilun = bs->opaque; + struct IscsiTask iTask; + struct unmap_list list; + + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + return -EINVAL; + } + + if (!iscsilun->lbp.lbpu) { + /* UNMAP is not supported by the target */ + return 0; + } + + list.lba = sector_qemu2lun(sector_num, iscsilun); + list.num = sector_qemu2lun(nb_sectors, iscsilun); + + iscsi_co_init_iscsitask(iscsilun, &iTask); +retry: + if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1, + iscsi_co_generic_cb, &iTask) == NULL) { + return -ENOMEM; + } + + while (!iTask.complete) { + iscsi_set_events(iscsilun); + qemu_coroutine_yield(); + } + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; + } + + if (iTask.do_retry) { + iTask.complete = 0; + goto retry; + } + + if (iTask.status == SCSI_STATUS_CHECK_CONDITION) { + /* the target might fail with a check condition if it + is not happy with the alignment of the UNMAP request + we silently fail in this case */ + return 0; + } + + if (iTask.status != SCSI_STATUS_GOOD) { + return -EIO; + } + + iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors); + + return 0; +} + +static int +coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags) +{ + IscsiLun *iscsilun = bs->opaque; + struct IscsiTask iTask; + uint64_t lba; + uint32_t nb_blocks; + bool use_16_for_ws = iscsilun->use_16_for_rw; + + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + return -EINVAL; + } + + if (flags & BDRV_REQ_MAY_UNMAP) { + if (!use_16_for_ws && !iscsilun->lbp.lbpws10) { + /* WRITESAME10 with UNMAP is unsupported try WRITESAME16 */ + use_16_for_ws = true; + } + if (use_16_for_ws && !iscsilun->lbp.lbpws) { + /* WRITESAME16 with UNMAP is not supported by the target, + * fall back and try WRITESAME10/16 without UNMAP */ + flags &= ~BDRV_REQ_MAY_UNMAP; + use_16_for_ws = iscsilun->use_16_for_rw; + } + } + + if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) { + /* WRITESAME without UNMAP is not supported by the target */ + return -ENOTSUP; + } + + lba = sector_qemu2lun(sector_num, iscsilun); + nb_blocks = sector_qemu2lun(nb_sectors, iscsilun); + + if (iscsilun->zeroblock == NULL) { + iscsilun->zeroblock = g_try_malloc0(iscsilun->block_size); + if (iscsilun->zeroblock == NULL) { + return -ENOMEM; + } + } + + iscsi_co_init_iscsitask(iscsilun, &iTask); + iTask.force_next_flush = true; +retry: + if (use_16_for_ws) { + iTask.task = iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba, + iscsilun->zeroblock, iscsilun->block_size, + nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), + 0, 0, iscsi_co_generic_cb, &iTask); + } else { + iTask.task = iscsi_writesame10_task(iscsilun->iscsi, iscsilun->lun, lba, + iscsilun->zeroblock, iscsilun->block_size, + nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), + 0, 0, iscsi_co_generic_cb, &iTask); + } + if (iTask.task == NULL) { + return -ENOMEM; + } + + while (!iTask.complete) { + iscsi_set_events(iscsilun); + qemu_coroutine_yield(); + } + + if (iTask.status == SCSI_STATUS_CHECK_CONDITION && + iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && + (iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE || + iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB)) { + /* WRITE SAME is not supported by the target */ + iscsilun->has_write_same = false; + scsi_free_scsi_task(iTask.task); + return -ENOTSUP; + } + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; + } + + if (iTask.do_retry) { + iTask.complete = 0; + goto retry; + } + + if (iTask.status != SCSI_STATUS_GOOD) { + return -EIO; + } + + if (flags & BDRV_REQ_MAY_UNMAP) { + iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors); + } else { + iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors); + } + + return 0; +} + +static void parse_chap(struct iscsi_context *iscsi, const char *target, + Error **errp) +{ + QemuOptsList *list; + QemuOpts *opts; + const char *user = NULL; + const char *password = NULL; + + list = qemu_find_opts("iscsi"); + if (!list) { + return; + } + + opts = qemu_opts_find(list, target); + if (opts == NULL) { + opts = QTAILQ_FIRST(&list->head); + if (!opts) { + return; + } + } + + user = qemu_opt_get(opts, "user"); + if (!user) { + return; + } + + password = qemu_opt_get(opts, "password"); + if (!password) { + error_setg(errp, "CHAP username specified but no password was given"); + return; + } + + if (iscsi_set_initiator_username_pwd(iscsi, user, password)) { + error_setg(errp, "Failed to set initiator username and password"); + } +} + +static void parse_header_digest(struct iscsi_context *iscsi, const char *target, + Error **errp) +{ + QemuOptsList *list; + QemuOpts *opts; + const char *digest = NULL; + + list = qemu_find_opts("iscsi"); + if (!list) { + return; + } + + opts = qemu_opts_find(list, target); + if (opts == NULL) { + opts = QTAILQ_FIRST(&list->head); + if (!opts) { + return; + } + } + + digest = qemu_opt_get(opts, "header-digest"); + if (!digest) { + return; + } + + if (!strcmp(digest, "CRC32C")) { + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C); + } else if (!strcmp(digest, "NONE")) { + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE); + } else if (!strcmp(digest, "CRC32C-NONE")) { + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE); + } else if (!strcmp(digest, "NONE-CRC32C")) { + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); + } else { + error_setg(errp, "Invalid header-digest setting : %s", digest); + } +} + +static char *parse_initiator_name(const char *target) +{ + QemuOptsList *list; + QemuOpts *opts; + const char *name; + char *iscsi_name; + UuidInfo *uuid_info; + + list = qemu_find_opts("iscsi"); + if (list) { + opts = qemu_opts_find(list, target); + if (!opts) { + opts = QTAILQ_FIRST(&list->head); + } + if (opts) { + name = qemu_opt_get(opts, "initiator-name"); + if (name) { + return g_strdup(name); + } + } + } + + uuid_info = qmp_query_uuid(NULL); + if (strcmp(uuid_info->UUID, UUID_NONE) == 0) { + name = qemu_get_vm_name(); + } else { + name = uuid_info->UUID; + } + iscsi_name = g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s", + name ? ":" : "", name ? name : ""); + qapi_free_UuidInfo(uuid_info); + return iscsi_name; +} + +static void iscsi_nop_timed_event(void *opaque) +{ + IscsiLun *iscsilun = opaque; + + if (iscsi_get_nops_in_flight(iscsilun->iscsi) > MAX_NOP_FAILURES) { + error_report("iSCSI: NOP timeout. Reconnecting..."); + iscsi_reconnect(iscsilun->iscsi); + } + + if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) { + error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages."); + return; + } + + timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); + iscsi_set_events(iscsilun); +} + +static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) +{ + struct scsi_task *task = NULL; + struct scsi_readcapacity10 *rc10 = NULL; + struct scsi_readcapacity16 *rc16 = NULL; + int retries = ISCSI_CMD_RETRIES; + + do { + if (task != NULL) { + scsi_free_scsi_task(task); + task = NULL; + } + + switch (iscsilun->type) { + case TYPE_DISK: + task = iscsi_readcapacity16_sync(iscsilun->iscsi, iscsilun->lun); + if (task != NULL && task->status == SCSI_STATUS_GOOD) { + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { + error_setg(errp, "iSCSI: Failed to unmarshall readcapacity16 data."); + } else { + iscsilun->block_size = rc16->block_length; + iscsilun->num_blocks = rc16->returned_lba + 1; + iscsilun->lbpme = !!rc16->lbpme; + iscsilun->lbprz = !!rc16->lbprz; + iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff); + } + } + break; + case TYPE_ROM: + task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0); + if (task != NULL && task->status == SCSI_STATUS_GOOD) { + rc10 = scsi_datain_unmarshall(task); + if (rc10 == NULL) { + error_setg(errp, "iSCSI: Failed to unmarshall readcapacity10 data."); + } else { + iscsilun->block_size = rc10->block_size; + if (rc10->lba == 0) { + /* blank disk loaded */ + iscsilun->num_blocks = 0; + } else { + iscsilun->num_blocks = rc10->lba + 1; + } + } + } + break; + default: + return; + } + } while (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION + && retries-- > 0); + + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + error_setg(errp, "iSCSI: failed to send readcapacity10 command."); + } + if (task) { + scsi_free_scsi_task(task); + } +} + +/* TODO Convert to fine grained options */ +static QemuOptsList runtime_opts = { + .name = "iscsi", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "URL to the iscsi image", + }, + { /* end of list */ } + }, +}; + +static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun, + int evpd, int pc, void **inq, Error **errp) +{ + int full_size; + struct scsi_task *task = NULL; + task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + goto fail; + } + full_size = scsi_datain_getfullsize(task); + if (full_size > task->datain.size) { + scsi_free_scsi_task(task); + + /* we need more data for the full list */ + task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + goto fail; + } + } + + *inq = scsi_datain_unmarshall(task); + if (*inq == NULL) { + error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob"); + goto fail_with_err; + } + + return task; + +fail: + error_setg(errp, "iSCSI: Inquiry command failed : %s", + iscsi_get_error(iscsi)); +fail_with_err: + if (task != NULL) { + scsi_free_scsi_task(task); + } + return NULL; +} + +static void iscsi_detach_aio_context(BlockDriverState *bs) +{ + IscsiLun *iscsilun = bs->opaque; + + aio_set_fd_handler(iscsilun->aio_context, + iscsi_get_fd(iscsilun->iscsi), + NULL, NULL, NULL); + iscsilun->events = 0; + + if (iscsilun->nop_timer) { + timer_del(iscsilun->nop_timer); + timer_free(iscsilun->nop_timer); + iscsilun->nop_timer = NULL; + } + if (iscsilun->event_timer) { + timer_del(iscsilun->event_timer); + timer_free(iscsilun->event_timer); + iscsilun->event_timer = NULL; + } +} + +static void iscsi_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + IscsiLun *iscsilun = bs->opaque; + + iscsilun->aio_context = new_context; + iscsi_set_events(iscsilun); + + /* Set up a timer for sending out iSCSI NOPs */ + iscsilun->nop_timer = aio_timer_new(iscsilun->aio_context, + QEMU_CLOCK_REALTIME, SCALE_MS, + iscsi_nop_timed_event, iscsilun); + timer_mod(iscsilun->nop_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); + + /* Prepare a timer for a delayed call to iscsi_set_events */ + iscsilun->event_timer = aio_timer_new(iscsilun->aio_context, + QEMU_CLOCK_REALTIME, SCALE_MS, + iscsi_timed_set_events, iscsilun); +} + +static void iscsi_modesense_sync(IscsiLun *iscsilun) +{ + struct scsi_task *task; + struct scsi_mode_sense *ms = NULL; + iscsilun->write_protected = false; + iscsilun->dpofua = false; + + task = iscsi_modesense6_sync(iscsilun->iscsi, iscsilun->lun, + 1, SCSI_MODESENSE_PC_CURRENT, + 0x3F, 0, 255); + if (task == NULL) { + error_report("iSCSI: Failed to send MODE_SENSE(6) command: %s", + iscsi_get_error(iscsilun->iscsi)); + goto out; + } + + if (task->status != SCSI_STATUS_GOOD) { + error_report("iSCSI: Failed MODE_SENSE(6), LUN assumed writable"); + goto out; + } + ms = scsi_datain_unmarshall(task); + if (!ms) { + error_report("iSCSI: Failed to unmarshall MODE_SENSE(6) data: %s", + iscsi_get_error(iscsilun->iscsi)); + goto out; + } + iscsilun->write_protected = ms->device_specific_parameter & 0x80; + iscsilun->dpofua = ms->device_specific_parameter & 0x10; + +out: + if (task) { + scsi_free_scsi_task(task); + } +} + +/* + * We support iscsi url's on the form + * iscsi://[%@][:]// + */ +static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + IscsiLun *iscsilun = bs->opaque; + struct iscsi_context *iscsi = NULL; + struct iscsi_url *iscsi_url = NULL; + struct scsi_task *task = NULL; + struct scsi_inquiry_standard *inq = NULL; + struct scsi_inquiry_supported_pages *inq_vpd; + char *initiator_name = NULL; + QemuOpts *opts; + Error *local_err = NULL; + const char *filename; + int i, ret = 0; + + if ((BDRV_SECTOR_SIZE % 512) != 0) { + error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. " + "BDRV_SECTOR_SIZE(%lld) is not a multiple " + "of 512", BDRV_SECTOR_SIZE); + return -EINVAL; + } + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + filename = qemu_opt_get(opts, "filename"); + + iscsi_url = iscsi_parse_full_url(iscsi, filename); + if (iscsi_url == NULL) { + error_setg(errp, "Failed to parse URL : %s", filename); + ret = -EINVAL; + goto out; + } + + memset(iscsilun, 0, sizeof(IscsiLun)); + + initiator_name = parse_initiator_name(iscsi_url->target); + + iscsi = iscsi_create_context(initiator_name); + if (iscsi == NULL) { + error_setg(errp, "iSCSI: Failed to create iSCSI context."); + ret = -ENOMEM; + goto out; + } + + if (iscsi_set_targetname(iscsi, iscsi_url->target)) { + error_setg(errp, "iSCSI: Failed to set target name."); + ret = -EINVAL; + goto out; + } + + if (iscsi_url->user[0] != '\0') { + ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, + iscsi_url->passwd); + if (ret != 0) { + error_setg(errp, "Failed to set initiator username and password"); + ret = -EINVAL; + goto out; + } + } + + /* check if we got CHAP username/password via the options */ + parse_chap(iscsi, iscsi_url->target, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) { + error_setg(errp, "iSCSI: Failed to set session type to normal."); + ret = -EINVAL; + goto out; + } + + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); + + /* check if we got HEADER_DIGEST via the options */ + parse_header_digest(iscsi, iscsi_url->target, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { + error_setg(errp, "iSCSI: Failed to connect to LUN : %s", + iscsi_get_error(iscsi)); + ret = -EINVAL; + goto out; + } + + iscsilun->iscsi = iscsi; + iscsilun->aio_context = bdrv_get_aio_context(bs); + iscsilun->lun = iscsi_url->lun; + iscsilun->has_write_same = true; + + task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 0, 0, + (void **) &inq, errp); + if (task == NULL) { + ret = -EINVAL; + goto out; + } + iscsilun->type = inq->periperal_device_type; + scsi_free_scsi_task(task); + task = NULL; + + iscsi_modesense_sync(iscsilun); + + /* Check the write protect flag of the LUN if we want to write */ + if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && + iscsilun->write_protected) { + error_setg(errp, "Cannot open a write protected LUN as read-write"); + ret = -EACCES; + goto out; + } + + iscsi_readcapacity_sync(iscsilun, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun); + bs->request_alignment = iscsilun->block_size; + + /* We don't have any emulation for devices other than disks and CD-ROMs, so + * this must be sg ioctl compatible. We force it to be sg, otherwise qemu + * will try to read from the device to guess the image format. + */ + if (iscsilun->type != TYPE_DISK && iscsilun->type != TYPE_ROM) { + bs->sg = 1; + } + + task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES, + (void **) &inq_vpd, errp); + if (task == NULL) { + ret = -EINVAL; + goto out; + } + for (i = 0; i < inq_vpd->num_pages; i++) { + struct scsi_task *inq_task; + struct scsi_inquiry_logical_block_provisioning *inq_lbp; + struct scsi_inquiry_block_limits *inq_bl; + switch (inq_vpd->pages[i]) { + case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: + inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, + (void **) &inq_lbp, errp); + if (inq_task == NULL) { + ret = -EINVAL; + goto out; + } + memcpy(&iscsilun->lbp, inq_lbp, + sizeof(struct scsi_inquiry_logical_block_provisioning)); + scsi_free_scsi_task(inq_task); + break; + case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS: + inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, + (void **) &inq_bl, errp); + if (inq_task == NULL) { + ret = -EINVAL; + goto out; + } + memcpy(&iscsilun->bl, inq_bl, + sizeof(struct scsi_inquiry_block_limits)); + scsi_free_scsi_task(inq_task); + break; + default: + break; + } + } + scsi_free_scsi_task(task); + task = NULL; + + iscsi_attach_aio_context(bs, iscsilun->aio_context); + + /* Guess the internal cluster (page) size of the iscsi target by the means + * of opt_unmap_gran. Transfer the unmap granularity only if it has a + * reasonable size */ + if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 4 * 1024 && + iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) { + iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran * + iscsilun->block_size) >> BDRV_SECTOR_BITS; + if (iscsilun->lbprz) { + iscsilun->allocationmap = iscsi_allocationmap_init(iscsilun); + if (iscsilun->allocationmap == NULL) { + ret = -ENOMEM; + } + } + } + +out: + qemu_opts_del(opts); + g_free(initiator_name); + if (iscsi_url != NULL) { + iscsi_destroy_url(iscsi_url); + } + if (task != NULL) { + scsi_free_scsi_task(task); + } + + if (ret) { + if (iscsi != NULL) { + if (iscsi_is_logged_in(iscsi)) { + iscsi_logout_sync(iscsi); + } + iscsi_destroy_context(iscsi); + } + memset(iscsilun, 0, sizeof(IscsiLun)); + } + return ret; +} + +static void iscsi_close(BlockDriverState *bs) +{ + IscsiLun *iscsilun = bs->opaque; + struct iscsi_context *iscsi = iscsilun->iscsi; + + iscsi_detach_aio_context(bs); + if (iscsi_is_logged_in(iscsi)) { + iscsi_logout_sync(iscsi); + } + iscsi_destroy_context(iscsi); + g_free(iscsilun->zeroblock); + g_free(iscsilun->allocationmap); + memset(iscsilun, 0, sizeof(IscsiLun)); +} + +static int sector_limits_lun2qemu(int64_t sector, IscsiLun *iscsilun) +{ + return MIN(sector_lun2qemu(sector, iscsilun), INT_MAX / 2 + 1); +} + +static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* We don't actually refresh here, but just return data queried in + * iscsi_open(): iscsi targets don't change their limits. */ + + IscsiLun *iscsilun = bs->opaque; + uint32_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff; + + if (iscsilun->bl.max_xfer_len) { + max_xfer_len = MIN(max_xfer_len, iscsilun->bl.max_xfer_len); + } + + bs->bl.max_transfer_length = sector_limits_lun2qemu(max_xfer_len, iscsilun); + + if (iscsilun->lbp.lbpu) { + if (iscsilun->bl.max_unmap < 0xffffffff) { + bs->bl.max_discard = + sector_limits_lun2qemu(iscsilun->bl.max_unmap, iscsilun); + } + bs->bl.discard_alignment = + sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun); + } + + if (iscsilun->bl.max_ws_len < 0xffffffff) { + bs->bl.max_write_zeroes = + sector_limits_lun2qemu(iscsilun->bl.max_ws_len, iscsilun); + } + if (iscsilun->lbp.lbpws) { + bs->bl.write_zeroes_alignment = + sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun); + } + bs->bl.opt_transfer_length = + sector_limits_lun2qemu(iscsilun->bl.opt_xfer_len, iscsilun); +} + +/* Note that this will not re-establish a connection with an iSCSI target - it + * is effectively a NOP. */ +static int iscsi_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + IscsiLun *iscsilun = state->bs->opaque; + + if (state->flags & BDRV_O_RDWR && iscsilun->write_protected) { + error_setg(errp, "Cannot open a write protected LUN as read-write"); + return -EACCES; + } + return 0; +} + +static int iscsi_truncate(BlockDriverState *bs, int64_t offset) +{ + IscsiLun *iscsilun = bs->opaque; + Error *local_err = NULL; + + if (iscsilun->type != TYPE_DISK) { + return -ENOTSUP; + } + + iscsi_readcapacity_sync(iscsilun, &local_err); + if (local_err != NULL) { + error_free(local_err); + return -EIO; + } + + if (offset > iscsi_getlength(bs)) { + return -EINVAL; + } + + if (iscsilun->allocationmap != NULL) { + g_free(iscsilun->allocationmap); + iscsilun->allocationmap = iscsi_allocationmap_init(iscsilun); + } + + return 0; +} + +static int iscsi_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int ret = 0; + int64_t total_size = 0; + BlockDriverState *bs; + IscsiLun *iscsilun = NULL; + QDict *bs_options; + + bs = bdrv_new(); + + /* Read out options */ + total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + bs->opaque = g_new0(struct IscsiLun, 1); + iscsilun = bs->opaque; + + bs_options = qdict_new(); + qdict_put(bs_options, "filename", qstring_from_str(filename)); + ret = iscsi_open(bs, bs_options, 0, NULL); + QDECREF(bs_options); + + if (ret != 0) { + goto out; + } + iscsi_detach_aio_context(bs); + if (iscsilun->type != TYPE_DISK) { + ret = -ENODEV; + goto out; + } + if (bs->total_sectors < total_size) { + ret = -ENOSPC; + goto out; + } + + ret = 0; +out: + if (iscsilun->iscsi != NULL) { + iscsi_destroy_context(iscsilun->iscsi); + } + g_free(bs->opaque); + bs->opaque = NULL; + bdrv_unref(bs); + return ret; +} + +static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + IscsiLun *iscsilun = bs->opaque; + bdi->unallocated_blocks_are_zero = iscsilun->lbprz; + bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws; + bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE; + return 0; +} + +static QemuOptsList iscsi_create_opts = { + .name = "iscsi-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_iscsi = { + .format_name = "iscsi", + .protocol_name = "iscsi", + + .instance_size = sizeof(IscsiLun), + .bdrv_needs_filename = true, + .bdrv_file_open = iscsi_open, + .bdrv_close = iscsi_close, + .bdrv_create = iscsi_create, + .create_opts = &iscsi_create_opts, + .bdrv_reopen_prepare = iscsi_reopen_prepare, + + .bdrv_getlength = iscsi_getlength, + .bdrv_get_info = iscsi_get_info, + .bdrv_truncate = iscsi_truncate, + .bdrv_refresh_limits = iscsi_refresh_limits, + + .bdrv_co_get_block_status = iscsi_co_get_block_status, + .bdrv_co_discard = iscsi_co_discard, + .bdrv_co_write_zeroes = iscsi_co_write_zeroes, + .bdrv_co_readv = iscsi_co_readv, + .bdrv_co_writev = iscsi_co_writev, + .bdrv_co_flush_to_disk = iscsi_co_flush, + +#ifdef __linux__ + .bdrv_ioctl = iscsi_ioctl, + .bdrv_aio_ioctl = iscsi_aio_ioctl, +#endif + + .bdrv_detach_aio_context = iscsi_detach_aio_context, + .bdrv_attach_aio_context = iscsi_attach_aio_context, +}; + +static QemuOptsList qemu_iscsi_opts = { + .name = "iscsi", + .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head), + .desc = { + { + .name = "user", + .type = QEMU_OPT_STRING, + .help = "username for CHAP authentication to target", + },{ + .name = "password", + .type = QEMU_OPT_STRING, + .help = "password for CHAP authentication to target", + },{ + .name = "header-digest", + .type = QEMU_OPT_STRING, + .help = "HeaderDigest setting. " + "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}", + },{ + .name = "initiator-name", + .type = QEMU_OPT_STRING, + .help = "Initiator iqn name to use when connecting", + }, + { /* end of list */ } + }, +}; + +static void iscsi_block_init(void) +{ + bdrv_register(&bdrv_iscsi); + qemu_add_opts(&qemu_iscsi_opts); +} + +block_init(iscsi_block_init); diff --git a/block/linux-aio.c b/block/linux-aio.c new file mode 100644 index 0000000..c991443 --- /dev/null +++ b/block/linux-aio.c @@ -0,0 +1,337 @@ +/* + * Linux native AIO support. + * + * Copyright (C) 2009 IBM, Corp. + * Copyright (C) 2009 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu-common.h" +#include "block/aio.h" +#include "qemu/queue.h" +#include "block/raw-aio.h" +#include "qemu/event_notifier.h" + +#include + +/* + * Queue size (per-device). + * + * XXX: eventually we need to communicate this to the guest and/or make it + * tunable by the guest. If we get more outstanding requests at a time + * than this we will get EAGAIN from io_submit which is communicated to + * the guest as an I/O error. + */ +#define MAX_EVENTS 128 + +#define MAX_QUEUED_IO 128 + +struct qemu_laiocb { + BlockAIOCB common; + struct qemu_laio_state *ctx; + struct iocb iocb; + ssize_t ret; + size_t nbytes; + QEMUIOVector *qiov; + bool is_read; + QSIMPLEQ_ENTRY(qemu_laiocb) next; +}; + +typedef struct { + int plugged; + unsigned int n; + bool blocked; + QSIMPLEQ_HEAD(, qemu_laiocb) pending; +} LaioQueue; + +struct qemu_laio_state { + io_context_t ctx; + EventNotifier e; + + /* io queue for submit at batch */ + LaioQueue io_q; + + /* I/O completion processing */ + QEMUBH *completion_bh; + struct io_event events[MAX_EVENTS]; + int event_idx; + int event_max; +}; + +static void ioq_submit(struct qemu_laio_state *s); + +static inline ssize_t io_event_ret(struct io_event *ev) +{ + return (ssize_t)(((uint64_t)ev->res2 << 32) | ev->res); +} + +/* + * Completes an AIO request (calls the callback and frees the ACB). + */ +static void qemu_laio_process_completion(struct qemu_laio_state *s, + struct qemu_laiocb *laiocb) +{ + int ret; + + ret = laiocb->ret; + if (ret != -ECANCELED) { + if (ret == laiocb->nbytes) { + ret = 0; + } else if (ret >= 0) { + /* Short reads mean EOF, pad with zeros. */ + if (laiocb->is_read) { + qemu_iovec_memset(laiocb->qiov, ret, 0, + laiocb->qiov->size - ret); + } else { + ret = -EINVAL; + } + } + } + laiocb->common.cb(laiocb->common.opaque, ret); + + qemu_aio_unref(laiocb); +} + +/* The completion BH fetches completed I/O requests and invokes their + * callbacks. + * + * The function is somewhat tricky because it supports nested event loops, for + * example when a request callback invokes aio_poll(). In order to do this, + * the completion events array and index are kept in qemu_laio_state. The BH + * reschedules itself as long as there are completions pending so it will + * either be called again in a nested event loop or will be called after all + * events have been completed. When there are no events left to complete, the + * BH returns without rescheduling. + */ +static void qemu_laio_completion_bh(void *opaque) +{ + struct qemu_laio_state *s = opaque; + + /* Fetch more completion events when empty */ + if (s->event_idx == s->event_max) { + do { + struct timespec ts = { 0 }; + s->event_max = io_getevents(s->ctx, MAX_EVENTS, MAX_EVENTS, + s->events, &ts); + } while (s->event_max == -EINTR); + + s->event_idx = 0; + if (s->event_max <= 0) { + s->event_max = 0; + return; /* no more events */ + } + } + + /* Reschedule so nested event loops see currently pending completions */ + qemu_bh_schedule(s->completion_bh); + + /* Process completion events */ + while (s->event_idx < s->event_max) { + struct iocb *iocb = s->events[s->event_idx].obj; + struct qemu_laiocb *laiocb = + container_of(iocb, struct qemu_laiocb, iocb); + + laiocb->ret = io_event_ret(&s->events[s->event_idx]); + s->event_idx++; + + qemu_laio_process_completion(s, laiocb); + } + + if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { + ioq_submit(s); + } +} + +static void qemu_laio_completion_cb(EventNotifier *e) +{ + struct qemu_laio_state *s = container_of(e, struct qemu_laio_state, e); + + if (event_notifier_test_and_clear(&s->e)) { + qemu_bh_schedule(s->completion_bh); + } +} + +static void laio_cancel(BlockAIOCB *blockacb) +{ + struct qemu_laiocb *laiocb = (struct qemu_laiocb *)blockacb; + struct io_event event; + int ret; + + if (laiocb->ret != -EINPROGRESS) { + return; + } + ret = io_cancel(laiocb->ctx->ctx, &laiocb->iocb, &event); + laiocb->ret = -ECANCELED; + if (ret != 0) { + /* iocb is not cancelled, cb will be called by the event loop later */ + return; + } + + laiocb->common.cb(laiocb->common.opaque, laiocb->ret); +} + +static const AIOCBInfo laio_aiocb_info = { + .aiocb_size = sizeof(struct qemu_laiocb), + .cancel_async = laio_cancel, +}; + +static void ioq_init(LaioQueue *io_q) +{ + QSIMPLEQ_INIT(&io_q->pending); + io_q->plugged = 0; + io_q->n = 0; + io_q->blocked = false; +} + +static void ioq_submit(struct qemu_laio_state *s) +{ + int ret, len; + struct qemu_laiocb *aiocb; + struct iocb *iocbs[MAX_QUEUED_IO]; + QSIMPLEQ_HEAD(, qemu_laiocb) completed; + + do { + len = 0; + QSIMPLEQ_FOREACH(aiocb, &s->io_q.pending, next) { + iocbs[len++] = &aiocb->iocb; + if (len == MAX_QUEUED_IO) { + break; + } + } + + ret = io_submit(s->ctx, len, iocbs); + if (ret == -EAGAIN) { + break; + } + if (ret < 0) { + abort(); + } + + s->io_q.n -= ret; + aiocb = container_of(iocbs[ret - 1], struct qemu_laiocb, iocb); + QSIMPLEQ_SPLIT_AFTER(&s->io_q.pending, aiocb, next, &completed); + } while (ret == len && !QSIMPLEQ_EMPTY(&s->io_q.pending)); + s->io_q.blocked = (s->io_q.n > 0); +} + +void laio_io_plug(BlockDriverState *bs, void *aio_ctx) +{ + struct qemu_laio_state *s = aio_ctx; + + s->io_q.plugged++; +} + +void laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug) +{ + struct qemu_laio_state *s = aio_ctx; + + assert(s->io_q.plugged > 0 || !unplug); + + if (unplug && --s->io_q.plugged > 0) { + return; + } + + if (!s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { + ioq_submit(s); + } +} + +BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, int type) +{ + struct qemu_laio_state *s = aio_ctx; + struct qemu_laiocb *laiocb; + struct iocb *iocbs; + off_t offset = sector_num * 512; + + laiocb = qemu_aio_get(&laio_aiocb_info, bs, cb, opaque); + laiocb->nbytes = nb_sectors * 512; + laiocb->ctx = s; + laiocb->ret = -EINPROGRESS; + laiocb->is_read = (type == QEMU_AIO_READ); + laiocb->qiov = qiov; + + iocbs = &laiocb->iocb; + + switch (type) { + case QEMU_AIO_WRITE: + io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); + break; + case QEMU_AIO_READ: + io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); + break; + /* Currently Linux kernel does not support other operations */ + default: + fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", + __func__, type); + goto out_free_aiocb; + } + io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e)); + + QSIMPLEQ_INSERT_TAIL(&s->io_q.pending, laiocb, next); + s->io_q.n++; + if (!s->io_q.blocked && + (!s->io_q.plugged || s->io_q.n >= MAX_QUEUED_IO)) { + ioq_submit(s); + } + return &laiocb->common; + +out_free_aiocb: + qemu_aio_unref(laiocb); + return NULL; +} + +void laio_detach_aio_context(void *s_, AioContext *old_context) +{ + struct qemu_laio_state *s = s_; + + aio_set_event_notifier(old_context, &s->e, NULL); + qemu_bh_delete(s->completion_bh); +} + +void laio_attach_aio_context(void *s_, AioContext *new_context) +{ + struct qemu_laio_state *s = s_; + + s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s); + aio_set_event_notifier(new_context, &s->e, qemu_laio_completion_cb); +} + +void *laio_init(void) +{ + struct qemu_laio_state *s; + + s = g_malloc0(sizeof(*s)); + if (event_notifier_init(&s->e, false) < 0) { + goto out_free_state; + } + + if (io_setup(MAX_EVENTS, &s->ctx) != 0) { + goto out_close_efd; + } + + ioq_init(&s->io_q); + + return s; + +out_close_efd: + event_notifier_cleanup(&s->e); +out_free_state: + g_free(s); + return NULL; +} + +void laio_cleanup(void *s_) +{ + struct qemu_laio_state *s = s_; + + event_notifier_cleanup(&s->e); + + if (io_destroy(s->ctx) != 0) { + fprintf(stderr, "%s: destroy AIO context %p failed\n", + __func__, &s->ctx); + } + g_free(s); +} diff --git a/block/mirror.c b/block/mirror.c new file mode 100644 index 0000000..58f391a --- /dev/null +++ b/block/mirror.c @@ -0,0 +1,782 @@ +/* + * Image mirroring + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "trace.h" +#include "block/blockjob.h" +#include "block/block_int.h" +#include "qemu/ratelimit.h" +#include "qemu/bitmap.h" + +#define SLICE_TIME 100000000ULL /* ns */ +#define MAX_IN_FLIGHT 16 + +/* The mirroring buffer is a list of granularity-sized chunks. + * Free chunks are organized in a list. + */ +typedef struct MirrorBuffer { + QSIMPLEQ_ENTRY(MirrorBuffer) next; +} MirrorBuffer; + +typedef struct MirrorBlockJob { + BlockJob common; + RateLimit limit; + BlockDriverState *target; + BlockDriverState *base; + /* The name of the graph node to replace */ + char *replaces; + /* The BDS to replace */ + BlockDriverState *to_replace; + /* Used to block operations on the drive-mirror-replace target */ + Error *replace_blocker; + bool is_none_mode; + BlockdevOnError on_source_error, on_target_error; + bool synced; + bool should_complete; + int64_t sector_num; + int64_t granularity; + size_t buf_size; + int64_t bdev_length; + unsigned long *cow_bitmap; + BdrvDirtyBitmap *dirty_bitmap; + HBitmapIter hbi; + uint8_t *buf; + QSIMPLEQ_HEAD(, MirrorBuffer) buf_free; + int buf_free_count; + + unsigned long *in_flight_bitmap; + int in_flight; + int sectors_in_flight; + int ret; +} MirrorBlockJob; + +typedef struct MirrorOp { + MirrorBlockJob *s; + QEMUIOVector qiov; + int64_t sector_num; + int nb_sectors; +} MirrorOp; + +static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, + int error) +{ + s->synced = false; + if (read) { + return block_job_error_action(&s->common, s->common.bs, + s->on_source_error, true, error); + } else { + return block_job_error_action(&s->common, s->target, + s->on_target_error, false, error); + } +} + +static void mirror_iteration_done(MirrorOp *op, int ret) +{ + MirrorBlockJob *s = op->s; + struct iovec *iov; + int64_t chunk_num; + int i, nb_chunks, sectors_per_chunk; + + trace_mirror_iteration_done(s, op->sector_num, op->nb_sectors, ret); + + s->in_flight--; + s->sectors_in_flight -= op->nb_sectors; + iov = op->qiov.iov; + for (i = 0; i < op->qiov.niov; i++) { + MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base; + QSIMPLEQ_INSERT_TAIL(&s->buf_free, buf, next); + s->buf_free_count++; + } + + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + chunk_num = op->sector_num / sectors_per_chunk; + nb_chunks = op->nb_sectors / sectors_per_chunk; + bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); + if (ret >= 0) { + if (s->cow_bitmap) { + bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); + } + s->common.offset += (uint64_t)op->nb_sectors * BDRV_SECTOR_SIZE; + } + + qemu_iovec_destroy(&op->qiov); + g_slice_free(MirrorOp, op); + + /* Enter coroutine when it is not sleeping. The coroutine sleeps to + * rate-limit itself. The coroutine will eventually resume since there is + * a sleep timeout so don't wake it early. + */ + if (s->common.busy) { + qemu_coroutine_enter(s->common.co, NULL); + } +} + +static void mirror_write_complete(void *opaque, int ret) +{ + MirrorOp *op = opaque; + MirrorBlockJob *s = op->s; + if (ret < 0) { + BlockErrorAction action; + + bdrv_set_dirty_bitmap(s->dirty_bitmap, op->sector_num, op->nb_sectors); + action = mirror_error_action(s, false, -ret); + if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { + s->ret = ret; + } + } + mirror_iteration_done(op, ret); +} + +static void mirror_read_complete(void *opaque, int ret) +{ + MirrorOp *op = opaque; + MirrorBlockJob *s = op->s; + if (ret < 0) { + BlockErrorAction action; + + bdrv_set_dirty_bitmap(s->dirty_bitmap, op->sector_num, op->nb_sectors); + action = mirror_error_action(s, true, -ret); + if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { + s->ret = ret; + } + + mirror_iteration_done(op, ret); + return; + } + bdrv_aio_writev(s->target, op->sector_num, &op->qiov, op->nb_sectors, + mirror_write_complete, op); +} + +static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) +{ + BlockDriverState *source = s->common.bs; + int nb_sectors, sectors_per_chunk, nb_chunks; + int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector; + uint64_t delay_ns = 0; + MirrorOp *op; + + s->sector_num = hbitmap_iter_next(&s->hbi); + if (s->sector_num < 0) { + bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); + s->sector_num = hbitmap_iter_next(&s->hbi); + trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); + assert(s->sector_num >= 0); + } + + hbitmap_next_sector = s->sector_num; + sector_num = s->sector_num; + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + end = s->bdev_length / BDRV_SECTOR_SIZE; + + /* Extend the QEMUIOVector to include all adjacent blocks that will + * be copied in this operation. + * + * We have to do this if we have no backing file yet in the destination, + * and the cluster size is very large. Then we need to do COW ourselves. + * The first time a cluster is copied, copy it entirely. Note that, + * because both the granularity and the cluster size are powers of two, + * the number of sectors to copy cannot exceed one cluster. + * + * We also want to extend the QEMUIOVector to include more adjacent + * dirty blocks if possible, to limit the number of I/O operations and + * run efficiently even with a small granularity. + */ + nb_chunks = 0; + nb_sectors = 0; + next_sector = sector_num; + next_chunk = sector_num / sectors_per_chunk; + + /* Wait for I/O to this cluster (from a previous iteration) to be done. */ + while (test_bit(next_chunk, s->in_flight_bitmap)) { + trace_mirror_yield_in_flight(s, sector_num, s->in_flight); + qemu_coroutine_yield(); + } + + do { + int added_sectors, added_chunks; + + if (!bdrv_get_dirty(source, s->dirty_bitmap, next_sector) || + test_bit(next_chunk, s->in_flight_bitmap)) { + assert(nb_sectors > 0); + break; + } + + added_sectors = sectors_per_chunk; + if (s->cow_bitmap && !test_bit(next_chunk, s->cow_bitmap)) { + bdrv_round_to_clusters(s->target, + next_sector, added_sectors, + &next_sector, &added_sectors); + + /* On the first iteration, the rounding may make us copy + * sectors before the first dirty one. + */ + if (next_sector < sector_num) { + assert(nb_sectors == 0); + sector_num = next_sector; + next_chunk = next_sector / sectors_per_chunk; + } + } + + added_sectors = MIN(added_sectors, end - (sector_num + nb_sectors)); + added_chunks = (added_sectors + sectors_per_chunk - 1) / sectors_per_chunk; + + /* When doing COW, it may happen that there is not enough space for + * a full cluster. Wait if that is the case. + */ + while (nb_chunks == 0 && s->buf_free_count < added_chunks) { + trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight); + qemu_coroutine_yield(); + } + if (s->buf_free_count < nb_chunks + added_chunks) { + trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight); + break; + } + + /* We have enough free space to copy these sectors. */ + bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks); + + nb_sectors += added_sectors; + nb_chunks += added_chunks; + next_sector += added_sectors; + next_chunk += added_chunks; + if (!s->synced && s->common.speed) { + delay_ns = ratelimit_calculate_delay(&s->limit, added_sectors); + } + } while (delay_ns == 0 && next_sector < end); + + /* Allocate a MirrorOp that is used as an AIO callback. */ + op = g_slice_new(MirrorOp); + op->s = s; + op->sector_num = sector_num; + op->nb_sectors = nb_sectors; + + /* Now make a QEMUIOVector taking enough granularity-sized chunks + * from s->buf_free. + */ + qemu_iovec_init(&op->qiov, nb_chunks); + next_sector = sector_num; + while (nb_chunks-- > 0) { + MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); + size_t remaining = (nb_sectors * BDRV_SECTOR_SIZE) - op->qiov.size; + + QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); + s->buf_free_count--; + qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining)); + + /* Advance the HBitmapIter in parallel, so that we do not examine + * the same sector twice. + */ + if (next_sector > hbitmap_next_sector + && bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) { + hbitmap_next_sector = hbitmap_iter_next(&s->hbi); + } + + next_sector += sectors_per_chunk; + } + + bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num, nb_sectors); + + /* Copy the dirty cluster. */ + s->in_flight++; + s->sectors_in_flight += nb_sectors; + trace_mirror_one_iteration(s, sector_num, nb_sectors); + bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, + mirror_read_complete, op); + return delay_ns; +} + +static void mirror_free_init(MirrorBlockJob *s) +{ + int granularity = s->granularity; + size_t buf_size = s->buf_size; + uint8_t *buf = s->buf; + + assert(s->buf_free_count == 0); + QSIMPLEQ_INIT(&s->buf_free); + while (buf_size != 0) { + MirrorBuffer *cur = (MirrorBuffer *)buf; + QSIMPLEQ_INSERT_TAIL(&s->buf_free, cur, next); + s->buf_free_count++; + buf_size -= granularity; + buf += granularity; + } +} + +static void mirror_drain(MirrorBlockJob *s) +{ + while (s->in_flight > 0) { + qemu_coroutine_yield(); + } +} + +typedef struct { + int ret; +} MirrorExitData; + +static void mirror_exit(BlockJob *job, void *opaque) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorExitData *data = opaque; + AioContext *replace_aio_context = NULL; + + if (s->to_replace) { + replace_aio_context = bdrv_get_aio_context(s->to_replace); + aio_context_acquire(replace_aio_context); + } + + if (s->should_complete && data->ret == 0) { + BlockDriverState *to_replace = s->common.bs; + if (s->to_replace) { + to_replace = s->to_replace; + } + if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { + bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); + } + bdrv_swap(s->target, to_replace); + if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) { + /* drop the bs loop chain formed by the swap: break the loop then + * trigger the unref from the top one */ + BlockDriverState *p = s->base->backing_hd; + bdrv_set_backing_hd(s->base, NULL); + bdrv_unref(p); + } + } + if (s->to_replace) { + bdrv_op_unblock_all(s->to_replace, s->replace_blocker); + error_free(s->replace_blocker); + bdrv_unref(s->to_replace); + } + if (replace_aio_context) { + aio_context_release(replace_aio_context); + } + g_free(s->replaces); + bdrv_unref(s->target); + block_job_completed(&s->common, data->ret); + g_free(data); +} + +static void coroutine_fn mirror_run(void *opaque) +{ + MirrorBlockJob *s = opaque; + MirrorExitData *data; + BlockDriverState *bs = s->common.bs; + int64_t sector_num, end, sectors_per_chunk, length; + uint64_t last_pause_ns; + BlockDriverInfo bdi; + char backing_filename[2]; /* we only need 2 characters because we are only + checking for a NULL string */ + int ret = 0; + int n; + + if (block_job_is_cancelled(&s->common)) { + goto immediate_exit; + } + + s->bdev_length = bdrv_getlength(bs); + if (s->bdev_length < 0) { + ret = s->bdev_length; + goto immediate_exit; + } else if (s->bdev_length == 0) { + /* Report BLOCK_JOB_READY and wait for complete. */ + block_job_event_ready(&s->common); + s->synced = true; + while (!block_job_is_cancelled(&s->common) && !s->should_complete) { + block_job_yield(&s->common); + } + s->common.cancelled = false; + goto immediate_exit; + } + + length = DIV_ROUND_UP(s->bdev_length, s->granularity); + s->in_flight_bitmap = bitmap_new(length); + + /* If we have no backing file yet in the destination, we cannot let + * the destination do COW. Instead, we copy sectors around the + * dirty data if needed. We need a bitmap to do that. + */ + bdrv_get_backing_filename(s->target, backing_filename, + sizeof(backing_filename)); + if (backing_filename[0] && !s->target->backing_hd) { + ret = bdrv_get_info(s->target, &bdi); + if (ret < 0) { + goto immediate_exit; + } + if (s->granularity < bdi.cluster_size) { + s->buf_size = MAX(s->buf_size, bdi.cluster_size); + s->cow_bitmap = bitmap_new(length); + } + } + + end = s->bdev_length / BDRV_SECTOR_SIZE; + s->buf = qemu_try_blockalign(bs, s->buf_size); + if (s->buf == NULL) { + ret = -ENOMEM; + goto immediate_exit; + } + + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + mirror_free_init(s); + + if (!s->is_none_mode) { + /* First part, loop on the sectors and initialize the dirty bitmap. */ + BlockDriverState *base = s->base; + for (sector_num = 0; sector_num < end; ) { + int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1; + ret = bdrv_is_allocated_above(bs, base, + sector_num, next - sector_num, &n); + + if (ret < 0) { + goto immediate_exit; + } + + assert(n > 0); + if (ret == 1) { + bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n); + sector_num = next; + } else { + sector_num += n; + } + } + } + + bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); + last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + for (;;) { + uint64_t delay_ns = 0; + int64_t cnt; + bool should_complete; + + if (s->ret < 0) { + ret = s->ret; + goto immediate_exit; + } + + cnt = bdrv_get_dirty_count(s->dirty_bitmap); + /* s->common.offset contains the number of bytes already processed so + * far, cnt is the number of dirty sectors remaining and + * s->sectors_in_flight is the number of sectors currently being + * processed; together those are the current total operation length */ + s->common.len = s->common.offset + + (cnt + s->sectors_in_flight) * BDRV_SECTOR_SIZE; + + /* Note that even when no rate limit is applied we need to yield + * periodically with no pending I/O so that bdrv_drain_all() returns. + * We do so every SLICE_TIME nanoseconds, or when there is an error, + * or when the source is clean, whichever comes first. + */ + if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - last_pause_ns < SLICE_TIME && + s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 || + (cnt == 0 && s->in_flight > 0)) { + trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt); + qemu_coroutine_yield(); + continue; + } else if (cnt != 0) { + delay_ns = mirror_iteration(s); + } + } + + should_complete = false; + if (s->in_flight == 0 && cnt == 0) { + trace_mirror_before_flush(s); + ret = bdrv_flush(s->target); + if (ret < 0) { + if (mirror_error_action(s, false, -ret) == + BLOCK_ERROR_ACTION_REPORT) { + goto immediate_exit; + } + } else { + /* We're out of the streaming phase. From now on, if the job + * is cancelled we will actually complete all pending I/O and + * report completion. This way, block-job-cancel will leave + * the target in a consistent state. + */ + if (!s->synced) { + block_job_event_ready(&s->common); + s->synced = true; + } + + should_complete = s->should_complete || + block_job_is_cancelled(&s->common); + cnt = bdrv_get_dirty_count(s->dirty_bitmap); + } + } + + if (cnt == 0 && should_complete) { + /* The dirty bitmap is not updated while operations are pending. + * If we're about to exit, wait for pending operations before + * calling bdrv_get_dirty_count(bs), or we may exit while the + * source has dirty data to copy! + * + * Note that I/O can be submitted by the guest while + * mirror_populate runs. + */ + trace_mirror_before_drain(s, cnt); + bdrv_drain(bs); + cnt = bdrv_get_dirty_count(s->dirty_bitmap); + } + + ret = 0; + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); + if (!s->synced) { + block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); + if (block_job_is_cancelled(&s->common)) { + break; + } + } else if (!should_complete) { + delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); + block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); + } else if (cnt == 0) { + /* The two disks are in sync. Exit and report successful + * completion. + */ + assert(QLIST_EMPTY(&bs->tracked_requests)); + s->common.cancelled = false; + break; + } + last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + } + +immediate_exit: + if (s->in_flight > 0) { + /* We get here only if something went wrong. Either the job failed, + * or it was cancelled prematurely so that we do not guarantee that + * the target is a copy of the source. + */ + assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common))); + mirror_drain(s); + } + + assert(s->in_flight == 0); + qemu_vfree(s->buf); + g_free(s->cow_bitmap); + g_free(s->in_flight_bitmap); + bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); + bdrv_iostatus_disable(s->target); + + data = g_malloc(sizeof(*data)); + data->ret = ret; + block_job_defer_to_main_loop(&s->common, mirror_exit, data); +} + +static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + + if (speed < 0) { + error_set(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } + ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); +} + +static void mirror_iostatus_reset(BlockJob *job) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + + bdrv_iostatus_reset(s->target); +} + +static void mirror_complete(BlockJob *job, Error **errp) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + Error *local_err = NULL; + int ret; + + ret = bdrv_open_backing_file(s->target, NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return; + } + if (!s->synced) { + error_set(errp, QERR_BLOCK_JOB_NOT_READY, + bdrv_get_device_name(job->bs)); + return; + } + + /* check the target bs is not blocked and block all operations on it */ + if (s->replaces) { + AioContext *replace_aio_context; + + s->to_replace = check_to_replace_node(s->replaces, &local_err); + if (!s->to_replace) { + error_propagate(errp, local_err); + return; + } + + replace_aio_context = bdrv_get_aio_context(s->to_replace); + aio_context_acquire(replace_aio_context); + + error_setg(&s->replace_blocker, + "block device is in use by block-job-complete"); + bdrv_op_block_all(s->to_replace, s->replace_blocker); + bdrv_ref(s->to_replace); + + aio_context_release(replace_aio_context); + } + + s->should_complete = true; + block_job_enter(&s->common); +} + +static const BlockJobDriver mirror_job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = BLOCK_JOB_TYPE_MIRROR, + .set_speed = mirror_set_speed, + .iostatus_reset= mirror_iostatus_reset, + .complete = mirror_complete, +}; + +static const BlockJobDriver commit_active_job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = BLOCK_JOB_TYPE_COMMIT, + .set_speed = mirror_set_speed, + .iostatus_reset + = mirror_iostatus_reset, + .complete = mirror_complete, +}; + +static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, + const char *replaces, + int64_t speed, uint32_t granularity, + int64_t buf_size, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, + BlockCompletionFunc *cb, + void *opaque, Error **errp, + const BlockJobDriver *driver, + bool is_none_mode, BlockDriverState *base) +{ + MirrorBlockJob *s; + + if (granularity == 0) { + granularity = bdrv_get_default_bitmap_granularity(target); + } + + assert ((granularity & (granularity - 1)) == 0); + + if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || + on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && + !bdrv_iostatus_is_enabled(bs)) { + error_set(errp, QERR_INVALID_PARAMETER, "on-source-error"); + return; + } + + + s = block_job_create(driver, bs, speed, cb, opaque, errp); + if (!s) { + return; + } + + s->replaces = g_strdup(replaces); + s->on_source_error = on_source_error; + s->on_target_error = on_target_error; + s->target = target; + s->is_none_mode = is_none_mode; + s->base = base; + s->granularity = granularity; + s->buf_size = MAX(buf_size, granularity); + + s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); + if (!s->dirty_bitmap) { + return; + } + bdrv_set_enable_write_cache(s->target, true); + bdrv_set_on_error(s->target, on_target_error, on_target_error); + bdrv_iostatus_enable(s->target); + s->common.co = qemu_coroutine_create(mirror_run); + trace_mirror_start(bs, s, s->common.co, opaque); + qemu_coroutine_enter(s->common.co, s); +} + +void mirror_start(BlockDriverState *bs, BlockDriverState *target, + const char *replaces, + int64_t speed, uint32_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockdevOnError on_source_error, + BlockdevOnError on_target_error, + BlockCompletionFunc *cb, + void *opaque, Error **errp) +{ + bool is_none_mode; + BlockDriverState *base; + + if (mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) { + error_setg(errp, "Sync mode 'dirty-bitmap' not supported"); + return; + } + is_none_mode = mode == MIRROR_SYNC_MODE_NONE; + base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL; + mirror_start_job(bs, target, replaces, + speed, granularity, buf_size, + on_source_error, on_target_error, cb, opaque, errp, + &mirror_job_driver, is_none_mode, base); +} + +void commit_active_start(BlockDriverState *bs, BlockDriverState *base, + int64_t speed, + BlockdevOnError on_error, + BlockCompletionFunc *cb, + void *opaque, Error **errp) +{ + int64_t length, base_length; + int orig_base_flags; + int ret; + Error *local_err = NULL; + + orig_base_flags = bdrv_get_flags(base); + + if (bdrv_reopen(base, bs->open_flags, errp)) { + return; + } + + length = bdrv_getlength(bs); + if (length < 0) { + error_setg_errno(errp, -length, + "Unable to determine length of %s", bs->filename); + goto error_restore_flags; + } + + base_length = bdrv_getlength(base); + if (base_length < 0) { + error_setg_errno(errp, -base_length, + "Unable to determine length of %s", base->filename); + goto error_restore_flags; + } + + if (length > base_length) { + ret = bdrv_truncate(base, length); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Top image %s is larger than base image %s, and " + "resize of base image failed", + bs->filename, base->filename); + goto error_restore_flags; + } + } + + bdrv_ref(base); + mirror_start_job(bs, base, NULL, speed, 0, 0, + on_error, on_error, cb, opaque, &local_err, + &commit_active_job_driver, false, base); + if (local_err) { + error_propagate(errp, local_err); + goto error_restore_flags; + } + + return; + +error_restore_flags: + /* ignore error and errp for bdrv_reopen, because we want to propagate + * the original error */ + bdrv_reopen(base, orig_base_flags, NULL); + return; +} diff --git a/block/nbd-client.c b/block/nbd-client.c new file mode 100644 index 0000000..e1bb919 --- /dev/null +++ b/block/nbd-client.c @@ -0,0 +1,407 @@ +/* + * QEMU Block driver for NBD + * + * Copyright (C) 2008 Bull S.A.S. + * Author: Laurent Vivier + * + * Some parts: + * Copyright (C) 2007 Anthony Liguori + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "nbd-client.h" +#include "qemu/sockets.h" + +#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs)) +#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs)) + +static void nbd_recv_coroutines_enter_all(NbdClientSession *s) +{ + int i; + + for (i = 0; i < MAX_NBD_REQUESTS; i++) { + if (s->recv_coroutine[i]) { + qemu_coroutine_enter(s->recv_coroutine[i], NULL); + } + } +} + +static void nbd_teardown_connection(BlockDriverState *bs) +{ + NbdClientSession *client = nbd_get_client_session(bs); + + /* finish any pending coroutines */ + shutdown(client->sock, 2); + nbd_recv_coroutines_enter_all(client); + + nbd_client_detach_aio_context(bs); + closesocket(client->sock); + client->sock = -1; +} + +static void nbd_reply_ready(void *opaque) +{ + BlockDriverState *bs = opaque; + NbdClientSession *s = nbd_get_client_session(bs); + uint64_t i; + int ret; + + if (s->reply.handle == 0) { + /* No reply already in flight. Fetch a header. It is possible + * that another thread has done the same thing in parallel, so + * the socket is not readable anymore. + */ + ret = nbd_receive_reply(s->sock, &s->reply); + if (ret == -EAGAIN) { + return; + } + if (ret < 0) { + s->reply.handle = 0; + goto fail; + } + } + + /* There's no need for a mutex on the receive side, because the + * handler acts as a synchronization point and ensures that only + * one coroutine is called until the reply finishes. */ + i = HANDLE_TO_INDEX(s, s->reply.handle); + if (i >= MAX_NBD_REQUESTS) { + goto fail; + } + + if (s->recv_coroutine[i]) { + qemu_coroutine_enter(s->recv_coroutine[i], NULL); + return; + } + +fail: + nbd_teardown_connection(bs); +} + +static void nbd_restart_write(void *opaque) +{ + BlockDriverState *bs = opaque; + + qemu_coroutine_enter(nbd_get_client_session(bs)->send_coroutine, NULL); +} + +static int nbd_co_send_request(BlockDriverState *bs, + struct nbd_request *request, + QEMUIOVector *qiov, int offset) +{ + NbdClientSession *s = nbd_get_client_session(bs); + AioContext *aio_context; + int rc, ret, i; + + qemu_co_mutex_lock(&s->send_mutex); + + for (i = 0; i < MAX_NBD_REQUESTS; i++) { + if (s->recv_coroutine[i] == NULL) { + s->recv_coroutine[i] = qemu_coroutine_self(); + break; + } + } + + assert(i < MAX_NBD_REQUESTS); + request->handle = INDEX_TO_HANDLE(s, i); + s->send_coroutine = qemu_coroutine_self(); + aio_context = bdrv_get_aio_context(bs); + + aio_set_fd_handler(aio_context, s->sock, + nbd_reply_ready, nbd_restart_write, bs); + if (qiov) { + if (!s->is_unix) { + socket_set_cork(s->sock, 1); + } + rc = nbd_send_request(s->sock, request); + if (rc >= 0) { + ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov, + offset, request->len); + if (ret != request->len) { + rc = -EIO; + } + } + if (!s->is_unix) { + socket_set_cork(s->sock, 0); + } + } else { + rc = nbd_send_request(s->sock, request); + } + aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, bs); + s->send_coroutine = NULL; + qemu_co_mutex_unlock(&s->send_mutex); + return rc; +} + +static void nbd_co_receive_reply(NbdClientSession *s, + struct nbd_request *request, struct nbd_reply *reply, + QEMUIOVector *qiov, int offset) +{ + int ret; + + /* Wait until we're woken up by the read handler. TODO: perhaps + * peek at the next reply and avoid yielding if it's ours? */ + qemu_coroutine_yield(); + *reply = s->reply; + if (reply->handle != request->handle) { + reply->error = EIO; + } else { + if (qiov && reply->error == 0) { + ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov, + offset, request->len); + if (ret != request->len) { + reply->error = EIO; + } + } + + /* Tell the read handler to read another header. */ + s->reply.handle = 0; + } +} + +static void nbd_coroutine_start(NbdClientSession *s, + struct nbd_request *request) +{ + /* Poor man semaphore. The free_sema is locked when no other request + * can be accepted, and unlocked after receiving one reply. */ + if (s->in_flight >= MAX_NBD_REQUESTS - 1) { + qemu_co_mutex_lock(&s->free_sema); + assert(s->in_flight < MAX_NBD_REQUESTS); + } + s->in_flight++; + + /* s->recv_coroutine[i] is set as soon as we get the send_lock. */ +} + +static void nbd_coroutine_end(NbdClientSession *s, + struct nbd_request *request) +{ + int i = HANDLE_TO_INDEX(s, request->handle); + s->recv_coroutine[i] = NULL; + if (s->in_flight-- == MAX_NBD_REQUESTS) { + qemu_co_mutex_unlock(&s->free_sema); + } +} + +static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov, + int offset) +{ + NbdClientSession *client = nbd_get_client_session(bs); + struct nbd_request request = { .type = NBD_CMD_READ }; + struct nbd_reply reply; + ssize_t ret; + + request.from = sector_num * 512; + request.len = nb_sectors * 512; + + nbd_coroutine_start(client, &request); + ret = nbd_co_send_request(bs, &request, NULL, 0); + if (ret < 0) { + reply.error = -ret; + } else { + nbd_co_receive_reply(client, &request, &reply, qiov, offset); + } + nbd_coroutine_end(client, &request); + return -reply.error; + +} + +static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov, + int offset) +{ + NbdClientSession *client = nbd_get_client_session(bs); + struct nbd_request request = { .type = NBD_CMD_WRITE }; + struct nbd_reply reply; + ssize_t ret; + + if (!bdrv_enable_write_cache(bs) && + (client->nbdflags & NBD_FLAG_SEND_FUA)) { + request.type |= NBD_CMD_FLAG_FUA; + } + + request.from = sector_num * 512; + request.len = nb_sectors * 512; + + nbd_coroutine_start(client, &request); + ret = nbd_co_send_request(bs, &request, qiov, offset); + if (ret < 0) { + reply.error = -ret; + } else { + nbd_co_receive_reply(client, &request, &reply, NULL, 0); + } + nbd_coroutine_end(client, &request); + return -reply.error; +} + +/* qemu-nbd has a limit of slightly less than 1M per request. Try to + * remain aligned to 4K. */ +#define NBD_MAX_SECTORS 2040 + +int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + int offset = 0; + int ret; + while (nb_sectors > NBD_MAX_SECTORS) { + ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset); + if (ret < 0) { + return ret; + } + offset += NBD_MAX_SECTORS * 512; + sector_num += NBD_MAX_SECTORS; + nb_sectors -= NBD_MAX_SECTORS; + } + return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset); +} + +int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + int offset = 0; + int ret; + while (nb_sectors > NBD_MAX_SECTORS) { + ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset); + if (ret < 0) { + return ret; + } + offset += NBD_MAX_SECTORS * 512; + sector_num += NBD_MAX_SECTORS; + nb_sectors -= NBD_MAX_SECTORS; + } + return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset); +} + +int nbd_client_co_flush(BlockDriverState *bs) +{ + NbdClientSession *client = nbd_get_client_session(bs); + struct nbd_request request = { .type = NBD_CMD_FLUSH }; + struct nbd_reply reply; + ssize_t ret; + + if (!(client->nbdflags & NBD_FLAG_SEND_FLUSH)) { + return 0; + } + + if (client->nbdflags & NBD_FLAG_SEND_FUA) { + request.type |= NBD_CMD_FLAG_FUA; + } + + request.from = 0; + request.len = 0; + + nbd_coroutine_start(client, &request); + ret = nbd_co_send_request(bs, &request, NULL, 0); + if (ret < 0) { + reply.error = -ret; + } else { + nbd_co_receive_reply(client, &request, &reply, NULL, 0); + } + nbd_coroutine_end(client, &request); + return -reply.error; +} + +int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + NbdClientSession *client = nbd_get_client_session(bs); + struct nbd_request request = { .type = NBD_CMD_TRIM }; + struct nbd_reply reply; + ssize_t ret; + + if (!(client->nbdflags & NBD_FLAG_SEND_TRIM)) { + return 0; + } + request.from = sector_num * 512; + request.len = nb_sectors * 512; + + nbd_coroutine_start(client, &request); + ret = nbd_co_send_request(bs, &request, NULL, 0); + if (ret < 0) { + reply.error = -ret; + } else { + nbd_co_receive_reply(client, &request, &reply, NULL, 0); + } + nbd_coroutine_end(client, &request); + return -reply.error; + +} + +void nbd_client_detach_aio_context(BlockDriverState *bs) +{ + aio_set_fd_handler(bdrv_get_aio_context(bs), + nbd_get_client_session(bs)->sock, NULL, NULL, NULL); +} + +void nbd_client_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock, + nbd_reply_ready, NULL, bs); +} + +void nbd_client_close(BlockDriverState *bs) +{ + NbdClientSession *client = nbd_get_client_session(bs); + struct nbd_request request = { + .type = NBD_CMD_DISC, + .from = 0, + .len = 0 + }; + + if (client->sock == -1) { + return; + } + + nbd_send_request(client->sock, &request); + + nbd_teardown_connection(bs); +} + +int nbd_client_init(BlockDriverState *bs, int sock, const char *export, + Error **errp) +{ + NbdClientSession *client = nbd_get_client_session(bs); + int ret; + + /* NBD handshake */ + logout("session init %s\n", export); + qemu_set_block(sock); + ret = nbd_receive_negotiate(sock, export, + &client->nbdflags, &client->size, errp); + if (ret < 0) { + logout("Failed to negotiate with the NBD server\n"); + closesocket(sock); + return ret; + } + + qemu_co_mutex_init(&client->send_mutex); + qemu_co_mutex_init(&client->free_sema); + client->sock = sock; + + /* Now that we're connected, set the socket to be non-blocking and + * kick the reply mechanism. */ + qemu_set_nonblock(sock); + nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); + + logout("Established connection with NBD server\n"); + return 0; +} diff --git a/block/nbd-client.h b/block/nbd-client.h new file mode 100644 index 0000000..e841340 --- /dev/null +++ b/block/nbd-client.h @@ -0,0 +1,53 @@ +#ifndef NBD_CLIENT_H +#define NBD_CLIENT_H + +#include "qemu-common.h" +#include "block/nbd.h" +#include "block/block_int.h" + +/* #define DEBUG_NBD */ + +#if defined(DEBUG_NBD) +#define logout(fmt, ...) \ + fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__) +#else +#define logout(fmt, ...) ((void)0) +#endif + +#define MAX_NBD_REQUESTS 16 + +typedef struct NbdClientSession { + int sock; + uint32_t nbdflags; + off_t size; + + CoMutex send_mutex; + CoMutex free_sema; + Coroutine *send_coroutine; + int in_flight; + + Coroutine *recv_coroutine[MAX_NBD_REQUESTS]; + struct nbd_reply reply; + + bool is_unix; +} NbdClientSession; + +NbdClientSession *nbd_get_client_session(BlockDriverState *bs); + +int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name, + Error **errp); +void nbd_client_close(BlockDriverState *bs); + +int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors); +int nbd_client_co_flush(BlockDriverState *bs); +int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); +int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); + +void nbd_client_detach_aio_context(BlockDriverState *bs); +void nbd_client_attach_aio_context(BlockDriverState *bs, + AioContext *new_context); + +#endif /* NBD_CLIENT_H */ diff --git a/block/nbd.c b/block/nbd.c new file mode 100644 index 0000000..2176186 --- /dev/null +++ b/block/nbd.c @@ -0,0 +1,448 @@ +/* + * QEMU Block driver for NBD + * + * Copyright (C) 2008 Bull S.A.S. + * Author: Laurent Vivier + * + * Some parts: + * Copyright (C) 2007 Anthony Liguori + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block/nbd-client.h" +#include "qemu/uri.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "qemu/sockets.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qstring.h" + +#include +#include + +#define EN_OPTSTR ":exportname=" + +typedef struct BDRVNBDState { + NbdClientSession client; + QemuOpts *socket_opts; +} BDRVNBDState; + +static int nbd_parse_uri(const char *filename, QDict *options) +{ + URI *uri; + const char *p; + QueryParams *qp = NULL; + int ret = 0; + bool is_unix; + + uri = uri_parse(filename); + if (!uri) { + return -EINVAL; + } + + /* transport */ + if (!strcmp(uri->scheme, "nbd")) { + is_unix = false; + } else if (!strcmp(uri->scheme, "nbd+tcp")) { + is_unix = false; + } else if (!strcmp(uri->scheme, "nbd+unix")) { + is_unix = true; + } else { + ret = -EINVAL; + goto out; + } + + p = uri->path ? uri->path : "/"; + p += strspn(p, "/"); + if (p[0]) { + qdict_put(options, "export", qstring_from_str(p)); + } + + qp = query_params_parse(uri->query); + if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { + ret = -EINVAL; + goto out; + } + + if (is_unix) { + /* nbd+unix:///export?socket=path */ + if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { + ret = -EINVAL; + goto out; + } + qdict_put(options, "path", qstring_from_str(qp->p[0].value)); + } else { + QString *host; + /* nbd[+tcp]://host[:port]/export */ + if (!uri->server) { + ret = -EINVAL; + goto out; + } + + /* strip braces from literal IPv6 address */ + if (uri->server[0] == '[') { + host = qstring_from_substr(uri->server, 1, + strlen(uri->server) - 2); + } else { + host = qstring_from_str(uri->server); + } + + qdict_put(options, "host", host); + if (uri->port) { + char* port_str = g_strdup_printf("%d", uri->port); + qdict_put(options, "port", qstring_from_str(port_str)); + g_free(port_str); + } + } + +out: + if (qp) { + query_params_free(qp); + } + uri_free(uri); + return ret; +} + +static void nbd_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + char *file; + char *export_name; + const char *host_spec; + const char *unixpath; + + if (qdict_haskey(options, "host") + || qdict_haskey(options, "port") + || qdict_haskey(options, "path")) + { + error_setg(errp, "host/port/path and a file name may not be specified " + "at the same time"); + return; + } + + if (strstr(filename, "://")) { + int ret = nbd_parse_uri(filename, options); + if (ret < 0) { + error_setg(errp, "No valid URL specified"); + } + return; + } + + file = g_strdup(filename); + + export_name = strstr(file, EN_OPTSTR); + if (export_name) { + if (export_name[strlen(EN_OPTSTR)] == 0) { + goto out; + } + export_name[0] = 0; /* truncate 'file' */ + export_name += strlen(EN_OPTSTR); + + qdict_put(options, "export", qstring_from_str(export_name)); + } + + /* extract the host_spec - fail if it's not nbd:... */ + if (!strstart(file, "nbd:", &host_spec)) { + error_setg(errp, "File name string for NBD must start with 'nbd:'"); + goto out; + } + + if (!*host_spec) { + goto out; + } + + /* are we a UNIX or TCP socket? */ + if (strstart(host_spec, "unix:", &unixpath)) { + qdict_put(options, "path", qstring_from_str(unixpath)); + } else { + InetSocketAddress *addr = NULL; + + addr = inet_parse(host_spec, errp); + if (!addr) { + goto out; + } + + qdict_put(options, "host", qstring_from_str(addr->host)); + qdict_put(options, "port", qstring_from_str(addr->port)); + qapi_free_InetSocketAddress(addr); + } + +out: + g_free(file); +} + +static void nbd_config(BDRVNBDState *s, QDict *options, char **export, + Error **errp) +{ + Error *local_err = NULL; + + if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) { + if (qdict_haskey(options, "path")) { + error_setg(errp, "path and host may not be used at the same time."); + } else { + error_setg(errp, "one of path and host must be specified."); + } + return; + } + + s->client.is_unix = qdict_haskey(options, "path"); + s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0, + &error_abort); + + qemu_opts_absorb_qdict(s->socket_opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (!qemu_opt_get(s->socket_opts, "port")) { + qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT, + &error_abort); + } + + *export = g_strdup(qdict_get_try_str(options, "export")); + if (*export) { + qdict_del(options, "export"); + } +} + +NbdClientSession *nbd_get_client_session(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + return &s->client; +} + +static int nbd_establish_connection(BlockDriverState *bs, Error **errp) +{ + BDRVNBDState *s = bs->opaque; + int sock; + + if (s->client.is_unix) { + sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL); + } else { + sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL); + if (sock >= 0) { + socket_set_nodelay(sock); + } + } + + /* Failed to establish connection */ + if (sock < 0) { + logout("Failed to establish connection to NBD server\n"); + return -EIO; + } + + return sock; +} + +static int nbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVNBDState *s = bs->opaque; + char *export = NULL; + int result, sock; + Error *local_err = NULL; + + /* Pop the config into our state object. Exit if invalid. */ + nbd_config(s, options, &export, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + + /* establish TCP connection, return error if it fails + * TODO: Configurable retry-until-timeout behaviour. + */ + sock = nbd_establish_connection(bs, errp); + if (sock < 0) { + g_free(export); + return sock; + } + + /* NBD handshake */ + result = nbd_client_init(bs, sock, export, errp); + g_free(export); + return result; +} + +static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + return nbd_client_co_readv(bs, sector_num, nb_sectors, qiov); +} + +static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + return nbd_client_co_writev(bs, sector_num, nb_sectors, qiov); +} + +static int nbd_co_flush(BlockDriverState *bs) +{ + return nbd_client_co_flush(bs); +} + +static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl.max_discard = UINT32_MAX >> BDRV_SECTOR_BITS; + bs->bl.max_transfer_length = UINT32_MAX >> BDRV_SECTOR_BITS; +} + +static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + return nbd_client_co_discard(bs, sector_num, nb_sectors); +} + +static void nbd_close(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + + qemu_opts_del(s->socket_opts); + nbd_client_close(bs); +} + +static int64_t nbd_getlength(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + + return s->client.size; +} + +static void nbd_detach_aio_context(BlockDriverState *bs) +{ + nbd_client_detach_aio_context(bs); +} + +static void nbd_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + nbd_client_attach_aio_context(bs, new_context); +} + +static void nbd_refresh_filename(BlockDriverState *bs) +{ + QDict *opts = qdict_new(); + const char *path = qdict_get_try_str(bs->options, "path"); + const char *host = qdict_get_try_str(bs->options, "host"); + const char *port = qdict_get_try_str(bs->options, "port"); + const char *export = qdict_get_try_str(bs->options, "export"); + + qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); + + if (path && export) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "nbd+unix:///%s?socket=%s", export, path); + } else if (path && !export) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "nbd+unix://?socket=%s", path); + } else if (!path && export && port) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "nbd://%s:%s/%s", host, port, export); + } else if (!path && export && !port) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "nbd://%s/%s", host, export); + } else if (!path && !export && port) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "nbd://%s:%s", host, port); + } else if (!path && !export && !port) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "nbd://%s", host); + } + + if (path) { + qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(path))); + } else if (port) { + qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host))); + qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(port))); + } else { + qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host))); + } + if (export) { + qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export))); + } + + bs->full_open_options = opts; +} + +static BlockDriver bdrv_nbd = { + .format_name = "nbd", + .protocol_name = "nbd", + .instance_size = sizeof(BDRVNBDState), + .bdrv_parse_filename = nbd_parse_filename, + .bdrv_file_open = nbd_open, + .bdrv_co_readv = nbd_co_readv, + .bdrv_co_writev = nbd_co_writev, + .bdrv_close = nbd_close, + .bdrv_co_flush_to_os = nbd_co_flush, + .bdrv_co_discard = nbd_co_discard, + .bdrv_refresh_limits = nbd_refresh_limits, + .bdrv_getlength = nbd_getlength, + .bdrv_detach_aio_context = nbd_detach_aio_context, + .bdrv_attach_aio_context = nbd_attach_aio_context, + .bdrv_refresh_filename = nbd_refresh_filename, +}; + +static BlockDriver bdrv_nbd_tcp = { + .format_name = "nbd", + .protocol_name = "nbd+tcp", + .instance_size = sizeof(BDRVNBDState), + .bdrv_parse_filename = nbd_parse_filename, + .bdrv_file_open = nbd_open, + .bdrv_co_readv = nbd_co_readv, + .bdrv_co_writev = nbd_co_writev, + .bdrv_close = nbd_close, + .bdrv_co_flush_to_os = nbd_co_flush, + .bdrv_co_discard = nbd_co_discard, + .bdrv_refresh_limits = nbd_refresh_limits, + .bdrv_getlength = nbd_getlength, + .bdrv_detach_aio_context = nbd_detach_aio_context, + .bdrv_attach_aio_context = nbd_attach_aio_context, + .bdrv_refresh_filename = nbd_refresh_filename, +}; + +static BlockDriver bdrv_nbd_unix = { + .format_name = "nbd", + .protocol_name = "nbd+unix", + .instance_size = sizeof(BDRVNBDState), + .bdrv_parse_filename = nbd_parse_filename, + .bdrv_file_open = nbd_open, + .bdrv_co_readv = nbd_co_readv, + .bdrv_co_writev = nbd_co_writev, + .bdrv_close = nbd_close, + .bdrv_co_flush_to_os = nbd_co_flush, + .bdrv_co_discard = nbd_co_discard, + .bdrv_refresh_limits = nbd_refresh_limits, + .bdrv_getlength = nbd_getlength, + .bdrv_detach_aio_context = nbd_detach_aio_context, + .bdrv_attach_aio_context = nbd_attach_aio_context, + .bdrv_refresh_filename = nbd_refresh_filename, +}; + +static void bdrv_nbd_init(void) +{ + bdrv_register(&bdrv_nbd); + bdrv_register(&bdrv_nbd_tcp); + bdrv_register(&bdrv_nbd_unix); +} + +block_init(bdrv_nbd_init); diff --git a/block/nfs.c b/block/nfs.c new file mode 100644 index 0000000..ca9e24e --- /dev/null +++ b/block/nfs.c @@ -0,0 +1,509 @@ +/* + * QEMU Block driver for native access to files on NFS shares + * + * Copyright (c) 2014 Peter Lieven + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config-host.h" + +#include +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "block/block_int.h" +#include "trace.h" +#include "qemu/iov.h" +#include "qemu/uri.h" +#include "sysemu/sysemu.h" +#include + +typedef struct NFSClient { + struct nfs_context *context; + struct nfsfh *fh; + int events; + bool has_zero_init; + AioContext *aio_context; +} NFSClient; + +typedef struct NFSRPC { + int ret; + int complete; + QEMUIOVector *iov; + struct stat *st; + Coroutine *co; + QEMUBH *bh; + NFSClient *client; +} NFSRPC; + +static void nfs_process_read(void *arg); +static void nfs_process_write(void *arg); + +static void nfs_set_events(NFSClient *client) +{ + int ev = nfs_which_events(client->context); + if (ev != client->events) { + aio_set_fd_handler(client->aio_context, + nfs_get_fd(client->context), + (ev & POLLIN) ? nfs_process_read : NULL, + (ev & POLLOUT) ? nfs_process_write : NULL, + client); + + } + client->events = ev; +} + +static void nfs_process_read(void *arg) +{ + NFSClient *client = arg; + nfs_service(client->context, POLLIN); + nfs_set_events(client); +} + +static void nfs_process_write(void *arg) +{ + NFSClient *client = arg; + nfs_service(client->context, POLLOUT); + nfs_set_events(client); +} + +static void nfs_co_init_task(NFSClient *client, NFSRPC *task) +{ + *task = (NFSRPC) { + .co = qemu_coroutine_self(), + .client = client, + }; +} + +static void nfs_co_generic_bh_cb(void *opaque) +{ + NFSRPC *task = opaque; + task->complete = 1; + qemu_bh_delete(task->bh); + qemu_coroutine_enter(task->co, NULL); +} + +static void +nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, + void *private_data) +{ + NFSRPC *task = private_data; + task->ret = ret; + if (task->ret > 0 && task->iov) { + if (task->ret <= task->iov->size) { + qemu_iovec_from_buf(task->iov, 0, data, task->ret); + } else { + task->ret = -EIO; + } + } + if (task->ret == 0 && task->st) { + memcpy(task->st, data, sizeof(struct stat)); + } + if (task->ret < 0) { + error_report("NFS Error: %s", nfs_get_error(nfs)); + } + if (task->co) { + task->bh = aio_bh_new(task->client->aio_context, + nfs_co_generic_bh_cb, task); + qemu_bh_schedule(task->bh); + } else { + task->complete = 1; + } +} + +static int coroutine_fn nfs_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + + nfs_co_init_task(client, &task); + task.iov = iov; + + if (nfs_pread_async(client->context, client->fh, + sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, + nfs_co_generic_cb, &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + if (task.ret < 0) { + return task.ret; + } + + /* zero pad short reads */ + if (task.ret < iov->size) { + qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret); + } + + return 0; +} + +static int coroutine_fn nfs_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + char *buf = NULL; + + nfs_co_init_task(client, &task); + + buf = g_try_malloc(nb_sectors * BDRV_SECTOR_SIZE); + if (nb_sectors && buf == NULL) { + return -ENOMEM; + } + + qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE); + + if (nfs_pwrite_async(client->context, client->fh, + sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, + buf, nfs_co_generic_cb, &task) != 0) { + g_free(buf); + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + g_free(buf); + + if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) { + return task.ret < 0 ? task.ret : -EIO; + } + + return 0; +} + +static int coroutine_fn nfs_co_flush(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + + nfs_co_init_task(client, &task); + + if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, + &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + return task.ret; +} + +/* TODO Convert to fine grained options */ +static QemuOptsList runtime_opts = { + .name = "nfs", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "URL to the NFS file", + }, + { /* end of list */ } + }, +}; + +static void nfs_detach_aio_context(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + + aio_set_fd_handler(client->aio_context, + nfs_get_fd(client->context), + NULL, NULL, NULL); + client->events = 0; +} + +static void nfs_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + NFSClient *client = bs->opaque; + + client->aio_context = new_context; + nfs_set_events(client); +} + +static void nfs_client_close(NFSClient *client) +{ + if (client->context) { + if (client->fh) { + nfs_close(client->context, client->fh); + } + aio_set_fd_handler(client->aio_context, + nfs_get_fd(client->context), + NULL, NULL, NULL); + nfs_destroy_context(client->context); + } + memset(client, 0, sizeof(NFSClient)); +} + +static void nfs_file_close(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + nfs_client_close(client); +} + +static int64_t nfs_client_open(NFSClient *client, const char *filename, + int flags, Error **errp) +{ + int ret = -EINVAL, i; + struct stat st; + URI *uri; + QueryParams *qp = NULL; + char *file = NULL, *strp = NULL; + + uri = uri_parse(filename); + if (!uri) { + error_setg(errp, "Invalid URL specified"); + goto fail; + } + if (!uri->server) { + error_setg(errp, "Invalid URL specified"); + goto fail; + } + strp = strrchr(uri->path, '/'); + if (strp == NULL) { + error_setg(errp, "Invalid URL specified"); + goto fail; + } + file = g_strdup(strp); + *strp = 0; + + client->context = nfs_init_context(); + if (client->context == NULL) { + error_setg(errp, "Failed to init NFS context"); + goto fail; + } + + qp = query_params_parse(uri->query); + for (i = 0; i < qp->n; i++) { + unsigned long long val; + if (!qp->p[i].value) { + error_setg(errp, "Value for NFS parameter expected: %s", + qp->p[i].name); + goto fail; + } + if (parse_uint_full(qp->p[i].value, &val, 0)) { + error_setg(errp, "Illegal value for NFS parameter: %s", + qp->p[i].name); + goto fail; + } + if (!strcmp(qp->p[i].name, "uid")) { + nfs_set_uid(client->context, val); + } else if (!strcmp(qp->p[i].name, "gid")) { + nfs_set_gid(client->context, val); + } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) { + nfs_set_tcp_syncnt(client->context, val); +#ifdef LIBNFS_FEATURE_READAHEAD + } else if (!strcmp(qp->p[i].name, "readahead")) { + nfs_set_readahead(client->context, val); +#endif + } else { + error_setg(errp, "Unknown NFS parameter name: %s", + qp->p[i].name); + goto fail; + } + } + + ret = nfs_mount(client->context, uri->server, uri->path); + if (ret < 0) { + error_setg(errp, "Failed to mount nfs share: %s", + nfs_get_error(client->context)); + goto fail; + } + + if (flags & O_CREAT) { + ret = nfs_creat(client->context, file, 0600, &client->fh); + if (ret < 0) { + error_setg(errp, "Failed to create file: %s", + nfs_get_error(client->context)); + goto fail; + } + } else { + ret = nfs_open(client->context, file, flags, &client->fh); + if (ret < 0) { + error_setg(errp, "Failed to open file : %s", + nfs_get_error(client->context)); + goto fail; + } + } + + ret = nfs_fstat(client->context, client->fh, &st); + if (ret < 0) { + error_setg(errp, "Failed to fstat file: %s", + nfs_get_error(client->context)); + goto fail; + } + + ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); + client->has_zero_init = S_ISREG(st.st_mode); + goto out; +fail: + nfs_client_close(client); +out: + if (qp) { + query_params_free(qp); + } + uri_free(uri); + g_free(file); + return ret; +} + +static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { + NFSClient *client = bs->opaque; + int64_t ret; + QemuOpts *opts; + Error *local_err = NULL; + + client->aio_context = bdrv_get_aio_context(bs); + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + ret = nfs_client_open(client, qemu_opt_get(opts, "filename"), + (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, + errp); + if (ret < 0) { + goto out; + } + bs->total_sectors = ret; + ret = 0; +out: + qemu_opts_del(opts); + return ret; +} + +static QemuOptsList nfs_create_opts = { + .name = "nfs-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { /* end of list */ } + } +}; + +static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp) +{ + int ret = 0; + int64_t total_size = 0; + NFSClient *client = g_new0(NFSClient, 1); + + client->aio_context = qemu_get_aio_context(); + + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + + ret = nfs_client_open(client, url, O_CREAT, errp); + if (ret < 0) { + goto out; + } + ret = nfs_ftruncate(client->context, client->fh, total_size); + nfs_client_close(client); +out: + g_free(client); + return ret; +} + +static int nfs_has_zero_init(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + return client->has_zero_init; +} + +static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + NFSRPC task = {0}; + struct stat st; + + task.st = &st; + if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, + &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + aio_poll(client->aio_context, true); + } + + return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize); +} + +static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) +{ + NFSClient *client = bs->opaque; + return nfs_ftruncate(client->context, client->fh, offset); +} + +static BlockDriver bdrv_nfs = { + .format_name = "nfs", + .protocol_name = "nfs", + + .instance_size = sizeof(NFSClient), + .bdrv_needs_filename = true, + .create_opts = &nfs_create_opts, + + .bdrv_has_zero_init = nfs_has_zero_init, + .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, + .bdrv_truncate = nfs_file_truncate, + + .bdrv_file_open = nfs_file_open, + .bdrv_close = nfs_file_close, + .bdrv_create = nfs_file_create, + + .bdrv_co_readv = nfs_co_readv, + .bdrv_co_writev = nfs_co_writev, + .bdrv_co_flush_to_disk = nfs_co_flush, + + .bdrv_detach_aio_context = nfs_detach_aio_context, + .bdrv_attach_aio_context = nfs_attach_aio_context, +}; + +static void nfs_block_init(void) +{ + bdrv_register(&bdrv_nfs); +} + +block_init(nfs_block_init); diff --git a/block/null.c b/block/null.c new file mode 100644 index 0000000..7d08323 --- /dev/null +++ b/block/null.c @@ -0,0 +1,222 @@ +/* + * Null block driver + * + * Authors: + * Fam Zheng + * + * Copyright (C) 2014 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "block/block_int.h" + +#define NULL_OPT_LATENCY "latency-ns" + +typedef struct { + int64_t length; + int64_t latency_ns; +} BDRVNullState; + +static QemuOptsList runtime_opts = { + .name = "null", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "", + }, + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "size of the null block", + }, + { + .name = NULL_OPT_LATENCY, + .type = QEMU_OPT_NUMBER, + .help = "nanoseconds (approximated) to wait " + "before completing request", + }, + { /* end of list */ } + }, +}; + +static int null_file_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + QemuOpts *opts; + BDRVNullState *s = bs->opaque; + int ret = 0; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &error_abort); + s->length = + qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30); + s->latency_ns = + qemu_opt_get_number(opts, NULL_OPT_LATENCY, 0); + if (s->latency_ns < 0) { + error_setg(errp, "latency-ns is invalid"); + ret = -EINVAL; + } + qemu_opts_del(opts); + return ret; +} + +static void null_close(BlockDriverState *bs) +{ +} + +static int64_t null_getlength(BlockDriverState *bs) +{ + BDRVNullState *s = bs->opaque; + return s->length; +} + +static coroutine_fn int null_co_common(BlockDriverState *bs) +{ + BDRVNullState *s = bs->opaque; + + if (s->latency_ns) { + co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME, + s->latency_ns); + } + return 0; +} + +static coroutine_fn int null_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov) +{ + return null_co_common(bs); +} + +static coroutine_fn int null_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov) +{ + return null_co_common(bs); +} + +static coroutine_fn int null_co_flush(BlockDriverState *bs) +{ + return null_co_common(bs); +} + +typedef struct { + BlockAIOCB common; + QEMUBH *bh; + QEMUTimer timer; +} NullAIOCB; + +static const AIOCBInfo null_aiocb_info = { + .aiocb_size = sizeof(NullAIOCB), +}; + +static void null_bh_cb(void *opaque) +{ + NullAIOCB *acb = opaque; + acb->common.cb(acb->common.opaque, 0); + qemu_bh_delete(acb->bh); + qemu_aio_unref(acb); +} + +static void null_timer_cb(void *opaque) +{ + NullAIOCB *acb = opaque; + acb->common.cb(acb->common.opaque, 0); + timer_deinit(&acb->timer); + qemu_aio_unref(acb); +} + +static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, + BlockCompletionFunc *cb, + void *opaque) +{ + NullAIOCB *acb; + BDRVNullState *s = bs->opaque; + + acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque); + /* Only emulate latency after vcpu is running. */ + if (s->latency_ns) { + aio_timer_init(bdrv_get_aio_context(bs), &acb->timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + null_timer_cb, acb); + timer_mod_ns(&acb->timer, + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns); + } else { + acb->bh = aio_bh_new(bdrv_get_aio_context(bs), null_bh_cb, acb); + qemu_bh_schedule(acb->bh); + } + return &acb->common; +} + +static BlockAIOCB *null_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + return null_aio_common(bs, cb, opaque); +} + +static BlockAIOCB *null_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + return null_aio_common(bs, cb, opaque); +} + +static BlockAIOCB *null_aio_flush(BlockDriverState *bs, + BlockCompletionFunc *cb, + void *opaque) +{ + return null_aio_common(bs, cb, opaque); +} + +static int null_reopen_prepare(BDRVReopenState *reopen_state, + BlockReopenQueue *queue, Error **errp) +{ + return 0; +} + +static BlockDriver bdrv_null_co = { + .format_name = "null-co", + .protocol_name = "null-co", + .instance_size = sizeof(BDRVNullState), + + .bdrv_file_open = null_file_open, + .bdrv_close = null_close, + .bdrv_getlength = null_getlength, + + .bdrv_co_readv = null_co_readv, + .bdrv_co_writev = null_co_writev, + .bdrv_co_flush_to_disk = null_co_flush, + .bdrv_reopen_prepare = null_reopen_prepare, +}; + +static BlockDriver bdrv_null_aio = { + .format_name = "null-aio", + .protocol_name = "null-aio", + .instance_size = sizeof(BDRVNullState), + + .bdrv_file_open = null_file_open, + .bdrv_close = null_close, + .bdrv_getlength = null_getlength, + + .bdrv_aio_readv = null_aio_readv, + .bdrv_aio_writev = null_aio_writev, + .bdrv_aio_flush = null_aio_flush, + .bdrv_reopen_prepare = null_reopen_prepare, +}; + +static void bdrv_null_init(void) +{ + bdrv_register(&bdrv_null_co); + bdrv_register(&bdrv_null_aio); +} + +block_init(bdrv_null_init); diff --git a/block/parallels.c b/block/parallels.c new file mode 100644 index 0000000..046b568 --- /dev/null +++ b/block/parallels.c @@ -0,0 +1,758 @@ +/* + * Block driver for Parallels disk image format + * + * Copyright (c) 2007 Alex Beregszaszi + * Copyright (c) 2015 Denis V. Lunev + * + * This code was originally based on comparing different disk images created + * by Parallels. Currently it is based on opened OpenVZ sources + * available at + * http://git.openvz.org/?p=ploop;a=summary + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "qemu/bitmap.h" +#include "qapi/util.h" + +/**************************************************************/ + +#define HEADER_MAGIC "WithoutFreeSpace" +#define HEADER_MAGIC2 "WithouFreSpacExt" +#define HEADER_VERSION 2 +#define HEADER_INUSE_MAGIC (0x746F6E59) + +#define DEFAULT_CLUSTER_SIZE 1048576 /* 1 MiB */ + + +// always little-endian +typedef struct ParallelsHeader { + char magic[16]; // "WithoutFreeSpace" + uint32_t version; + uint32_t heads; + uint32_t cylinders; + uint32_t tracks; + uint32_t bat_entries; + uint64_t nb_sectors; + uint32_t inuse; + uint32_t data_off; + char padding[12]; +} QEMU_PACKED ParallelsHeader; + + +typedef enum ParallelsPreallocMode { + PRL_PREALLOC_MODE_FALLOCATE = 0, + PRL_PREALLOC_MODE_TRUNCATE = 1, + PRL_PREALLOC_MODE_MAX = 2, +} ParallelsPreallocMode; + +static const char *prealloc_mode_lookup[] = { + "falloc", + "truncate", + NULL, +}; + + +typedef struct BDRVParallelsState { + /** Locking is conservative, the lock protects + * - image file extending (truncate, fallocate) + * - any access to block allocation table + */ + CoMutex lock; + + ParallelsHeader *header; + uint32_t header_size; + bool header_unclean; + + unsigned long *bat_dirty_bmap; + unsigned int bat_dirty_block; + + uint32_t *bat_bitmap; + unsigned int bat_size; + + int64_t data_end; + uint64_t prealloc_size; + ParallelsPreallocMode prealloc_mode; + + unsigned int tracks; + + unsigned int off_multiplier; +} BDRVParallelsState; + + +#define PARALLELS_OPT_PREALLOC_MODE "prealloc-mode" +#define PARALLELS_OPT_PREALLOC_SIZE "prealloc-size" + +static QemuOptsList parallels_runtime_opts = { + .name = "parallels", + .head = QTAILQ_HEAD_INITIALIZER(parallels_runtime_opts.head), + .desc = { + { + .name = PARALLELS_OPT_PREALLOC_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Preallocation size on image expansion", + .def_value_str = "128MiB", + }, + { + .name = PARALLELS_OPT_PREALLOC_MODE, + .type = QEMU_OPT_STRING, + .help = "Preallocation mode on image expansion " + "(allowed values: falloc, truncate)", + .def_value_str = "falloc", + }, + { /* end of list */ }, + }, +}; + + +static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx) +{ + return (uint64_t)le32_to_cpu(s->bat_bitmap[idx]) * s->off_multiplier; +} + +static uint32_t bat_entry_off(uint32_t idx) +{ + return sizeof(ParallelsHeader) + sizeof(uint32_t) * idx; +} + +static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num) +{ + uint32_t index, offset; + + index = sector_num / s->tracks; + offset = sector_num % s->tracks; + + /* not allocated */ + if ((index >= s->bat_size) || (s->bat_bitmap[index] == 0)) { + return -1; + } + return bat2sect(s, index) + offset; +} + +static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num, + int nb_sectors) +{ + int ret = s->tracks - sector_num % s->tracks; + return MIN(nb_sectors, ret); +} + +static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, + int nb_sectors, int *pnum) +{ + int64_t start_off = -2, prev_end_off = -2; + + *pnum = 0; + while (nb_sectors > 0 || start_off == -2) { + int64_t offset = seek_to_sector(s, sector_num); + int to_end; + + if (start_off == -2) { + start_off = offset; + prev_end_off = offset; + } else if (offset != prev_end_off) { + break; + } + + to_end = cluster_remainder(s, sector_num, nb_sectors); + nb_sectors -= to_end; + sector_num += to_end; + *pnum += to_end; + + if (offset > 0) { + prev_end_off += to_end; + } + } + return start_off; +} + +static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + BDRVParallelsState *s = bs->opaque; + uint32_t idx, to_allocate, i; + int64_t pos, space; + + pos = block_status(s, sector_num, nb_sectors, pnum); + if (pos > 0) { + return pos; + } + + idx = sector_num / s->tracks; + if (idx >= s->bat_size) { + return -EINVAL; + } + + to_allocate = (sector_num + *pnum + s->tracks - 1) / s->tracks - idx; + space = to_allocate * s->tracks; + if (s->data_end + space > bdrv_getlength(bs->file) >> BDRV_SECTOR_BITS) { + int ret; + space += s->prealloc_size; + if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { + ret = bdrv_write_zeroes(bs->file, s->data_end, space, 0); + } else { + ret = bdrv_truncate(bs->file, + (s->data_end + space) << BDRV_SECTOR_BITS); + } + if (ret < 0) { + return ret; + } + } + + for (i = 0; i < to_allocate; i++) { + s->bat_bitmap[idx + i] = cpu_to_le32(s->data_end / s->off_multiplier); + s->data_end += s->tracks; + bitmap_set(s->bat_dirty_bmap, + bat_entry_off(idx) / s->bat_dirty_block, 1); + } + + return bat2sect(s, idx) + sector_num % s->tracks; +} + + +static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) +{ + BDRVParallelsState *s = bs->opaque; + unsigned long size = DIV_ROUND_UP(s->header_size, s->bat_dirty_block); + unsigned long bit; + + qemu_co_mutex_lock(&s->lock); + + bit = find_first_bit(s->bat_dirty_bmap, size); + while (bit < size) { + uint32_t off = bit * s->bat_dirty_block; + uint32_t to_write = s->bat_dirty_block; + int ret; + + if (off + to_write > s->header_size) { + to_write = s->header_size - off; + } + ret = bdrv_pwrite(bs->file, off, (uint8_t *)s->header + off, to_write); + if (ret < 0) { + qemu_co_mutex_unlock(&s->lock); + return ret; + } + bit = find_next_bit(s->bat_dirty_bmap, size, bit + 1); + } + bitmap_zero(s->bat_dirty_bmap, size); + + qemu_co_mutex_unlock(&s->lock); + return 0; +} + + +static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + BDRVParallelsState *s = bs->opaque; + int64_t offset; + + qemu_co_mutex_lock(&s->lock); + offset = block_status(s, sector_num, nb_sectors, pnum); + qemu_co_mutex_unlock(&s->lock); + + if (offset < 0) { + return 0; + } + + return (offset << BDRV_SECTOR_BITS) | + BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; +} + +static coroutine_fn int parallels_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + BDRVParallelsState *s = bs->opaque; + uint64_t bytes_done = 0; + QEMUIOVector hd_qiov; + int ret = 0; + + qemu_iovec_init(&hd_qiov, qiov->niov); + + while (nb_sectors > 0) { + int64_t position; + int n, nbytes; + + qemu_co_mutex_lock(&s->lock); + position = allocate_clusters(bs, sector_num, nb_sectors, &n); + qemu_co_mutex_unlock(&s->lock); + if (position < 0) { + ret = (int)position; + break; + } + + nbytes = n << BDRV_SECTOR_BITS; + + qemu_iovec_reset(&hd_qiov); + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); + + ret = bdrv_co_writev(bs->file, position, n, &hd_qiov); + if (ret < 0) { + break; + } + + nb_sectors -= n; + sector_num += n; + bytes_done += nbytes; + } + + qemu_iovec_destroy(&hd_qiov); + return ret; +} + +static coroutine_fn int parallels_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + BDRVParallelsState *s = bs->opaque; + uint64_t bytes_done = 0; + QEMUIOVector hd_qiov; + int ret = 0; + + qemu_iovec_init(&hd_qiov, qiov->niov); + + while (nb_sectors > 0) { + int64_t position; + int n, nbytes; + + qemu_co_mutex_lock(&s->lock); + position = block_status(s, sector_num, nb_sectors, &n); + qemu_co_mutex_unlock(&s->lock); + + nbytes = n << BDRV_SECTOR_BITS; + + if (position < 0) { + qemu_iovec_memset(qiov, bytes_done, 0, nbytes); + } else { + qemu_iovec_reset(&hd_qiov); + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); + + ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); + if (ret < 0) { + break; + } + } + + nb_sectors -= n; + sector_num += n; + bytes_done += nbytes; + } + + qemu_iovec_destroy(&hd_qiov); + return ret; +} + + +static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + int64_t size, prev_off, high_off; + int ret; + uint32_t i; + bool flush_bat = false; + int cluster_size = s->tracks << BDRV_SECTOR_BITS; + + size = bdrv_getlength(bs->file); + if (size < 0) { + res->check_errors++; + return size; + } + + if (s->header_unclean) { + fprintf(stderr, "%s image was not closed correctly\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR"); + res->corruptions++; + if (fix & BDRV_FIX_ERRORS) { + /* parallels_close will do the job right */ + res->corruptions_fixed++; + s->header_unclean = false; + } + } + + res->bfi.total_clusters = s->bat_size; + res->bfi.compressed_clusters = 0; /* compression is not supported */ + + high_off = 0; + prev_off = 0; + for (i = 0; i < s->bat_size; i++) { + int64_t off = bat2sect(s, i) << BDRV_SECTOR_BITS; + if (off == 0) { + prev_off = 0; + continue; + } + + /* cluster outside the image */ + if (off > size) { + fprintf(stderr, "%s cluster %u is outside image\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); + res->corruptions++; + if (fix & BDRV_FIX_ERRORS) { + prev_off = 0; + s->bat_bitmap[i] = 0; + res->corruptions_fixed++; + flush_bat = true; + continue; + } + } + + res->bfi.allocated_clusters++; + if (off > high_off) { + high_off = off; + } + + if (prev_off != 0 && (prev_off + cluster_size) != off) { + res->bfi.fragmented_clusters++; + } + prev_off = off; + } + + if (flush_bat) { + ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size); + if (ret < 0) { + res->check_errors++; + return ret; + } + } + + res->image_end_offset = high_off + cluster_size; + if (size > res->image_end_offset) { + int64_t count; + count = DIV_ROUND_UP(size - res->image_end_offset, cluster_size); + fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n", + fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR", + size - res->image_end_offset); + res->leaks += count; + if (fix & BDRV_FIX_LEAKS) { + ret = bdrv_truncate(bs->file, res->image_end_offset); + if (ret < 0) { + res->check_errors++; + return ret; + } + res->leaks_fixed += count; + } + } + + return 0; +} + + +static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int64_t total_size, cl_size; + uint8_t tmp[BDRV_SECTOR_SIZE]; + Error *local_err = NULL; + BlockDriverState *file; + uint32_t bat_entries, bat_sectors; + ParallelsHeader header; + int ret; + + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, + DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE); + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return ret; + } + + file = NULL; + ret = bdrv_open(&file, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return ret; + } + ret = bdrv_truncate(file, 0); + if (ret < 0) { + goto exit; + } + + bat_entries = DIV_ROUND_UP(total_size, cl_size); + bat_sectors = DIV_ROUND_UP(bat_entry_off(bat_entries), cl_size); + bat_sectors = (bat_sectors * cl_size) >> BDRV_SECTOR_BITS; + + memset(&header, 0, sizeof(header)); + memcpy(header.magic, HEADER_MAGIC2, sizeof(header.magic)); + header.version = cpu_to_le32(HEADER_VERSION); + /* don't care much about geometry, it is not used on image level */ + header.heads = cpu_to_le32(16); + header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE / 16 / 32); + header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS); + header.bat_entries = cpu_to_le32(bat_entries); + header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE)); + header.data_off = cpu_to_le32(bat_sectors); + + /* write all the data */ + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp, &header, sizeof(header)); + + ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0); + if (ret < 0) { + goto exit; + } + ret = 0; + +done: + bdrv_unref(file); + return ret; + +exit: + error_setg_errno(errp, -ret, "Failed to create Parallels image"); + goto done; +} + + +static int parallels_probe(const uint8_t *buf, int buf_size, + const char *filename) +{ + const ParallelsHeader *ph = (const void *)buf; + + if (buf_size < sizeof(ParallelsHeader)) { + return 0; + } + + if ((!memcmp(ph->magic, HEADER_MAGIC, 16) || + !memcmp(ph->magic, HEADER_MAGIC2, 16)) && + (le32_to_cpu(ph->version) == HEADER_VERSION)) { + return 100; + } + + return 0; +} + +static int parallels_update_header(BlockDriverState *bs) +{ + BDRVParallelsState *s = bs->opaque; + unsigned size = MAX(bdrv_opt_mem_align(bs->file), sizeof(ParallelsHeader)); + + if (size > s->header_size) { + size = s->header_size; + } + return bdrv_pwrite_sync(bs->file, 0, s->header, size); +} + +static int parallels_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVParallelsState *s = bs->opaque; + ParallelsHeader ph; + int ret, size, i; + QemuOpts *opts = NULL; + Error *local_err = NULL; + char *buf; + + ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); + if (ret < 0) { + goto fail; + } + + bs->total_sectors = le64_to_cpu(ph.nb_sectors); + + if (le32_to_cpu(ph.version) != HEADER_VERSION) { + goto fail_format; + } + if (!memcmp(ph.magic, HEADER_MAGIC, 16)) { + s->off_multiplier = 1; + bs->total_sectors = 0xffffffff & bs->total_sectors; + } else if (!memcmp(ph.magic, HEADER_MAGIC2, 16)) { + s->off_multiplier = le32_to_cpu(ph.tracks); + } else { + goto fail_format; + } + + s->tracks = le32_to_cpu(ph.tracks); + if (s->tracks == 0) { + error_setg(errp, "Invalid image: Zero sectors per track"); + ret = -EINVAL; + goto fail; + } + if (s->tracks > INT32_MAX/513) { + error_setg(errp, "Invalid image: Too big cluster"); + ret = -EFBIG; + goto fail; + } + + s->bat_size = le32_to_cpu(ph.bat_entries); + if (s->bat_size > INT_MAX / sizeof(uint32_t)) { + error_setg(errp, "Catalog too large"); + ret = -EFBIG; + goto fail; + } + + size = bat_entry_off(s->bat_size); + s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file)); + s->header = qemu_try_blockalign(bs->file, s->header_size); + if (s->header == NULL) { + ret = -ENOMEM; + goto fail; + } + s->data_end = le32_to_cpu(ph.data_off); + if (s->data_end == 0) { + s->data_end = ROUND_UP(bat_entry_off(s->bat_size), BDRV_SECTOR_SIZE); + } + if (s->data_end < s->header_size) { + /* there is not enough unused space to fit to block align between BAT + and actual data. We can't avoid read-modify-write... */ + s->header_size = size; + } + + ret = bdrv_pread(bs->file, 0, s->header, s->header_size); + if (ret < 0) { + goto fail; + } + s->bat_bitmap = (uint32_t *)(s->header + 1); + + for (i = 0; i < s->bat_size; i++) { + int64_t off = bat2sect(s, i); + if (off >= s->data_end) { + s->data_end = off + s->tracks; + } + } + + if (le32_to_cpu(ph.inuse) == HEADER_INUSE_MAGIC) { + /* Image was not closed correctly. The check is mandatory */ + s->header_unclean = true; + if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { + error_setg(errp, "parallels: Image was not closed correctly; " + "cannot be opened read/write"); + ret = -EACCES; + goto fail; + } + } + + opts = qemu_opts_create(¶llels_runtime_opts, NULL, 0, &local_err); + if (local_err != NULL) { + goto fail_options; + } + + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err != NULL) { + goto fail_options; + } + + s->prealloc_size = + qemu_opt_get_size_del(opts, PARALLELS_OPT_PREALLOC_SIZE, 0); + s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS); + buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE); + s->prealloc_mode = qapi_enum_parse(prealloc_mode_lookup, buf, + PRL_PREALLOC_MODE_MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err); + g_free(buf); + if (local_err != NULL) { + goto fail_options; + } + if (!bdrv_has_zero_init(bs->file) || + bdrv_truncate(bs->file, bdrv_getlength(bs->file)) != 0) { + s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; + } + + if (flags & BDRV_O_RDWR) { + s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC); + ret = parallels_update_header(bs); + if (ret < 0) { + goto fail; + } + } + + s->bat_dirty_block = 4 * getpagesize(); + s->bat_dirty_bmap = + bitmap_new(DIV_ROUND_UP(s->header_size, s->bat_dirty_block)); + + qemu_co_mutex_init(&s->lock); + return 0; + +fail_format: + error_setg(errp, "Image not in Parallels format"); + ret = -EINVAL; +fail: + qemu_vfree(s->header); + return ret; + +fail_options: + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; +} + + +static void parallels_close(BlockDriverState *bs) +{ + BDRVParallelsState *s = bs->opaque; + + if (bs->open_flags & BDRV_O_RDWR) { + s->header->inuse = 0; + parallels_update_header(bs); + } + + if (bs->open_flags & BDRV_O_RDWR) { + bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS); + } + + g_free(s->bat_dirty_bmap); + qemu_vfree(s->header); +} + +static QemuOptsList parallels_create_opts = { + .name = "parallels-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size", + }, + { + .name = BLOCK_OPT_CLUSTER_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Parallels image cluster size", + .def_value_str = stringify(DEFAULT_CLUSTER_SIZE), + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_parallels = { + .format_name = "parallels", + .instance_size = sizeof(BDRVParallelsState), + .bdrv_probe = parallels_probe, + .bdrv_open = parallels_open, + .bdrv_close = parallels_close, + .bdrv_co_get_block_status = parallels_co_get_block_status, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_co_flush_to_os = parallels_co_flush_to_os, + .bdrv_co_readv = parallels_co_readv, + .bdrv_co_writev = parallels_co_writev, + + .bdrv_create = parallels_create, + .bdrv_check = parallels_check, + .create_opts = ¶llels_create_opts, +}; + +static void bdrv_parallels_init(void) +{ + bdrv_register(&bdrv_parallels); +} + +block_init(bdrv_parallels_init); diff --git a/block/qapi.c b/block/qapi.c new file mode 100644 index 0000000..18d2b95 --- /dev/null +++ b/block/qapi.c @@ -0,0 +1,666 @@ +/* + * Block layer qmp and info dump related functions + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block/qapi.h" +#include "block/block_int.h" +#include "block/write-threshold.h" +#include "qmp-commands.h" +#include "qapi-visit.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp/types.h" +#include "sysemu/block-backend.h" + +BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) +{ + ImageInfo **p_image_info; + BlockDriverState *bs0; + BlockDeviceInfo *info = g_malloc0(sizeof(*info)); + + info->file = g_strdup(bs->filename); + info->ro = bs->read_only; + info->drv = g_strdup(bs->drv->format_name); + info->encrypted = bs->encrypted; + info->encryption_key_missing = bdrv_key_required(bs); + + info->cache = g_new(BlockdevCacheInfo, 1); + *info->cache = (BlockdevCacheInfo) { + .writeback = bdrv_enable_write_cache(bs), + .direct = !!(bs->open_flags & BDRV_O_NOCACHE), + .no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH), + }; + + if (bs->node_name[0]) { + info->has_node_name = true; + info->node_name = g_strdup(bs->node_name); + } + + if (bs->backing_file[0]) { + info->has_backing_file = true; + info->backing_file = g_strdup(bs->backing_file); + } + + info->backing_file_depth = bdrv_get_backing_file_depth(bs); + info->detect_zeroes = bs->detect_zeroes; + + if (bs->io_limits_enabled) { + ThrottleConfig cfg; + throttle_get_config(&bs->throttle_state, &cfg); + info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; + info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; + info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; + + info->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg; + info->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg; + info->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg; + + info->has_bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; + info->bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; + info->has_bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; + info->bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; + info->has_bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; + info->bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; + + info->has_iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; + info->iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; + info->has_iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; + info->iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; + info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; + info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; + + info->has_iops_size = cfg.op_size; + info->iops_size = cfg.op_size; + } + + info->write_threshold = bdrv_write_threshold_get(bs); + + bs0 = bs; + p_image_info = &info->image; + while (1) { + Error *local_err = NULL; + bdrv_query_image_info(bs0, p_image_info, &local_err); + if (local_err) { + error_propagate(errp, local_err); + qapi_free_BlockDeviceInfo(info); + return NULL; + } + if (bs0->drv && bs0->backing_hd) { + bs0 = bs0->backing_hd; + (*p_image_info)->has_backing_image = true; + p_image_info = &((*p_image_info)->backing_image); + } else { + break; + } + } + + return info; +} + +/* + * Returns 0 on success, with *p_list either set to describe snapshot + * information, or NULL because there are no snapshots. Returns -errno on + * error, with *p_list untouched. + */ +int bdrv_query_snapshot_info_list(BlockDriverState *bs, + SnapshotInfoList **p_list, + Error **errp) +{ + int i, sn_count; + QEMUSnapshotInfo *sn_tab = NULL; + SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL; + SnapshotInfo *info; + + sn_count = bdrv_snapshot_list(bs, &sn_tab); + if (sn_count < 0) { + const char *dev = bdrv_get_device_name(bs); + switch (sn_count) { + case -ENOMEDIUM: + error_setg(errp, "Device '%s' is not inserted", dev); + break; + case -ENOTSUP: + error_setg(errp, + "Device '%s' does not support internal snapshots", + dev); + break; + default: + error_setg_errno(errp, -sn_count, + "Can't list snapshots of device '%s'", dev); + break; + } + return sn_count; + } + + for (i = 0; i < sn_count; i++) { + info = g_new0(SnapshotInfo, 1); + info->id = g_strdup(sn_tab[i].id_str); + info->name = g_strdup(sn_tab[i].name); + info->vm_state_size = sn_tab[i].vm_state_size; + info->date_sec = sn_tab[i].date_sec; + info->date_nsec = sn_tab[i].date_nsec; + info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; + info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; + + info_list = g_new0(SnapshotInfoList, 1); + info_list->value = info; + + /* XXX: waiting for the qapi to support qemu-queue.h types */ + if (!cur_item) { + head = cur_item = info_list; + } else { + cur_item->next = info_list; + cur_item = info_list; + } + + } + + g_free(sn_tab); + *p_list = head; + return 0; +} + +/** + * bdrv_query_image_info: + * @bs: block device to examine + * @p_info: location to store image information + * @errp: location to store error information + * + * Store "flat" image information in @p_info. + * + * "Flat" means it does *not* query backing image information, + * i.e. (*pinfo)->has_backing_image will be set to false and + * (*pinfo)->backing_image to NULL even when the image does in fact have + * a backing image. + * + * @p_info will be set only on success. On error, store error in @errp. + */ +void bdrv_query_image_info(BlockDriverState *bs, + ImageInfo **p_info, + Error **errp) +{ + int64_t size; + const char *backing_filename; + BlockDriverInfo bdi; + int ret; + Error *err = NULL; + ImageInfo *info; + + size = bdrv_getlength(bs); + if (size < 0) { + error_setg_errno(errp, -size, "Can't get size of device '%s'", + bdrv_get_device_name(bs)); + return; + } + + info = g_new0(ImageInfo, 1); + info->filename = g_strdup(bs->filename); + info->format = g_strdup(bdrv_get_format_name(bs)); + info->virtual_size = size; + info->actual_size = bdrv_get_allocated_file_size(bs); + info->has_actual_size = info->actual_size >= 0; + if (bdrv_is_encrypted(bs)) { + info->encrypted = true; + info->has_encrypted = true; + } + if (bdrv_get_info(bs, &bdi) >= 0) { + if (bdi.cluster_size != 0) { + info->cluster_size = bdi.cluster_size; + info->has_cluster_size = true; + } + info->dirty_flag = bdi.is_dirty; + info->has_dirty_flag = true; + } + info->format_specific = bdrv_get_specific_info(bs); + info->has_format_specific = info->format_specific != NULL; + + backing_filename = bs->backing_file; + if (backing_filename[0] != '\0') { + char *backing_filename2 = g_malloc0(PATH_MAX); + info->backing_filename = g_strdup(backing_filename); + info->has_backing_filename = true; + bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err); + if (err) { + error_propagate(errp, err); + qapi_free_ImageInfo(info); + g_free(backing_filename2); + return; + } + + if (strcmp(backing_filename, backing_filename2) != 0) { + info->full_backing_filename = + g_strdup(backing_filename2); + info->has_full_backing_filename = true; + } + + if (bs->backing_format[0]) { + info->backing_filename_format = g_strdup(bs->backing_format); + info->has_backing_filename_format = true; + } + g_free(backing_filename2); + } + + ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err); + switch (ret) { + case 0: + if (info->snapshots) { + info->has_snapshots = true; + } + break; + /* recoverable error */ + case -ENOMEDIUM: + case -ENOTSUP: + error_free(err); + break; + default: + error_propagate(errp, err); + qapi_free_ImageInfo(info); + return; + } + + *p_info = info; +} + +/* @p_info will be set only on success. */ +static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, + Error **errp) +{ + BlockInfo *info = g_malloc0(sizeof(*info)); + BlockDriverState *bs = blk_bs(blk); + info->device = g_strdup(blk_name(blk)); + info->type = g_strdup("unknown"); + info->locked = blk_dev_is_medium_locked(blk); + info->removable = blk_dev_has_removable_media(blk); + + if (blk_dev_has_removable_media(blk)) { + info->has_tray_open = true; + info->tray_open = blk_dev_is_tray_open(blk); + } + + if (bdrv_iostatus_is_enabled(bs)) { + info->has_io_status = true; + info->io_status = bs->iostatus; + } + + if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { + info->has_dirty_bitmaps = true; + info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); + } + + if (bs->drv) { + info->has_inserted = true; + info->inserted = bdrv_block_device_info(bs, errp); + if (info->inserted == NULL) { + goto err; + } + } + + *p_info = info; + return; + + err: + qapi_free_BlockInfo(info); +} + +static BlockStats *bdrv_query_stats(const BlockDriverState *bs, + bool query_backing) +{ + BlockStats *s; + + s = g_malloc0(sizeof(*s)); + + if (bdrv_get_device_name(bs)[0]) { + s->has_device = true; + s->device = g_strdup(bdrv_get_device_name(bs)); + } + + if (bdrv_get_node_name(bs)[0]) { + s->has_node_name = true; + s->node_name = g_strdup(bdrv_get_node_name(bs)); + } + + s->stats = g_malloc0(sizeof(*s->stats)); + s->stats->rd_bytes = bs->stats.nr_bytes[BLOCK_ACCT_READ]; + s->stats->wr_bytes = bs->stats.nr_bytes[BLOCK_ACCT_WRITE]; + s->stats->rd_operations = bs->stats.nr_ops[BLOCK_ACCT_READ]; + s->stats->wr_operations = bs->stats.nr_ops[BLOCK_ACCT_WRITE]; + s->stats->rd_merged = bs->stats.merged[BLOCK_ACCT_READ]; + s->stats->wr_merged = bs->stats.merged[BLOCK_ACCT_WRITE]; + s->stats->wr_highest_offset = + bs->stats.wr_highest_sector * BDRV_SECTOR_SIZE; + s->stats->flush_operations = bs->stats.nr_ops[BLOCK_ACCT_FLUSH]; + s->stats->wr_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_WRITE]; + s->stats->rd_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_READ]; + s->stats->flush_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_FLUSH]; + + if (bs->file) { + s->has_parent = true; + s->parent = bdrv_query_stats(bs->file, query_backing); + } + + if (query_backing && bs->backing_hd) { + s->has_backing = true; + s->backing = bdrv_query_stats(bs->backing_hd, query_backing); + } + + return s; +} + +BlockInfoList *qmp_query_block(Error **errp) +{ + BlockInfoList *head = NULL, **p_next = &head; + BlockBackend *blk; + Error *local_err = NULL; + + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + BlockInfoList *info = g_malloc0(sizeof(*info)); + bdrv_query_info(blk, &info->value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto err; + } + + *p_next = info; + p_next = &info->next; + } + + return head; + + err: + qapi_free_BlockInfoList(head); + return NULL; +} + +BlockStatsList *qmp_query_blockstats(bool has_query_nodes, + bool query_nodes, + Error **errp) +{ + BlockStatsList *head = NULL, **p_next = &head; + BlockDriverState *bs = NULL; + + /* Just to be safe if query_nodes is not always initialized */ + query_nodes = has_query_nodes && query_nodes; + + while ((bs = query_nodes ? bdrv_next_node(bs) : bdrv_next(bs))) { + BlockStatsList *info = g_malloc0(sizeof(*info)); + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + info->value = bdrv_query_stats(bs, !query_nodes); + aio_context_release(ctx); + + *p_next = info; + p_next = &info->next; + } + + return head; +} + +#define NB_SUFFIXES 4 + +static char *get_human_readable_size(char *buf, int buf_size, int64_t size) +{ + static const char suffixes[NB_SUFFIXES] = {'K', 'M', 'G', 'T'}; + int64_t base; + int i; + + if (size <= 999) { + snprintf(buf, buf_size, "%" PRId64, size); + } else { + base = 1024; + for (i = 0; i < NB_SUFFIXES; i++) { + if (size < (10 * base)) { + snprintf(buf, buf_size, "%0.1f%c", + (double)size / base, + suffixes[i]); + break; + } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { + snprintf(buf, buf_size, "%" PRId64 "%c", + ((size + (base >> 1)) / base), + suffixes[i]); + break; + } + base = base * 1024; + } + } + return buf; +} + +void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, + QEMUSnapshotInfo *sn) +{ + char buf1[128], date_buf[128], clock_buf[128]; + struct tm tm; + time_t ti; + int64_t secs; + + if (!sn) { + func_fprintf(f, + "%-10s%-20s%7s%20s%15s", + "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); + } else { + ti = sn->date_sec; + localtime_r(&ti, &tm); + strftime(date_buf, sizeof(date_buf), + "%Y-%m-%d %H:%M:%S", &tm); + secs = sn->vm_clock_nsec / 1000000000; + snprintf(clock_buf, sizeof(clock_buf), + "%02d:%02d:%02d.%03d", + (int)(secs / 3600), + (int)((secs / 60) % 60), + (int)(secs % 60), + (int)((sn->vm_clock_nsec / 1000000) % 1000)); + func_fprintf(f, + "%-10s%-20s%7s%20s%15s", + sn->id_str, sn->name, + get_human_readable_size(buf1, sizeof(buf1), + sn->vm_state_size), + date_buf, + clock_buf); + } +} + +static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, + QDict *dict); +static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation, + QList *list); + +static void dump_qobject(fprintf_function func_fprintf, void *f, + int comp_indent, QObject *obj) +{ + switch (qobject_type(obj)) { + case QTYPE_QINT: { + QInt *value = qobject_to_qint(obj); + func_fprintf(f, "%" PRId64, qint_get_int(value)); + break; + } + case QTYPE_QSTRING: { + QString *value = qobject_to_qstring(obj); + func_fprintf(f, "%s", qstring_get_str(value)); + break; + } + case QTYPE_QDICT: { + QDict *value = qobject_to_qdict(obj); + dump_qdict(func_fprintf, f, comp_indent, value); + break; + } + case QTYPE_QLIST: { + QList *value = qobject_to_qlist(obj); + dump_qlist(func_fprintf, f, comp_indent, value); + break; + } + case QTYPE_QFLOAT: { + QFloat *value = qobject_to_qfloat(obj); + func_fprintf(f, "%g", qfloat_get_double(value)); + break; + } + case QTYPE_QBOOL: { + QBool *value = qobject_to_qbool(obj); + func_fprintf(f, "%s", qbool_get_int(value) ? "true" : "false"); + break; + } + case QTYPE_QERROR: { + QString *value = qerror_human((QError *)obj); + func_fprintf(f, "%s", qstring_get_str(value)); + QDECREF(value); + break; + } + default: + abort(); + } +} + +static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation, + QList *list) +{ + const QListEntry *entry; + int i = 0; + + for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) { + qtype_code type = qobject_type(entry->value); + bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); + const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: "; + + func_fprintf(f, format, indentation * 4, "", i); + dump_qobject(func_fprintf, f, indentation + 1, entry->value); + if (!composite) { + func_fprintf(f, "\n"); + } + } +} + +static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, + QDict *dict) +{ + const QDictEntry *entry; + + for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) { + qtype_code type = qobject_type(entry->value); + bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); + const char *format = composite ? "%*s%s:\n" : "%*s%s: "; + char key[strlen(entry->key) + 1]; + int i; + + /* replace dashes with spaces in key (variable) names */ + for (i = 0; entry->key[i]; i++) { + key[i] = entry->key[i] == '-' ? ' ' : entry->key[i]; + } + key[i] = 0; + + func_fprintf(f, format, indentation * 4, "", key); + dump_qobject(func_fprintf, f, indentation + 1, entry->value); + if (!composite) { + func_fprintf(f, "\n"); + } + } +} + +void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, + ImageInfoSpecific *info_spec) +{ + QmpOutputVisitor *ov = qmp_output_visitor_new(); + QObject *obj, *data; + + visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL, + &error_abort); + obj = qmp_output_get_qobject(ov); + assert(qobject_type(obj) == QTYPE_QDICT); + data = qdict_get(qobject_to_qdict(obj), "data"); + dump_qobject(func_fprintf, f, 1, data); + qmp_output_visitor_cleanup(ov); +} + +void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, + ImageInfo *info) +{ + char size_buf[128], dsize_buf[128]; + if (!info->has_actual_size) { + snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); + } else { + get_human_readable_size(dsize_buf, sizeof(dsize_buf), + info->actual_size); + } + get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size); + func_fprintf(f, + "image: %s\n" + "file format: %s\n" + "virtual size: %s (%" PRId64 " bytes)\n" + "disk size: %s\n", + info->filename, info->format, size_buf, + info->virtual_size, + dsize_buf); + + if (info->has_encrypted && info->encrypted) { + func_fprintf(f, "encrypted: yes\n"); + } + + if (info->has_cluster_size) { + func_fprintf(f, "cluster_size: %" PRId64 "\n", + info->cluster_size); + } + + if (info->has_dirty_flag && info->dirty_flag) { + func_fprintf(f, "cleanly shut down: no\n"); + } + + if (info->has_backing_filename) { + func_fprintf(f, "backing file: %s", info->backing_filename); + if (info->has_full_backing_filename) { + func_fprintf(f, " (actual path: %s)", info->full_backing_filename); + } + func_fprintf(f, "\n"); + if (info->has_backing_filename_format) { + func_fprintf(f, "backing file format: %s\n", + info->backing_filename_format); + } + } + + if (info->has_snapshots) { + SnapshotInfoList *elem; + + func_fprintf(f, "Snapshot list:\n"); + bdrv_snapshot_dump(func_fprintf, f, NULL); + func_fprintf(f, "\n"); + + /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but + * we convert to the block layer's native QEMUSnapshotInfo for now. + */ + for (elem = info->snapshots; elem; elem = elem->next) { + QEMUSnapshotInfo sn = { + .vm_state_size = elem->value->vm_state_size, + .date_sec = elem->value->date_sec, + .date_nsec = elem->value->date_nsec, + .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL + + elem->value->vm_clock_nsec, + }; + + pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); + pstrcpy(sn.name, sizeof(sn.name), elem->value->name); + bdrv_snapshot_dump(func_fprintf, f, &sn); + func_fprintf(f, "\n"); + } + } + + if (info->has_format_specific) { + func_fprintf(f, "Format specific information:\n"); + bdrv_image_info_specific_dump(func_fprintf, f, info->format_specific); + } +} diff --git a/block/qcow.c b/block/qcow.c new file mode 100644 index 0000000..911e59f --- /dev/null +++ b/block/qcow.c @@ -0,0 +1,987 @@ +/* + * Block driver for the QCOW format + * + * Copyright (c) 2004-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include +#include "qemu/aes.h" +#include "migration/migration.h" + +/**************************************************************/ +/* QEMU COW block driver with compression and encryption support */ + +#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) +#define QCOW_VERSION 1 + +#define QCOW_CRYPT_NONE 0 +#define QCOW_CRYPT_AES 1 + +#define QCOW_OFLAG_COMPRESSED (1LL << 63) + +typedef struct QCowHeader { + uint32_t magic; + uint32_t version; + uint64_t backing_file_offset; + uint32_t backing_file_size; + uint32_t mtime; + uint64_t size; /* in bytes */ + uint8_t cluster_bits; + uint8_t l2_bits; + uint16_t padding; + uint32_t crypt_method; + uint64_t l1_table_offset; +} QEMU_PACKED QCowHeader; + +#define L2_CACHE_SIZE 16 + +typedef struct BDRVQcowState { + int cluster_bits; + int cluster_size; + int cluster_sectors; + int l2_bits; + int l2_size; + unsigned int l1_size; + uint64_t cluster_offset_mask; + uint64_t l1_table_offset; + uint64_t *l1_table; + uint64_t *l2_cache; + uint64_t l2_cache_offsets[L2_CACHE_SIZE]; + uint32_t l2_cache_counts[L2_CACHE_SIZE]; + uint8_t *cluster_cache; + uint8_t *cluster_data; + uint64_t cluster_cache_offset; + uint32_t crypt_method; /* current crypt method, 0 if no key yet */ + uint32_t crypt_method_header; + AES_KEY aes_encrypt_key; + AES_KEY aes_decrypt_key; + CoMutex lock; + Error *migration_blocker; +} BDRVQcowState; + +static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); + +static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const QCowHeader *cow_header = (const void *)buf; + + if (buf_size >= sizeof(QCowHeader) && + be32_to_cpu(cow_header->magic) == QCOW_MAGIC && + be32_to_cpu(cow_header->version) == QCOW_VERSION) + return 100; + else + return 0; +} + +static int qcow_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVQcowState *s = bs->opaque; + unsigned int len, i, shift; + int ret; + QCowHeader header; + + ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + if (ret < 0) { + goto fail; + } + be32_to_cpus(&header.magic); + be32_to_cpus(&header.version); + be64_to_cpus(&header.backing_file_offset); + be32_to_cpus(&header.backing_file_size); + be32_to_cpus(&header.mtime); + be64_to_cpus(&header.size); + be32_to_cpus(&header.crypt_method); + be64_to_cpus(&header.l1_table_offset); + + if (header.magic != QCOW_MAGIC) { + error_setg(errp, "Image not in qcow format"); + ret = -EINVAL; + goto fail; + } + if (header.version != QCOW_VERSION) { + char version[64]; + snprintf(version, sizeof(version), "QCOW version %" PRIu32, + header.version); + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bdrv_get_device_or_node_name(bs), "qcow", version); + ret = -ENOTSUP; + goto fail; + } + + if (header.size <= 1) { + error_setg(errp, "Image size is too small (must be at least 2 bytes)"); + ret = -EINVAL; + goto fail; + } + if (header.cluster_bits < 9 || header.cluster_bits > 16) { + error_setg(errp, "Cluster size must be between 512 and 64k"); + ret = -EINVAL; + goto fail; + } + + /* l2_bits specifies number of entries; storing a uint64_t in each entry, + * so bytes = num_entries << 3. */ + if (header.l2_bits < 9 - 3 || header.l2_bits > 16 - 3) { + error_setg(errp, "L2 table size must be between 512 and 64k"); + ret = -EINVAL; + goto fail; + } + + if (header.crypt_method > QCOW_CRYPT_AES) { + error_setg(errp, "invalid encryption method in qcow header"); + ret = -EINVAL; + goto fail; + } + s->crypt_method_header = header.crypt_method; + if (s->crypt_method_header) { + bs->encrypted = 1; + } + s->cluster_bits = header.cluster_bits; + s->cluster_size = 1 << s->cluster_bits; + s->cluster_sectors = 1 << (s->cluster_bits - 9); + s->l2_bits = header.l2_bits; + s->l2_size = 1 << s->l2_bits; + bs->total_sectors = header.size / 512; + s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1; + + /* read the level 1 table */ + shift = s->cluster_bits + s->l2_bits; + if (header.size > UINT64_MAX - (1LL << shift)) { + error_setg(errp, "Image too large"); + ret = -EINVAL; + goto fail; + } else { + uint64_t l1_size = (header.size + (1LL << shift) - 1) >> shift; + if (l1_size > INT_MAX / sizeof(uint64_t)) { + error_setg(errp, "Image too large"); + ret = -EINVAL; + goto fail; + } + s->l1_size = l1_size; + } + + s->l1_table_offset = header.l1_table_offset; + s->l1_table = g_try_new(uint64_t, s->l1_size); + if (s->l1_table == NULL) { + error_setg(errp, "Could not allocate memory for L1 table"); + ret = -ENOMEM; + goto fail; + } + + ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + for(i = 0;i < s->l1_size; i++) { + be64_to_cpus(&s->l1_table[i]); + } + + /* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */ + s->l2_cache = + qemu_try_blockalign(bs->file, + s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + if (s->l2_cache == NULL) { + error_setg(errp, "Could not allocate L2 table cache"); + ret = -ENOMEM; + goto fail; + } + s->cluster_cache = g_malloc(s->cluster_size); + s->cluster_data = g_malloc(s->cluster_size); + s->cluster_cache_offset = -1; + + /* read the backing file name */ + if (header.backing_file_offset != 0) { + len = header.backing_file_size; + if (len > 1023 || len >= sizeof(bs->backing_file)) { + error_setg(errp, "Backing file name too long"); + ret = -EINVAL; + goto fail; + } + ret = bdrv_pread(bs->file, header.backing_file_offset, + bs->backing_file, len); + if (ret < 0) { + goto fail; + } + bs->backing_file[len] = '\0'; + } + + /* Disable migration when qcow images are used */ + error_setg(&s->migration_blocker, "The qcow format used by node '%s' " + "does not support live migration", + bdrv_get_device_or_node_name(bs)); + migrate_add_blocker(s->migration_blocker); + + qemu_co_mutex_init(&s->lock); + return 0; + + fail: + g_free(s->l1_table); + qemu_vfree(s->l2_cache); + g_free(s->cluster_cache); + g_free(s->cluster_data); + return ret; +} + + +/* We have nothing to do for QCOW reopen, stubs just return + * success */ +static int qcow_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + return 0; +} + +static int qcow_set_key(BlockDriverState *bs, const char *key) +{ + BDRVQcowState *s = bs->opaque; + uint8_t keybuf[16]; + int len, i; + + memset(keybuf, 0, 16); + len = strlen(key); + if (len > 16) + len = 16; + /* XXX: we could compress the chars to 7 bits to increase + entropy */ + for(i = 0;i < len;i++) { + keybuf[i] = key[i]; + } + assert(bs->encrypted); + s->crypt_method = s->crypt_method_header; + + if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) + return -1; + if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) + return -1; + return 0; +} + +/* The crypt function is compatible with the linux cryptoloop + algorithm for < 4 GB images. NOTE: out_buf == in_buf is + supported */ +static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, + uint8_t *out_buf, const uint8_t *in_buf, + int nb_sectors, int enc, + const AES_KEY *key) +{ + union { + uint64_t ll[2]; + uint8_t b[16]; + } ivec; + int i; + + for(i = 0; i < nb_sectors; i++) { + ivec.ll[0] = cpu_to_le64(sector_num); + ivec.ll[1] = 0; + AES_cbc_encrypt(in_buf, out_buf, 512, key, + ivec.b, enc); + sector_num++; + in_buf += 512; + out_buf += 512; + } +} + +/* 'allocate' is: + * + * 0 to not allocate. + * + * 1 to allocate a normal cluster (for sector indexes 'n_start' to + * 'n_end') + * + * 2 to allocate a compressed cluster of size + * 'compressed_size'. 'compressed_size' must be > 0 and < + * cluster_size + * + * return 0 if not allocated. + */ +static uint64_t get_cluster_offset(BlockDriverState *bs, + uint64_t offset, int allocate, + int compressed_size, + int n_start, int n_end) +{ + BDRVQcowState *s = bs->opaque; + int min_index, i, j, l1_index, l2_index; + uint64_t l2_offset, *l2_table, cluster_offset, tmp; + uint32_t min_count; + int new_l2_table; + + l1_index = offset >> (s->l2_bits + s->cluster_bits); + l2_offset = s->l1_table[l1_index]; + new_l2_table = 0; + if (!l2_offset) { + if (!allocate) + return 0; + /* allocate a new l2 entry */ + l2_offset = bdrv_getlength(bs->file); + /* round to cluster size */ + l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); + /* update the L1 entry */ + s->l1_table[l1_index] = l2_offset; + tmp = cpu_to_be64(l2_offset); + if (bdrv_pwrite_sync(bs->file, + s->l1_table_offset + l1_index * sizeof(tmp), + &tmp, sizeof(tmp)) < 0) + return 0; + new_l2_table = 1; + } + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == s->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++s->l2_cache_counts[i] == 0xffffffff) { + for(j = 0; j < L2_CACHE_SIZE; j++) { + s->l2_cache_counts[j] >>= 1; + } + } + l2_table = s->l2_cache + (i << s->l2_bits); + goto found; + } + } + /* not found: load a new entry in the least used one */ + min_index = 0; + min_count = 0xffffffff; + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (s->l2_cache_counts[i] < min_count) { + min_count = s->l2_cache_counts[i]; + min_index = i; + } + } + l2_table = s->l2_cache + (min_index << s->l2_bits); + if (new_l2_table) { + memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); + if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, + s->l2_size * sizeof(uint64_t)) < 0) + return 0; + } else { + if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } + s->l2_cache_offsets[min_index] = l2_offset; + s->l2_cache_counts[min_index] = 1; + found: + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); + cluster_offset = be64_to_cpu(l2_table[l2_index]); + if (!cluster_offset || + ((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) { + if (!allocate) + return 0; + /* allocate a new cluster */ + if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && + (n_end - n_start) < s->cluster_sectors) { + /* if the cluster is already compressed, we must + decompress it in the case it is not completely + overwritten */ + if (decompress_cluster(bs, cluster_offset) < 0) + return 0; + cluster_offset = bdrv_getlength(bs->file); + cluster_offset = (cluster_offset + s->cluster_size - 1) & + ~(s->cluster_size - 1); + /* write the cluster content */ + if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) != + s->cluster_size) + return -1; + } else { + cluster_offset = bdrv_getlength(bs->file); + if (allocate == 1) { + /* round to cluster size */ + cluster_offset = (cluster_offset + s->cluster_size - 1) & + ~(s->cluster_size - 1); + bdrv_truncate(bs->file, cluster_offset + s->cluster_size); + /* if encrypted, we must initialize the cluster + content which won't be written */ + if (bs->encrypted && + (n_end - n_start) < s->cluster_sectors) { + uint64_t start_sect; + assert(s->crypt_method); + start_sect = (offset & ~(s->cluster_size - 1)) >> 9; + memset(s->cluster_data + 512, 0x00, 512); + for(i = 0; i < s->cluster_sectors; i++) { + if (i < n_start || i >= n_end) { + encrypt_sectors(s, start_sect + i, + s->cluster_data, + s->cluster_data + 512, 1, 1, + &s->aes_encrypt_key); + if (bdrv_pwrite(bs->file, cluster_offset + i * 512, + s->cluster_data, 512) != 512) + return -1; + } + } + } + } else if (allocate == 2) { + cluster_offset |= QCOW_OFLAG_COMPRESSED | + (uint64_t)compressed_size << (63 - s->cluster_bits); + } + } + /* update L2 table */ + tmp = cpu_to_be64(cluster_offset); + l2_table[l2_index] = tmp; + if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), + &tmp, sizeof(tmp)) < 0) + return 0; + } + return cluster_offset; +} + +static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + BDRVQcowState *s = bs->opaque; + int index_in_cluster, n; + uint64_t cluster_offset; + + qemu_co_mutex_lock(&s->lock); + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); + qemu_co_mutex_unlock(&s->lock); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + *pnum = n; + if (!cluster_offset) { + return 0; + } + if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) { + return BDRV_BLOCK_DATA; + } + cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset; +} + +static int decompress_buffer(uint8_t *out_buf, int out_buf_size, + const uint8_t *buf, int buf_size) +{ + z_stream strm1, *strm = &strm1; + int ret, out_len; + + memset(strm, 0, sizeof(*strm)); + + strm->next_in = (uint8_t *)buf; + strm->avail_in = buf_size; + strm->next_out = out_buf; + strm->avail_out = out_buf_size; + + ret = inflateInit2(strm, -12); + if (ret != Z_OK) + return -1; + ret = inflate(strm, Z_FINISH); + out_len = strm->next_out - out_buf; + if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || + out_len != out_buf_size) { + inflateEnd(strm); + return -1; + } + inflateEnd(strm); + return 0; +} + +static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) +{ + BDRVQcowState *s = bs->opaque; + int ret, csize; + uint64_t coffset; + + coffset = cluster_offset & s->cluster_offset_mask; + if (s->cluster_cache_offset != coffset) { + csize = cluster_offset >> (63 - s->cluster_bits); + csize &= (s->cluster_size - 1); + ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); + if (ret != csize) + return -1; + if (decompress_buffer(s->cluster_cache, s->cluster_size, + s->cluster_data, csize) < 0) { + return -1; + } + s->cluster_cache_offset = coffset; + } + return 0; +} + +static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVQcowState *s = bs->opaque; + int index_in_cluster; + int ret = 0, n; + uint64_t cluster_offset; + struct iovec hd_iov; + QEMUIOVector hd_qiov; + uint8_t *buf; + void *orig_buf; + + if (qiov->niov > 1) { + buf = orig_buf = qemu_try_blockalign(bs, qiov->size); + if (buf == NULL) { + return -ENOMEM; + } + } else { + orig_buf = NULL; + buf = (uint8_t *)qiov->iov->iov_base; + } + + qemu_co_mutex_lock(&s->lock); + + while (nb_sectors != 0) { + /* prepare next request */ + cluster_offset = get_cluster_offset(bs, sector_num << 9, + 0, 0, 0, 0); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { + n = nb_sectors; + } + + if (!cluster_offset) { + if (bs->backing_hd) { + /* read from the base image */ + hd_iov.iov_base = (void *)buf; + hd_iov.iov_len = n * 512; + qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->backing_hd, sector_num, + n, &hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto fail; + } + } else { + /* Note: in this case, no need to wait */ + memset(buf, 0, 512 * n); + } + } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + /* add AIO support for compressed blocks ? */ + if (decompress_cluster(bs, cluster_offset) < 0) { + goto fail; + } + memcpy(buf, + s->cluster_cache + index_in_cluster * 512, 512 * n); + } else { + if ((cluster_offset & 511) != 0) { + goto fail; + } + hd_iov.iov_base = (void *)buf; + hd_iov.iov_len = n * 512; + qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->file, + (cluster_offset >> 9) + index_in_cluster, + n, &hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + break; + } + if (bs->encrypted) { + assert(s->crypt_method); + encrypt_sectors(s, sector_num, buf, buf, + n, 0, + &s->aes_decrypt_key); + } + } + ret = 0; + + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + +done: + qemu_co_mutex_unlock(&s->lock); + + if (qiov->niov > 1) { + qemu_iovec_from_buf(qiov, 0, orig_buf, qiov->size); + qemu_vfree(orig_buf); + } + + return ret; + +fail: + ret = -EIO; + goto done; +} + +static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVQcowState *s = bs->opaque; + int index_in_cluster; + uint64_t cluster_offset; + const uint8_t *src_buf; + int ret = 0, n; + uint8_t *cluster_data = NULL; + struct iovec hd_iov; + QEMUIOVector hd_qiov; + uint8_t *buf; + void *orig_buf; + + s->cluster_cache_offset = -1; /* disable compressed cache */ + + if (qiov->niov > 1) { + buf = orig_buf = qemu_try_blockalign(bs, qiov->size); + if (buf == NULL) { + return -ENOMEM; + } + qemu_iovec_to_buf(qiov, 0, buf, qiov->size); + } else { + orig_buf = NULL; + buf = (uint8_t *)qiov->iov->iov_base; + } + + qemu_co_mutex_lock(&s->lock); + + while (nb_sectors != 0) { + + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { + n = nb_sectors; + } + cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0, + index_in_cluster, + index_in_cluster + n); + if (!cluster_offset || (cluster_offset & 511) != 0) { + ret = -EIO; + break; + } + if (bs->encrypted) { + assert(s->crypt_method); + if (!cluster_data) { + cluster_data = g_malloc0(s->cluster_size); + } + encrypt_sectors(s, sector_num, cluster_data, buf, + n, 1, &s->aes_encrypt_key); + src_buf = cluster_data; + } else { + src_buf = buf; + } + + hd_iov.iov_base = (void *)src_buf; + hd_iov.iov_len = n * 512; + qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_writev(bs->file, + (cluster_offset >> 9) + index_in_cluster, + n, &hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + break; + } + ret = 0; + + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + qemu_co_mutex_unlock(&s->lock); + + if (qiov->niov > 1) { + qemu_vfree(orig_buf); + } + g_free(cluster_data); + + return ret; +} + +static void qcow_close(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + + g_free(s->l1_table); + qemu_vfree(s->l2_cache); + g_free(s->cluster_cache); + g_free(s->cluster_data); + + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); +} + +static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int header_size, backing_filename_len, l1_size, shift, i; + QCowHeader header; + uint8_t *tmp; + int64_t total_size = 0; + char *backing_file = NULL; + int flags = 0; + Error *local_err = NULL; + int ret; + BlockDriverState *qcow_bs; + + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); + if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) { + flags |= BLOCK_FLAG_ENCRYPT; + } + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto cleanup; + } + + qcow_bs = NULL; + ret = bdrv_open(&qcow_bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto cleanup; + } + + ret = bdrv_truncate(qcow_bs, 0); + if (ret < 0) { + goto exit; + } + + memset(&header, 0, sizeof(header)); + header.magic = cpu_to_be32(QCOW_MAGIC); + header.version = cpu_to_be32(QCOW_VERSION); + header.size = cpu_to_be64(total_size); + header_size = sizeof(header); + backing_filename_len = 0; + if (backing_file) { + if (strcmp(backing_file, "fat:")) { + header.backing_file_offset = cpu_to_be64(header_size); + backing_filename_len = strlen(backing_file); + header.backing_file_size = cpu_to_be32(backing_filename_len); + header_size += backing_filename_len; + } else { + /* special backing file for vvfat */ + backing_file = NULL; + } + header.cluster_bits = 9; /* 512 byte cluster to avoid copying + unmodified sectors */ + header.l2_bits = 12; /* 32 KB L2 tables */ + } else { + header.cluster_bits = 12; /* 4 KB clusters */ + header.l2_bits = 9; /* 4 KB L2 tables */ + } + header_size = (header_size + 7) & ~7; + shift = header.cluster_bits + header.l2_bits; + l1_size = (total_size + (1LL << shift) - 1) >> shift; + + header.l1_table_offset = cpu_to_be64(header_size); + if (flags & BLOCK_FLAG_ENCRYPT) { + header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); + } else { + header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); + } + + /* write all the data */ + ret = bdrv_pwrite(qcow_bs, 0, &header, sizeof(header)); + if (ret != sizeof(header)) { + goto exit; + } + + if (backing_file) { + ret = bdrv_pwrite(qcow_bs, sizeof(header), + backing_file, backing_filename_len); + if (ret != backing_filename_len) { + goto exit; + } + } + + tmp = g_malloc0(BDRV_SECTOR_SIZE); + for (i = 0; i < ((sizeof(uint64_t)*l1_size + BDRV_SECTOR_SIZE - 1)/ + BDRV_SECTOR_SIZE); i++) { + ret = bdrv_pwrite(qcow_bs, header_size + + BDRV_SECTOR_SIZE*i, tmp, BDRV_SECTOR_SIZE); + if (ret != BDRV_SECTOR_SIZE) { + g_free(tmp); + goto exit; + } + } + + g_free(tmp); + ret = 0; +exit: + bdrv_unref(qcow_bs); +cleanup: + g_free(backing_file); + return ret; +} + +static int qcow_make_empty(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint32_t l1_length = s->l1_size * sizeof(uint64_t); + int ret; + + memset(s->l1_table, 0, l1_length); + if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, + l1_length) < 0) + return -1; + ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); + if (ret < 0) + return ret; + + memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t)); + memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t)); + + return 0; +} + +/* XXX: put compressed sectors first, then all the cluster aligned + tables to avoid losing bytes in alignment */ +static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + z_stream strm; + int ret, out_len; + uint8_t *out_buf; + uint64_t cluster_offset; + + if (nb_sectors != s->cluster_sectors) { + ret = -EINVAL; + + /* Zero-pad last write if image size is not cluster aligned */ + if (sector_num + nb_sectors == bs->total_sectors && + nb_sectors < s->cluster_sectors) { + uint8_t *pad_buf = qemu_blockalign(bs, s->cluster_size); + memset(pad_buf, 0, s->cluster_size); + memcpy(pad_buf, buf, nb_sectors * BDRV_SECTOR_SIZE); + ret = qcow_write_compressed(bs, sector_num, + pad_buf, s->cluster_sectors); + qemu_vfree(pad_buf); + } + return ret; + } + + out_buf = g_malloc(s->cluster_size + (s->cluster_size / 1000) + 128); + + /* best compression, small window, no zlib header */ + memset(&strm, 0, sizeof(strm)); + ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, + Z_DEFLATED, -12, + 9, Z_DEFAULT_STRATEGY); + if (ret != 0) { + ret = -EINVAL; + goto fail; + } + + strm.avail_in = s->cluster_size; + strm.next_in = (uint8_t *)buf; + strm.avail_out = s->cluster_size; + strm.next_out = out_buf; + + ret = deflate(&strm, Z_FINISH); + if (ret != Z_STREAM_END && ret != Z_OK) { + deflateEnd(&strm); + ret = -EINVAL; + goto fail; + } + out_len = strm.next_out - out_buf; + + deflateEnd(&strm); + + if (ret != Z_STREAM_END || out_len >= s->cluster_size) { + /* could not compress: write normal cluster */ + ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors); + if (ret < 0) { + goto fail; + } + } else { + cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, + out_len, 0, 0); + if (cluster_offset == 0) { + ret = -EIO; + goto fail; + } + + cluster_offset &= s->cluster_offset_mask; + ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len); + if (ret < 0) { + goto fail; + } + } + + ret = 0; +fail: + g_free(out_buf); + return ret; +} + +static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVQcowState *s = bs->opaque; + bdi->cluster_size = s->cluster_size; + return 0; +} + +static QemuOptsList qcow_create_opts = { + .name = "qcow-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(qcow_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_BACKING_FILE, + .type = QEMU_OPT_STRING, + .help = "File name of a base image" + }, + { + .name = BLOCK_OPT_ENCRYPT, + .type = QEMU_OPT_BOOL, + .help = "Encrypt the image", + .def_value_str = "off" + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_qcow = { + .format_name = "qcow", + .instance_size = sizeof(BDRVQcowState), + .bdrv_probe = qcow_probe, + .bdrv_open = qcow_open, + .bdrv_close = qcow_close, + .bdrv_reopen_prepare = qcow_reopen_prepare, + .bdrv_create = qcow_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .supports_backing = true, + + .bdrv_co_readv = qcow_co_readv, + .bdrv_co_writev = qcow_co_writev, + .bdrv_co_get_block_status = qcow_co_get_block_status, + + .bdrv_set_key = qcow_set_key, + .bdrv_make_empty = qcow_make_empty, + .bdrv_write_compressed = qcow_write_compressed, + .bdrv_get_info = qcow_get_info, + + .create_opts = &qcow_create_opts, +}; + +static void bdrv_qcow_init(void) +{ + bdrv_register(&bdrv_qcow); +} + +block_init(bdrv_qcow_init); diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c new file mode 100644 index 0000000..ed92a09 --- /dev/null +++ b/block/qcow2-cache.c @@ -0,0 +1,353 @@ +/* + * L2/refcount table cache for the QCOW2 format + * + * Copyright (c) 2010 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block/block_int.h" +#include "qemu-common.h" +#include "qcow2.h" +#include "trace.h" + +typedef struct Qcow2CachedTable { + int64_t offset; + bool dirty; + uint64_t lru_counter; + int ref; +} Qcow2CachedTable; + +struct Qcow2Cache { + Qcow2CachedTable *entries; + struct Qcow2Cache *depends; + int size; + bool depends_on_flush; + void *table_array; + uint64_t lru_counter; +}; + +static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs, + Qcow2Cache *c, int table) +{ + BDRVQcowState *s = bs->opaque; + return (uint8_t *) c->table_array + (size_t) table * s->cluster_size; +} + +static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, + Qcow2Cache *c, void *table) +{ + BDRVQcowState *s = bs->opaque; + ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; + int idx = table_offset / s->cluster_size; + assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0); + return idx; +} + +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) +{ + BDRVQcowState *s = bs->opaque; + Qcow2Cache *c; + + c = g_new0(Qcow2Cache, 1); + c->size = num_tables; + c->entries = g_try_new0(Qcow2CachedTable, num_tables); + c->table_array = qemu_try_blockalign(bs->file, + (size_t) num_tables * s->cluster_size); + + if (!c->entries || !c->table_array) { + qemu_vfree(c->table_array); + g_free(c->entries); + g_free(c); + c = NULL; + } + + return c; +} + +int qcow2_cache_destroy(BlockDriverState *bs, Qcow2Cache *c) +{ + int i; + + for (i = 0; i < c->size; i++) { + assert(c->entries[i].ref == 0); + } + + qemu_vfree(c->table_array); + g_free(c->entries); + g_free(c); + + return 0; +} + +static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) +{ + int ret; + + ret = qcow2_cache_flush(bs, c->depends); + if (ret < 0) { + return ret; + } + + c->depends = NULL; + c->depends_on_flush = false; + + return 0; +} + +static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) +{ + BDRVQcowState *s = bs->opaque; + int ret = 0; + + if (!c->entries[i].dirty || !c->entries[i].offset) { + return 0; + } + + trace_qcow2_cache_entry_flush(qemu_coroutine_self(), + c == s->l2_table_cache, i); + + if (c->depends) { + ret = qcow2_cache_flush_dependency(bs, c); + } else if (c->depends_on_flush) { + ret = bdrv_flush(bs->file); + if (ret >= 0) { + c->depends_on_flush = false; + } + } + + if (ret < 0) { + return ret; + } + + if (c == s->refcount_block_cache) { + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, + c->entries[i].offset, s->cluster_size); + } else if (c == s->l2_table_cache) { + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, + c->entries[i].offset, s->cluster_size); + } else { + ret = qcow2_pre_write_overlap_check(bs, 0, + c->entries[i].offset, s->cluster_size); + } + + if (ret < 0) { + return ret; + } + + if (c == s->refcount_block_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); + } else if (c == s->l2_table_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); + } + + ret = bdrv_pwrite(bs->file, c->entries[i].offset, + qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); + if (ret < 0) { + return ret; + } + + c->entries[i].dirty = false; + + return 0; +} + +int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) +{ + BDRVQcowState *s = bs->opaque; + int result = 0; + int ret; + int i; + + trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); + + for (i = 0; i < c->size; i++) { + ret = qcow2_cache_entry_flush(bs, c, i); + if (ret < 0 && result != -ENOSPC) { + result = ret; + } + } + + if (result == 0) { + ret = bdrv_flush(bs->file); + if (ret < 0) { + result = ret; + } + } + + return result; +} + +int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, + Qcow2Cache *dependency) +{ + int ret; + + if (dependency->depends) { + ret = qcow2_cache_flush_dependency(bs, dependency); + if (ret < 0) { + return ret; + } + } + + if (c->depends && (c->depends != dependency)) { + ret = qcow2_cache_flush_dependency(bs, c); + if (ret < 0) { + return ret; + } + } + + c->depends = dependency; + return 0; +} + +void qcow2_cache_depends_on_flush(Qcow2Cache *c) +{ + c->depends_on_flush = true; +} + +int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) +{ + int ret, i; + + ret = qcow2_cache_flush(bs, c); + if (ret < 0) { + return ret; + } + + for (i = 0; i < c->size; i++) { + assert(c->entries[i].ref == 0); + c->entries[i].offset = 0; + c->entries[i].lru_counter = 0; + } + + c->lru_counter = 0; + + return 0; +} + +static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, + uint64_t offset, void **table, bool read_from_disk) +{ + BDRVQcowState *s = bs->opaque; + int i; + int ret; + int lookup_index; + uint64_t min_lru_counter = UINT64_MAX; + int min_lru_index = -1; + + trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, + offset, read_from_disk); + + /* Check if the table is already cached */ + i = lookup_index = (offset / s->cluster_size * 4) % c->size; + do { + const Qcow2CachedTable *t = &c->entries[i]; + if (t->offset == offset) { + goto found; + } + if (t->ref == 0 && t->lru_counter < min_lru_counter) { + min_lru_counter = t->lru_counter; + min_lru_index = i; + } + if (++i == c->size) { + i = 0; + } + } while (i != lookup_index); + + if (min_lru_index == -1) { + /* This can't happen in current synchronous code, but leave the check + * here as a reminder for whoever starts using AIO with the cache */ + abort(); + } + + /* Cache miss: write a table back and replace it */ + i = min_lru_index; + trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), + c == s->l2_table_cache, i); + if (i < 0) { + return i; + } + + ret = qcow2_cache_entry_flush(bs, c, i); + if (ret < 0) { + return ret; + } + + trace_qcow2_cache_get_read(qemu_coroutine_self(), + c == s->l2_table_cache, i); + c->entries[i].offset = 0; + if (read_from_disk) { + if (c == s->l2_table_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); + } + + ret = bdrv_pread(bs->file, offset, qcow2_cache_get_table_addr(bs, c, i), + s->cluster_size); + if (ret < 0) { + return ret; + } + } + + c->entries[i].offset = offset; + + /* And return the right table */ +found: + c->entries[i].ref++; + *table = qcow2_cache_get_table_addr(bs, c, i); + + trace_qcow2_cache_get_done(qemu_coroutine_self(), + c == s->l2_table_cache, i); + + return 0; +} + +int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table) +{ + return qcow2_cache_do_get(bs, c, offset, table, true); +} + +int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table) +{ + return qcow2_cache_do_get(bs, c, offset, table, false); +} + +void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) +{ + int i = qcow2_cache_get_table_idx(bs, c, *table); + + c->entries[i].ref--; + *table = NULL; + + if (c->entries[i].ref == 0) { + c->entries[i].lru_counter = ++c->lru_counter; + } + + assert(c->entries[i].ref >= 0); +} + +void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, + void *table) +{ + int i = qcow2_cache_get_table_idx(bs, c, table); + assert(c->entries[i].offset != 0); + c->entries[i].dirty = true; +} diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c new file mode 100644 index 0000000..1a5c97a --- /dev/null +++ b/block/qcow2-cluster.c @@ -0,0 +1,1853 @@ +/* + * Block driver for the QCOW version 2 format + * + * Copyright (c) 2004-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "qemu-common.h" +#include "block/block_int.h" +#include "block/qcow2.h" +#include "trace.h" + +int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, + bool exact_size) +{ + BDRVQcowState *s = bs->opaque; + int new_l1_size2, ret, i; + uint64_t *new_l1_table; + int64_t old_l1_table_offset, old_l1_size; + int64_t new_l1_table_offset, new_l1_size; + uint8_t data[12]; + + if (min_size <= s->l1_size) + return 0; + + /* Do a sanity check on min_size before trying to calculate new_l1_size + * (this prevents overflows during the while loop for the calculation of + * new_l1_size) */ + if (min_size > INT_MAX / sizeof(uint64_t)) { + return -EFBIG; + } + + if (exact_size) { + new_l1_size = min_size; + } else { + /* Bump size up to reduce the number of times we have to grow */ + new_l1_size = s->l1_size; + if (new_l1_size == 0) { + new_l1_size = 1; + } + while (min_size > new_l1_size) { + new_l1_size = (new_l1_size * 3 + 1) / 2; + } + } + + if (new_l1_size > INT_MAX / sizeof(uint64_t)) { + return -EFBIG; + } + +#ifdef DEBUG_ALLOC2 + fprintf(stderr, "grow l1_table from %d to %" PRId64 "\n", + s->l1_size, new_l1_size); +#endif + + new_l1_size2 = sizeof(uint64_t) * new_l1_size; + new_l1_table = qemu_try_blockalign(bs->file, + align_offset(new_l1_size2, 512)); + if (new_l1_table == NULL) { + return -ENOMEM; + } + memset(new_l1_table, 0, align_offset(new_l1_size2, 512)); + + memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); + + /* write new table (align to cluster) */ + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE); + new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); + if (new_l1_table_offset < 0) { + qemu_vfree(new_l1_table); + return new_l1_table_offset; + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail; + } + + /* the L1 position has not yet been updated, so these clusters must + * indeed be completely free */ + ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset, + new_l1_size2); + if (ret < 0) { + goto fail; + } + + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); + for(i = 0; i < s->l1_size; i++) + new_l1_table[i] = cpu_to_be64(new_l1_table[i]); + ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2); + if (ret < 0) + goto fail; + for(i = 0; i < s->l1_size; i++) + new_l1_table[i] = be64_to_cpu(new_l1_table[i]); + + /* set new table */ + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); + cpu_to_be32w((uint32_t*)data, new_l1_size); + stq_be_p(data + 4, new_l1_table_offset); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); + if (ret < 0) { + goto fail; + } + qemu_vfree(s->l1_table); + old_l1_table_offset = s->l1_table_offset; + s->l1_table_offset = new_l1_table_offset; + s->l1_table = new_l1_table; + old_l1_size = s->l1_size; + s->l1_size = new_l1_size; + qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * sizeof(uint64_t), + QCOW2_DISCARD_OTHER); + return 0; + fail: + qemu_vfree(new_l1_table); + qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2, + QCOW2_DISCARD_OTHER); + return ret; +} + +/* + * l2_load + * + * Loads a L2 table into memory. If the table is in the cache, the cache + * is used; otherwise the L2 table is loaded from the image file. + * + * Returns a pointer to the L2 table on success, or NULL if the read from + * the image file failed. + */ + +static int l2_load(BlockDriverState *bs, uint64_t l2_offset, + uint64_t **l2_table) +{ + BDRVQcowState *s = bs->opaque; + int ret; + + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); + + return ret; +} + +/* + * Writes one sector of the L1 table to the disk (can't update single entries + * and we really don't want bdrv_pread to perform a read-modify-write) + */ +#define L1_ENTRIES_PER_SECTOR (512 / 8) +int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) +{ + BDRVQcowState *s = bs->opaque; + uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 }; + int l1_start_index; + int i, ret; + + l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1); + for (i = 0; i < L1_ENTRIES_PER_SECTOR && l1_start_index + i < s->l1_size; + i++) + { + buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); + } + + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, + s->l1_table_offset + 8 * l1_start_index, sizeof(buf)); + if (ret < 0) { + return ret; + } + + BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, + buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + return 0; +} + +/* + * l2_allocate + * + * Allocate a new l2 entry in the file. If l1_index points to an already + * used entry in the L2 table (i.e. we are doing a copy on write for the L2 + * table) copy the contents of the old L2 table into the newly allocated one. + * Otherwise the new table is initialized with zeros. + * + */ + +static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) +{ + BDRVQcowState *s = bs->opaque; + uint64_t old_l2_offset; + uint64_t *l2_table = NULL; + int64_t l2_offset; + int ret; + + old_l2_offset = s->l1_table[l1_index]; + + trace_qcow2_l2_allocate(bs, l1_index); + + /* allocate a new l2 entry */ + + l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); + if (l2_offset < 0) { + ret = l2_offset; + goto fail; + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail; + } + + /* allocate a new entry in the l2 cache */ + + trace_qcow2_l2_allocate_get_empty(bs, l1_index); + ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); + if (ret < 0) { + goto fail; + } + + l2_table = *table; + + if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { + /* if there was no old l2 table, clear the new table */ + memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); + } else { + uint64_t* old_table; + + /* if there was an old l2 table, read it from the disk */ + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); + ret = qcow2_cache_get(bs, s->l2_table_cache, + old_l2_offset & L1E_OFFSET_MASK, + (void**) &old_table); + if (ret < 0) { + goto fail; + } + + memcpy(l2_table, old_table, s->cluster_size); + + qcow2_cache_put(bs, s->l2_table_cache, (void **) &old_table); + } + + /* write the l2 table to the file */ + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); + + trace_qcow2_l2_allocate_write_l2(bs, l1_index); + qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret < 0) { + goto fail; + } + + /* update the L1 entry */ + trace_qcow2_l2_allocate_write_l1(bs, l1_index); + s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; + ret = qcow2_write_l1_entry(bs, l1_index); + if (ret < 0) { + goto fail; + } + + *table = l2_table; + trace_qcow2_l2_allocate_done(bs, l1_index, 0); + return 0; + +fail: + trace_qcow2_l2_allocate_done(bs, l1_index, ret); + if (l2_table != NULL) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) table); + } + s->l1_table[l1_index] = old_l2_offset; + if (l2_offset > 0) { + qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t), + QCOW2_DISCARD_ALWAYS); + } + return ret; +} + +/* + * Checks how many clusters in a given L2 table are contiguous in the image + * file. As soon as one of the flags in the bitmask stop_flags changes compared + * to the first cluster, the search is stopped and the cluster is not counted + * as contiguous. (This allows it, for example, to stop at the first compressed + * cluster which may require a different handling) + */ +static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, + uint64_t *l2_table, uint64_t stop_flags) +{ + int i; + uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED; + uint64_t first_entry = be64_to_cpu(l2_table[0]); + uint64_t offset = first_entry & mask; + + if (!offset) + return 0; + + assert(qcow2_get_cluster_type(first_entry) != QCOW2_CLUSTER_COMPRESSED); + + for (i = 0; i < nb_clusters; i++) { + uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask; + if (offset + (uint64_t) i * cluster_size != l2_entry) { + break; + } + } + + return i; +} + +static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table) +{ + int i; + + for (i = 0; i < nb_clusters; i++) { + int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i])); + + if (type != QCOW2_CLUSTER_UNALLOCATED) { + break; + } + } + + return i; +} + +/* The crypt function is compatible with the linux cryptoloop + algorithm for < 4 GB images. NOTE: out_buf == in_buf is + supported */ +void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, + uint8_t *out_buf, const uint8_t *in_buf, + int nb_sectors, int enc, + const AES_KEY *key) +{ + union { + uint64_t ll[2]; + uint8_t b[16]; + } ivec; + int i; + + for(i = 0; i < nb_sectors; i++) { + ivec.ll[0] = cpu_to_le64(sector_num); + ivec.ll[1] = 0; + AES_cbc_encrypt(in_buf, out_buf, 512, key, + ivec.b, enc); + sector_num++; + in_buf += 512; + out_buf += 512; + } +} + +static int coroutine_fn copy_sectors(BlockDriverState *bs, + uint64_t start_sect, + uint64_t cluster_offset, + int n_start, int n_end) +{ + BDRVQcowState *s = bs->opaque; + QEMUIOVector qiov; + struct iovec iov; + int n, ret; + + n = n_end - n_start; + if (n <= 0) { + return 0; + } + + iov.iov_len = n * BDRV_SECTOR_SIZE; + iov.iov_base = qemu_try_blockalign(bs, iov.iov_len); + if (iov.iov_base == NULL) { + return -ENOMEM; + } + + qemu_iovec_init_external(&qiov, &iov, 1); + + BLKDBG_EVENT(bs->file, BLKDBG_COW_READ); + + if (!bs->drv) { + ret = -ENOMEDIUM; + goto out; + } + + /* Call .bdrv_co_readv() directly instead of using the public block-layer + * interface. This avoids double I/O throttling and request tracking, + * which can lead to deadlock when block layer copy-on-read is enabled. + */ + ret = bs->drv->bdrv_co_readv(bs, start_sect + n_start, n, &qiov); + if (ret < 0) { + goto out; + } + + if (bs->encrypted) { + assert(s->crypt_method); + qcow2_encrypt_sectors(s, start_sect + n_start, + iov.iov_base, iov.iov_base, n, 1, + &s->aes_encrypt_key); + } + + ret = qcow2_pre_write_overlap_check(bs, 0, + cluster_offset + n_start * BDRV_SECTOR_SIZE, n * BDRV_SECTOR_SIZE); + if (ret < 0) { + goto out; + } + + BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); + ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov); + if (ret < 0) { + goto out; + } + + ret = 0; +out: + qemu_vfree(iov.iov_base); + return ret; +} + + +/* + * get_cluster_offset + * + * For a given offset of the disk image, find the cluster offset in + * qcow2 file. The offset is stored in *cluster_offset. + * + * on entry, *num is the number of contiguous sectors we'd like to + * access following offset. + * + * on exit, *num is the number of contiguous sectors we can read. + * + * Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error + * cases. + */ +int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, + int *num, uint64_t *cluster_offset) +{ + BDRVQcowState *s = bs->opaque; + unsigned int l2_index; + uint64_t l1_index, l2_offset, *l2_table; + int l1_bits, c; + unsigned int index_in_cluster, nb_clusters; + uint64_t nb_available, nb_needed; + int ret; + + index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1); + nb_needed = *num + index_in_cluster; + + l1_bits = s->l2_bits + s->cluster_bits; + + /* compute how many bytes there are between the offset and + * the end of the l1 entry + */ + + nb_available = (1ULL << l1_bits) - (offset & ((1ULL << l1_bits) - 1)); + + /* compute the number of available sectors */ + + nb_available = (nb_available >> 9) + index_in_cluster; + + if (nb_needed > nb_available) { + nb_needed = nb_available; + } + + *cluster_offset = 0; + + /* seek the the l2 offset in the l1 table */ + + l1_index = offset >> l1_bits; + if (l1_index >= s->l1_size) { + ret = QCOW2_CLUSTER_UNALLOCATED; + goto out; + } + + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; + if (!l2_offset) { + ret = QCOW2_CLUSTER_UNALLOCATED; + goto out; + } + + if (offset_into_cluster(s, l2_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64 + " unaligned (L1 index: %#" PRIx64 ")", + l2_offset, l1_index); + return -EIO; + } + + /* load the l2 table in memory */ + + ret = l2_load(bs, l2_offset, &l2_table); + if (ret < 0) { + return ret; + } + + /* find the cluster offset for the given disk offset */ + + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); + *cluster_offset = be64_to_cpu(l2_table[l2_index]); + nb_clusters = size_to_clusters(s, nb_needed << 9); + + ret = qcow2_get_cluster_type(*cluster_offset); + switch (ret) { + case QCOW2_CLUSTER_COMPRESSED: + /* Compressed clusters can only be processed one by one */ + c = 1; + *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; + break; + case QCOW2_CLUSTER_ZERO: + if (s->qcow_version < 3) { + qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found" + " in pre-v3 image (L2 offset: %#" PRIx64 + ", L2 index: %#x)", l2_offset, l2_index); + ret = -EIO; + goto fail; + } + c = count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], QCOW_OFLAG_ZERO); + *cluster_offset = 0; + break; + case QCOW2_CLUSTER_UNALLOCATED: + /* how many empty clusters ? */ + c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); + *cluster_offset = 0; + break; + case QCOW2_CLUSTER_NORMAL: + /* how many allocated clusters ? */ + c = count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], QCOW_OFLAG_ZERO); + *cluster_offset &= L2E_OFFSET_MASK; + if (offset_into_cluster(s, *cluster_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset %#" + PRIx64 " unaligned (L2 offset: %#" PRIx64 + ", L2 index: %#x)", *cluster_offset, + l2_offset, l2_index); + ret = -EIO; + goto fail; + } + break; + default: + abort(); + } + + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + + nb_available = (c * s->cluster_sectors); + +out: + if (nb_available > nb_needed) + nb_available = nb_needed; + + *num = nb_available - index_in_cluster; + + return ret; + +fail: + qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); + return ret; +} + +/* + * get_cluster_table + * + * for a given disk offset, load (and allocate if needed) + * the l2 table. + * + * the l2 table offset in the qcow2 file and the cluster index + * in the l2 table are given to the caller. + * + * Returns 0 on success, -errno in failure case + */ +static int get_cluster_table(BlockDriverState *bs, uint64_t offset, + uint64_t **new_l2_table, + int *new_l2_index) +{ + BDRVQcowState *s = bs->opaque; + unsigned int l2_index; + uint64_t l1_index, l2_offset; + uint64_t *l2_table = NULL; + int ret; + + /* seek the the l2 offset in the l1 table */ + + l1_index = offset >> (s->l2_bits + s->cluster_bits); + if (l1_index >= s->l1_size) { + ret = qcow2_grow_l1_table(bs, l1_index + 1, false); + if (ret < 0) { + return ret; + } + } + + assert(l1_index < s->l1_size); + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; + if (offset_into_cluster(s, l2_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64 + " unaligned (L1 index: %#" PRIx64 ")", + l2_offset, l1_index); + return -EIO; + } + + /* seek the l2 table of the given l2 offset */ + + if (s->l1_table[l1_index] & QCOW_OFLAG_COPIED) { + /* load the l2 table in memory */ + ret = l2_load(bs, l2_offset, &l2_table); + if (ret < 0) { + return ret; + } + } else { + /* First allocate a new L2 table (and do COW if needed) */ + ret = l2_allocate(bs, l1_index, &l2_table); + if (ret < 0) { + return ret; + } + + /* Then decrease the refcount of the old table */ + if (l2_offset) { + qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t), + QCOW2_DISCARD_OTHER); + } + } + + /* find the cluster offset for the given disk offset */ + + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); + + *new_l2_table = l2_table; + *new_l2_index = l2_index; + + return 0; +} + +/* + * alloc_compressed_cluster_offset + * + * For a given offset of the disk image, return cluster offset in + * qcow2 file. + * + * If the offset is not found, allocate a new compressed cluster. + * + * Return the cluster offset if successful, + * Return 0, otherwise. + * + */ + +uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, + uint64_t offset, + int compressed_size) +{ + BDRVQcowState *s = bs->opaque; + int l2_index, ret; + uint64_t *l2_table; + int64_t cluster_offset; + int nb_csectors; + + ret = get_cluster_table(bs, offset, &l2_table, &l2_index); + if (ret < 0) { + return 0; + } + + /* Compression can't overwrite anything. Fail if the cluster was already + * allocated. */ + cluster_offset = be64_to_cpu(l2_table[l2_index]); + if (cluster_offset & L2E_OFFSET_MASK) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + return 0; + } + + cluster_offset = qcow2_alloc_bytes(bs, compressed_size); + if (cluster_offset < 0) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + return 0; + } + + nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) - + (cluster_offset >> 9); + + cluster_offset |= QCOW_OFLAG_COMPRESSED | + ((uint64_t)nb_csectors << s->csize_shift); + + /* update L2 table */ + + /* compressed clusters never have the copied flag */ + + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); + qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + l2_table[l2_index] = cpu_to_be64(cluster_offset); + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + + return cluster_offset; +} + +static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r) +{ + BDRVQcowState *s = bs->opaque; + int ret; + + if (r->nb_sectors == 0) { + return 0; + } + + qemu_co_mutex_unlock(&s->lock); + ret = copy_sectors(bs, m->offset / BDRV_SECTOR_SIZE, m->alloc_offset, + r->offset / BDRV_SECTOR_SIZE, + r->offset / BDRV_SECTOR_SIZE + r->nb_sectors); + qemu_co_mutex_lock(&s->lock); + + if (ret < 0) { + return ret; + } + + /* + * Before we update the L2 table to actually point to the new cluster, we + * need to be sure that the refcounts have been increased and COW was + * handled. + */ + qcow2_cache_depends_on_flush(s->l2_table_cache); + + return 0; +} + +int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) +{ + BDRVQcowState *s = bs->opaque; + int i, j = 0, l2_index, ret; + uint64_t *old_cluster, *l2_table; + uint64_t cluster_offset = m->alloc_offset; + + trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); + assert(m->nb_clusters > 0); + + old_cluster = g_try_new(uint64_t, m->nb_clusters); + if (old_cluster == NULL) { + ret = -ENOMEM; + goto err; + } + + /* copy content of unmodified sectors */ + ret = perform_cow(bs, m, &m->cow_start); + if (ret < 0) { + goto err; + } + + ret = perform_cow(bs, m, &m->cow_end); + if (ret < 0) { + goto err; + } + + /* Update L2 table. */ + if (s->use_lazy_refcounts) { + qcow2_mark_dirty(bs); + } + if (qcow2_need_accurate_refcounts(s)) { + qcow2_cache_set_dependency(bs, s->l2_table_cache, + s->refcount_block_cache); + } + + ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index); + if (ret < 0) { + goto err; + } + qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + + assert(l2_index + m->nb_clusters <= s->l2_size); + for (i = 0; i < m->nb_clusters; i++) { + /* if two concurrent writes happen to the same unallocated cluster + * each write allocates separate cluster and writes data concurrently. + * The first one to complete updates l2 table with pointer to its + * cluster the second one has to do RMW (which is done above by + * copy_sectors()), update l2 table with its cluster pointer and free + * old cluster. This is what this loop does */ + if(l2_table[l2_index + i] != 0) + old_cluster[j++] = l2_table[l2_index + i]; + + l2_table[l2_index + i] = cpu_to_be64((cluster_offset + + (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); + } + + + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + + /* + * If this was a COW, we need to decrease the refcount of the old cluster. + * Also flush bs->file to get the right order for L2 and refcount update. + * + * Don't discard clusters that reach a refcount of 0 (e.g. compressed + * clusters), the next write will reuse them anyway. + */ + if (j != 0) { + for (i = 0; i < j; i++) { + qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1, + QCOW2_DISCARD_NEVER); + } + } + + ret = 0; +err: + g_free(old_cluster); + return ret; + } + +/* + * Returns the number of contiguous clusters that can be used for an allocating + * write, but require COW to be performed (this includes yet unallocated space, + * which must copy from the backing file) + */ +static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, + uint64_t *l2_table, int l2_index) +{ + int i; + + for (i = 0; i < nb_clusters; i++) { + uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]); + int cluster_type = qcow2_get_cluster_type(l2_entry); + + switch(cluster_type) { + case QCOW2_CLUSTER_NORMAL: + if (l2_entry & QCOW_OFLAG_COPIED) { + goto out; + } + break; + case QCOW2_CLUSTER_UNALLOCATED: + case QCOW2_CLUSTER_COMPRESSED: + case QCOW2_CLUSTER_ZERO: + break; + default: + abort(); + } + } + +out: + assert(i <= nb_clusters); + return i; +} + +/* + * Check if there already is an AIO write request in flight which allocates + * the same cluster. In this case we need to wait until the previous + * request has completed and updated the L2 table accordingly. + * + * Returns: + * 0 if there was no dependency. *cur_bytes indicates the number of + * bytes from guest_offset that can be read before the next + * dependency must be processed (or the request is complete) + * + * -EAGAIN if we had to wait for another request, previously gathered + * information on cluster allocation may be invalid now. The caller + * must start over anyway, so consider *cur_bytes undefined. + */ +static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, + uint64_t *cur_bytes, QCowL2Meta **m) +{ + BDRVQcowState *s = bs->opaque; + QCowL2Meta *old_alloc; + uint64_t bytes = *cur_bytes; + + QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) { + + uint64_t start = guest_offset; + uint64_t end = start + bytes; + uint64_t old_start = l2meta_cow_start(old_alloc); + uint64_t old_end = l2meta_cow_end(old_alloc); + + if (end <= old_start || start >= old_end) { + /* No intersection */ + } else { + if (start < old_start) { + /* Stop at the start of a running allocation */ + bytes = old_start - start; + } else { + bytes = 0; + } + + /* Stop if already an l2meta exists. After yielding, it wouldn't + * be valid any more, so we'd have to clean up the old L2Metas + * and deal with requests depending on them before starting to + * gather new ones. Not worth the trouble. */ + if (bytes == 0 && *m) { + *cur_bytes = 0; + return 0; + } + + if (bytes == 0) { + /* Wait for the dependency to complete. We need to recheck + * the free/allocated clusters when we continue. */ + qemu_co_mutex_unlock(&s->lock); + qemu_co_queue_wait(&old_alloc->dependent_requests); + qemu_co_mutex_lock(&s->lock); + return -EAGAIN; + } + } + } + + /* Make sure that existing clusters and new allocations are only used up to + * the next dependency if we shortened the request above */ + *cur_bytes = bytes; + + return 0; +} + +/* + * Checks how many already allocated clusters that don't require a copy on + * write there are at the given guest_offset (up to *bytes). If + * *host_offset is not zero, only physically contiguous clusters beginning at + * this host offset are counted. + * + * Note that guest_offset may not be cluster aligned. In this case, the + * returned *host_offset points to exact byte referenced by guest_offset and + * therefore isn't cluster aligned as well. + * + * Returns: + * 0: if no allocated clusters are available at the given offset. + * *bytes is normally unchanged. It is set to 0 if the cluster + * is allocated and doesn't need COW, but doesn't have the right + * physical offset. + * + * 1: if allocated clusters that don't require a COW are available at + * the requested offset. *bytes may have decreased and describes + * the length of the area that can be written to. + * + * -errno: in error cases + */ +static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, + uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) +{ + BDRVQcowState *s = bs->opaque; + int l2_index; + uint64_t cluster_offset; + uint64_t *l2_table; + unsigned int nb_clusters; + unsigned int keep_clusters; + int ret; + + trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, + *bytes); + + assert(*host_offset == 0 || offset_into_cluster(s, guest_offset) + == offset_into_cluster(s, *host_offset)); + + /* + * Calculate the number of clusters to look for. We stop at L2 table + * boundaries to keep things simple. + */ + nb_clusters = + size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); + + l2_index = offset_to_l2_index(s, guest_offset); + nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + + /* Find L2 entry for the first involved cluster */ + ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); + if (ret < 0) { + return ret; + } + + cluster_offset = be64_to_cpu(l2_table[l2_index]); + + /* Check how many clusters are already allocated and don't need COW */ + if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL + && (cluster_offset & QCOW_OFLAG_COPIED)) + { + /* If a specific host_offset is required, check it */ + bool offset_matches = + (cluster_offset & L2E_OFFSET_MASK) == *host_offset; + + if (offset_into_cluster(s, cluster_offset & L2E_OFFSET_MASK)) { + qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset " + "%#llx unaligned (guest offset: %#" PRIx64 + ")", cluster_offset & L2E_OFFSET_MASK, + guest_offset); + ret = -EIO; + goto out; + } + + if (*host_offset != 0 && !offset_matches) { + *bytes = 0; + ret = 0; + goto out; + } + + /* We keep all QCOW_OFLAG_COPIED clusters */ + keep_clusters = + count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], + QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); + assert(keep_clusters <= nb_clusters); + + *bytes = MIN(*bytes, + keep_clusters * s->cluster_size + - offset_into_cluster(s, guest_offset)); + + ret = 1; + } else { + ret = 0; + } + + /* Cleanup */ +out: + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + + /* Only return a host offset if we actually made progress. Otherwise we + * would make requirements for handle_alloc() that it can't fulfill */ + if (ret > 0) { + *host_offset = (cluster_offset & L2E_OFFSET_MASK) + + offset_into_cluster(s, guest_offset); + } + + return ret; +} + +/* + * Allocates new clusters for the given guest_offset. + * + * At most *nb_clusters are allocated, and on return *nb_clusters is updated to + * contain the number of clusters that have been allocated and are contiguous + * in the image file. + * + * If *host_offset is non-zero, it specifies the offset in the image file at + * which the new clusters must start. *nb_clusters can be 0 on return in this + * case if the cluster at host_offset is already in use. If *host_offset is + * zero, the clusters can be allocated anywhere in the image file. + * + * *host_offset is updated to contain the offset into the image file at which + * the first allocated cluster starts. + * + * Return 0 on success and -errno in error cases. -EAGAIN means that the + * function has been waiting for another request and the allocation must be + * restarted, but the whole request should not be failed. + */ +static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, + uint64_t *host_offset, unsigned int *nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + + trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, + *host_offset, *nb_clusters); + + /* Allocate new clusters */ + trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); + if (*host_offset == 0) { + int64_t cluster_offset = + qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size); + if (cluster_offset < 0) { + return cluster_offset; + } + *host_offset = cluster_offset; + return 0; + } else { + int ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); + if (ret < 0) { + return ret; + } + *nb_clusters = ret; + return 0; + } +} + +/* + * Allocates new clusters for an area that either is yet unallocated or needs a + * copy on write. If *host_offset is non-zero, clusters are only allocated if + * the new allocation can match the specified host offset. + * + * Note that guest_offset may not be cluster aligned. In this case, the + * returned *host_offset points to exact byte referenced by guest_offset and + * therefore isn't cluster aligned as well. + * + * Returns: + * 0: if no clusters could be allocated. *bytes is set to 0, + * *host_offset is left unchanged. + * + * 1: if new clusters were allocated. *bytes may be decreased if the + * new allocation doesn't cover all of the requested area. + * *host_offset is updated to contain the host offset of the first + * newly allocated cluster. + * + * -errno: in error cases + */ +static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, + uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) +{ + BDRVQcowState *s = bs->opaque; + int l2_index; + uint64_t *l2_table; + uint64_t entry; + unsigned int nb_clusters; + int ret; + + uint64_t alloc_cluster_offset; + + trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, + *bytes); + assert(*bytes > 0); + + /* + * Calculate the number of clusters to look for. We stop at L2 table + * boundaries to keep things simple. + */ + nb_clusters = + size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); + + l2_index = offset_to_l2_index(s, guest_offset); + nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + + /* Find L2 entry for the first involved cluster */ + ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); + if (ret < 0) { + return ret; + } + + entry = be64_to_cpu(l2_table[l2_index]); + + /* For the moment, overwrite compressed clusters one by one */ + if (entry & QCOW_OFLAG_COMPRESSED) { + nb_clusters = 1; + } else { + nb_clusters = count_cow_clusters(s, nb_clusters, l2_table, l2_index); + } + + /* This function is only called when there were no non-COW clusters, so if + * we can't find any unallocated or COW clusters either, something is + * wrong with our code. */ + assert(nb_clusters > 0); + + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + + /* Allocate, if necessary at a given offset in the image file */ + alloc_cluster_offset = start_of_cluster(s, *host_offset); + ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset, + &nb_clusters); + if (ret < 0) { + goto fail; + } + + /* Can't extend contiguous allocation */ + if (nb_clusters == 0) { + *bytes = 0; + return 0; + } + + /* !*host_offset would overwrite the image header and is reserved for "no + * host offset preferred". If 0 was a valid host offset, it'd trigger the + * following overlap check; do that now to avoid having an invalid value in + * *host_offset. */ + if (!alloc_cluster_offset) { + ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset, + nb_clusters * s->cluster_size); + assert(ret < 0); + goto fail; + } + + /* + * Save info needed for meta data update. + * + * requested_sectors: Number of sectors from the start of the first + * newly allocated cluster to the end of the (possibly shortened + * before) write request. + * + * avail_sectors: Number of sectors from the start of the first + * newly allocated to the end of the last newly allocated cluster. + * + * nb_sectors: The number of sectors from the start of the first + * newly allocated cluster to the end of the area that the write + * request actually writes to (excluding COW at the end) + */ + int requested_sectors = + (*bytes + offset_into_cluster(s, guest_offset)) + >> BDRV_SECTOR_BITS; + int avail_sectors = nb_clusters + << (s->cluster_bits - BDRV_SECTOR_BITS); + int alloc_n_start = offset_into_cluster(s, guest_offset) + >> BDRV_SECTOR_BITS; + int nb_sectors = MIN(requested_sectors, avail_sectors); + QCowL2Meta *old_m = *m; + + *m = g_malloc0(sizeof(**m)); + + **m = (QCowL2Meta) { + .next = old_m, + + .alloc_offset = alloc_cluster_offset, + .offset = start_of_cluster(s, guest_offset), + .nb_clusters = nb_clusters, + .nb_available = nb_sectors, + + .cow_start = { + .offset = 0, + .nb_sectors = alloc_n_start, + }, + .cow_end = { + .offset = nb_sectors * BDRV_SECTOR_SIZE, + .nb_sectors = avail_sectors - nb_sectors, + }, + }; + qemu_co_queue_init(&(*m)->dependent_requests); + QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight); + + *host_offset = alloc_cluster_offset + offset_into_cluster(s, guest_offset); + *bytes = MIN(*bytes, (nb_sectors * BDRV_SECTOR_SIZE) + - offset_into_cluster(s, guest_offset)); + assert(*bytes != 0); + + return 1; + +fail: + if (*m && (*m)->nb_clusters > 0) { + QLIST_REMOVE(*m, next_in_flight); + } + return ret; +} + +/* + * alloc_cluster_offset + * + * For a given offset on the virtual disk, find the cluster offset in qcow2 + * file. If the offset is not found, allocate a new cluster. + * + * If the cluster was already allocated, m->nb_clusters is set to 0 and + * other fields in m are meaningless. + * + * If the cluster is newly allocated, m->nb_clusters is set to the number of + * contiguous clusters that have been allocated. In this case, the other + * fields of m are valid and contain information about the first allocated + * cluster. + * + * If the request conflicts with another write request in flight, the coroutine + * is queued and will be reentered when the dependency has completed. + * + * Return 0 on success and -errno in error cases + */ +int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, + int *num, uint64_t *host_offset, QCowL2Meta **m) +{ + BDRVQcowState *s = bs->opaque; + uint64_t start, remaining; + uint64_t cluster_offset; + uint64_t cur_bytes; + int ret; + + trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *num); + + assert((offset & ~BDRV_SECTOR_MASK) == 0); + +again: + start = offset; + remaining = (uint64_t)*num << BDRV_SECTOR_BITS; + cluster_offset = 0; + *host_offset = 0; + cur_bytes = 0; + *m = NULL; + + while (true) { + + if (!*host_offset) { + *host_offset = start_of_cluster(s, cluster_offset); + } + + assert(remaining >= cur_bytes); + + start += cur_bytes; + remaining -= cur_bytes; + cluster_offset += cur_bytes; + + if (remaining == 0) { + break; + } + + cur_bytes = remaining; + + /* + * Now start gathering as many contiguous clusters as possible: + * + * 1. Check for overlaps with in-flight allocations + * + * a) Overlap not in the first cluster -> shorten this request and + * let the caller handle the rest in its next loop iteration. + * + * b) Real overlaps of two requests. Yield and restart the search + * for contiguous clusters (the situation could have changed + * while we were sleeping) + * + * c) TODO: Request starts in the same cluster as the in-flight + * allocation ends. Shorten the COW of the in-fight allocation, + * set cluster_offset to write to the same cluster and set up + * the right synchronisation between the in-flight request and + * the new one. + */ + ret = handle_dependencies(bs, start, &cur_bytes, m); + if (ret == -EAGAIN) { + /* Currently handle_dependencies() doesn't yield if we already had + * an allocation. If it did, we would have to clean up the L2Meta + * structs before starting over. */ + assert(*m == NULL); + goto again; + } else if (ret < 0) { + return ret; + } else if (cur_bytes == 0) { + break; + } else { + /* handle_dependencies() may have decreased cur_bytes (shortened + * the allocations below) so that the next dependency is processed + * correctly during the next loop iteration. */ + } + + /* + * 2. Count contiguous COPIED clusters. + */ + ret = handle_copied(bs, start, &cluster_offset, &cur_bytes, m); + if (ret < 0) { + return ret; + } else if (ret) { + continue; + } else if (cur_bytes == 0) { + break; + } + + /* + * 3. If the request still hasn't completed, allocate new clusters, + * considering any cluster_offset of steps 1c or 2. + */ + ret = handle_alloc(bs, start, &cluster_offset, &cur_bytes, m); + if (ret < 0) { + return ret; + } else if (ret) { + continue; + } else { + assert(cur_bytes == 0); + break; + } + } + + *num -= remaining >> BDRV_SECTOR_BITS; + assert(*num > 0); + assert(*host_offset != 0); + + return 0; +} + +static int decompress_buffer(uint8_t *out_buf, int out_buf_size, + const uint8_t *buf, int buf_size) +{ + z_stream strm1, *strm = &strm1; + int ret, out_len; + + memset(strm, 0, sizeof(*strm)); + + strm->next_in = (uint8_t *)buf; + strm->avail_in = buf_size; + strm->next_out = out_buf; + strm->avail_out = out_buf_size; + + ret = inflateInit2(strm, -12); + if (ret != Z_OK) + return -1; + ret = inflate(strm, Z_FINISH); + out_len = strm->next_out - out_buf; + if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || + out_len != out_buf_size) { + inflateEnd(strm); + return -1; + } + inflateEnd(strm); + return 0; +} + +int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) +{ + BDRVQcowState *s = bs->opaque; + int ret, csize, nb_csectors, sector_offset; + uint64_t coffset; + + coffset = cluster_offset & s->cluster_offset_mask; + if (s->cluster_cache_offset != coffset) { + nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; + sector_offset = coffset & 511; + csize = nb_csectors * 512 - sector_offset; + BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); + ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); + if (ret < 0) { + return ret; + } + if (decompress_buffer(s->cluster_cache, s->cluster_size, + s->cluster_data + sector_offset, csize) < 0) { + return -EIO; + } + s->cluster_cache_offset = coffset; + } + return 0; +} + +/* + * This discards as many clusters of nb_clusters as possible at once (i.e. + * all clusters in the same L2 table) and returns the number of discarded + * clusters. + */ +static int discard_single_l2(BlockDriverState *bs, uint64_t offset, + unsigned int nb_clusters, enum qcow2_discard_type type, bool full_discard) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l2_table; + int l2_index; + int ret; + int i; + + ret = get_cluster_table(bs, offset, &l2_table, &l2_index); + if (ret < 0) { + return ret; + } + + /* Limit nb_clusters to one L2 table */ + nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + + for (i = 0; i < nb_clusters; i++) { + uint64_t old_l2_entry; + + old_l2_entry = be64_to_cpu(l2_table[l2_index + i]); + + /* + * If full_discard is false, make sure that a discarded area reads back + * as zeroes for v3 images (we cannot do it for v2 without actually + * writing a zero-filled buffer). We can skip the operation if the + * cluster is already marked as zero, or if it's unallocated and we + * don't have a backing file. + * + * TODO We might want to use bdrv_get_block_status(bs) here, but we're + * holding s->lock, so that doesn't work today. + * + * If full_discard is true, the sector should not read back as zeroes, + * but rather fall through to the backing file. + */ + switch (qcow2_get_cluster_type(old_l2_entry)) { + case QCOW2_CLUSTER_UNALLOCATED: + if (full_discard || !bs->backing_hd) { + continue; + } + break; + + case QCOW2_CLUSTER_ZERO: + if (!full_discard) { + continue; + } + break; + + case QCOW2_CLUSTER_NORMAL: + case QCOW2_CLUSTER_COMPRESSED: + break; + + default: + abort(); + } + + /* First remove L2 entries */ + qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + if (!full_discard && s->qcow_version >= 3) { + l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); + } else { + l2_table[l2_index + i] = cpu_to_be64(0); + } + + /* Then decrease the refcount */ + qcow2_free_any_clusters(bs, old_l2_entry, 1, type); + } + + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + + return nb_clusters; +} + +int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, + int nb_sectors, enum qcow2_discard_type type, bool full_discard) +{ + BDRVQcowState *s = bs->opaque; + uint64_t end_offset; + unsigned int nb_clusters; + int ret; + + end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); + + /* Round start up and end down */ + offset = align_offset(offset, s->cluster_size); + end_offset = start_of_cluster(s, end_offset); + + if (offset > end_offset) { + return 0; + } + + nb_clusters = size_to_clusters(s, end_offset - offset); + + s->cache_discards = true; + + /* Each L2 table is handled by its own loop iteration */ + while (nb_clusters > 0) { + ret = discard_single_l2(bs, offset, nb_clusters, type, full_discard); + if (ret < 0) { + goto fail; + } + + nb_clusters -= ret; + offset += (ret * s->cluster_size); + } + + ret = 0; +fail: + s->cache_discards = false; + qcow2_process_discards(bs, ret); + + return ret; +} + +/* + * This zeroes as many clusters of nb_clusters as possible at once (i.e. + * all clusters in the same L2 table) and returns the number of zeroed + * clusters. + */ +static int zero_single_l2(BlockDriverState *bs, uint64_t offset, + unsigned int nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l2_table; + int l2_index; + int ret; + int i; + + ret = get_cluster_table(bs, offset, &l2_table, &l2_index); + if (ret < 0) { + return ret; + } + + /* Limit nb_clusters to one L2 table */ + nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + + for (i = 0; i < nb_clusters; i++) { + uint64_t old_offset; + + old_offset = be64_to_cpu(l2_table[l2_index + i]); + + /* Update L2 entries */ + qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + if (old_offset & QCOW_OFLAG_COMPRESSED) { + l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); + qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); + } else { + l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO); + } + } + + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + + return nb_clusters; +} + +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + unsigned int nb_clusters; + int ret; + + /* The zero flag is only supported by version 3 and newer */ + if (s->qcow_version < 3) { + return -ENOTSUP; + } + + /* Each L2 table is handled by its own loop iteration */ + nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS); + + s->cache_discards = true; + + while (nb_clusters > 0) { + ret = zero_single_l2(bs, offset, nb_clusters); + if (ret < 0) { + goto fail; + } + + nb_clusters -= ret; + offset += (ret * s->cluster_size); + } + + ret = 0; +fail: + s->cache_discards = false; + qcow2_process_discards(bs, ret); + + return ret; +} + +/* + * Expands all zero clusters in a specific L1 table (or deallocates them, for + * non-backed non-pre-allocated zero clusters). + * + * l1_entries and *visited_l1_entries are used to keep track of progress for + * status_cb(). l1_entries contains the total number of L1 entries and + * *visited_l1_entries counts all visited L1 entries. + */ +static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, + int l1_size, int64_t *visited_l1_entries, + int64_t l1_entries, + BlockDriverAmendStatusCB *status_cb) +{ + BDRVQcowState *s = bs->opaque; + bool is_active_l1 = (l1_table == s->l1_table); + uint64_t *l2_table = NULL; + int ret; + int i, j; + + if (!is_active_l1) { + /* inactive L2 tables require a buffer to be stored in when loading + * them from disk */ + l2_table = qemu_try_blockalign(bs->file, s->cluster_size); + if (l2_table == NULL) { + return -ENOMEM; + } + } + + for (i = 0; i < l1_size; i++) { + uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; + bool l2_dirty = false; + uint64_t l2_refcount; + + if (!l2_offset) { + /* unallocated */ + (*visited_l1_entries)++; + if (status_cb) { + status_cb(bs, *visited_l1_entries, l1_entries); + } + continue; + } + + if (offset_into_cluster(s, l2_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" + PRIx64 " unaligned (L1 index: %#x)", + l2_offset, i); + ret = -EIO; + goto fail; + } + + if (is_active_l1) { + /* get active L2 tables from cache */ + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, + (void **)&l2_table); + } else { + /* load inactive L2 tables from disk */ + ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, + (void *)l2_table, s->cluster_sectors); + } + if (ret < 0) { + goto fail; + } + + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &l2_refcount); + if (ret < 0) { + goto fail; + } + + for (j = 0; j < s->l2_size; j++) { + uint64_t l2_entry = be64_to_cpu(l2_table[j]); + int64_t offset = l2_entry & L2E_OFFSET_MASK; + int cluster_type = qcow2_get_cluster_type(l2_entry); + bool preallocated = offset != 0; + + if (cluster_type != QCOW2_CLUSTER_ZERO) { + continue; + } + + if (!preallocated) { + if (!bs->backing_hd) { + /* not backed; therefore we can simply deallocate the + * cluster */ + l2_table[j] = 0; + l2_dirty = true; + continue; + } + + offset = qcow2_alloc_clusters(bs, s->cluster_size); + if (offset < 0) { + ret = offset; + goto fail; + } + + if (l2_refcount > 1) { + /* For shared L2 tables, set the refcount accordingly (it is + * already 1 and needs to be l2_refcount) */ + ret = qcow2_update_cluster_refcount(bs, + offset >> s->cluster_bits, + refcount_diff(1, l2_refcount), false, + QCOW2_DISCARD_OTHER); + if (ret < 0) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_OTHER); + goto fail; + } + } + } + + if (offset_into_cluster(s, offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset " + "%#" PRIx64 " unaligned (L2 offset: %#" + PRIx64 ", L2 index: %#x)", offset, + l2_offset, j); + if (!preallocated) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + ret = -EIO; + goto fail; + } + + ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); + if (ret < 0) { + if (!preallocated) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + goto fail; + } + + ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE, + s->cluster_sectors, 0); + if (ret < 0) { + if (!preallocated) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + goto fail; + } + + if (l2_refcount == 1) { + l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); + } else { + l2_table[j] = cpu_to_be64(offset); + } + l2_dirty = true; + } + + if (is_active_l1) { + if (l2_dirty) { + qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + qcow2_cache_depends_on_flush(s->l2_table_cache); + } + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + } else { + if (l2_dirty) { + ret = qcow2_pre_write_overlap_check(bs, + QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, l2_offset, + s->cluster_size); + if (ret < 0) { + goto fail; + } + + ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, + (void *)l2_table, s->cluster_sectors); + if (ret < 0) { + goto fail; + } + } + } + + (*visited_l1_entries)++; + if (status_cb) { + status_cb(bs, *visited_l1_entries, l1_entries); + } + } + + ret = 0; + +fail: + if (l2_table) { + if (!is_active_l1) { + qemu_vfree(l2_table); + } else { + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + } + } + return ret; +} + +/* + * For backed images, expands all zero clusters on the image. For non-backed + * images, deallocates all non-pre-allocated zero clusters (and claims the + * allocation for pre-allocated ones). This is important for downgrading to a + * qcow2 version which doesn't yet support metadata zero clusters. + */ +int qcow2_expand_zero_clusters(BlockDriverState *bs, + BlockDriverAmendStatusCB *status_cb) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l1_table = NULL; + int64_t l1_entries = 0, visited_l1_entries = 0; + int ret; + int i, j; + + if (status_cb) { + l1_entries = s->l1_size; + for (i = 0; i < s->nb_snapshots; i++) { + l1_entries += s->snapshots[i].l1_size; + } + } + + ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, + &visited_l1_entries, l1_entries, + status_cb); + if (ret < 0) { + goto fail; + } + + /* Inactive L1 tables may point to active L2 tables - therefore it is + * necessary to flush the L2 table cache before trying to access the L2 + * tables pointed to by inactive L1 entries (else we might try to expand + * zero clusters that have already been expanded); furthermore, it is also + * necessary to empty the L2 table cache, since it may contain tables which + * are now going to be modified directly on disk, bypassing the cache. + * qcow2_cache_empty() does both for us. */ + ret = qcow2_cache_empty(bs, s->l2_table_cache); + if (ret < 0) { + goto fail; + } + + for (i = 0; i < s->nb_snapshots; i++) { + int l1_sectors = (s->snapshots[i].l1_size * sizeof(uint64_t) + + BDRV_SECTOR_SIZE - 1) / BDRV_SECTOR_SIZE; + + l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); + + ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset / + BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors); + if (ret < 0) { + goto fail; + } + + for (j = 0; j < s->snapshots[i].l1_size; j++) { + be64_to_cpus(&l1_table[j]); + } + + ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size, + &visited_l1_entries, l1_entries, + status_cb); + if (ret < 0) { + goto fail; + } + } + + ret = 0; + +fail: + g_free(l1_table); + return ret; +} diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c new file mode 100644 index 0000000..0632fc3 --- /dev/null +++ b/block/qcow2-refcount.c @@ -0,0 +1,2451 @@ +/* + * Block driver for the QCOW version 2 format + * + * Copyright (c) 2004-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "block/qcow2.h" +#include "qemu/range.h" + +static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); +static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, + int64_t offset, int64_t length, uint64_t addend, + bool decrease, enum qcow2_discard_type type); + +static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index); + +static void set_refcount_ro0(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro1(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro2(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro3(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro4(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro5(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro6(void *refcount_array, uint64_t index, + uint64_t value); + + +static Qcow2GetRefcountFunc *const get_refcount_funcs[] = { + &get_refcount_ro0, + &get_refcount_ro1, + &get_refcount_ro2, + &get_refcount_ro3, + &get_refcount_ro4, + &get_refcount_ro5, + &get_refcount_ro6 +}; + +static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { + &set_refcount_ro0, + &set_refcount_ro1, + &set_refcount_ro2, + &set_refcount_ro3, + &set_refcount_ro4, + &set_refcount_ro5, + &set_refcount_ro6 +}; + + +/*********************************************************/ +/* refcount handling */ + +int qcow2_refcount_init(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + unsigned int refcount_table_size2, i; + int ret; + + assert(s->refcount_order >= 0 && s->refcount_order <= 6); + + s->get_refcount = get_refcount_funcs[s->refcount_order]; + s->set_refcount = set_refcount_funcs[s->refcount_order]; + + assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t)); + refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); + s->refcount_table = g_try_malloc(refcount_table_size2); + + if (s->refcount_table_size > 0) { + if (s->refcount_table == NULL) { + ret = -ENOMEM; + goto fail; + } + BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); + ret = bdrv_pread(bs->file, s->refcount_table_offset, + s->refcount_table, refcount_table_size2); + if (ret < 0) { + goto fail; + } + for(i = 0; i < s->refcount_table_size; i++) + be64_to_cpus(&s->refcount_table[i]); + } + return 0; + fail: + return ret; +} + +void qcow2_refcount_close(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + g_free(s->refcount_table); +} + + +static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 8] >> (index % 8)) & 0x1; +} + +static void set_refcount_ro0(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 1)); + ((uint8_t *)refcount_array)[index / 8] &= ~(0x1 << (index % 8)); + ((uint8_t *)refcount_array)[index / 8] |= value << (index % 8); +} + +static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 4] >> (2 * (index % 4))) + & 0x3; +} + +static void set_refcount_ro1(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 2)); + ((uint8_t *)refcount_array)[index / 4] &= ~(0x3 << (2 * (index % 4))); + ((uint8_t *)refcount_array)[index / 4] |= value << (2 * (index % 4)); +} + +static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 2] >> (4 * (index % 2))) + & 0xf; +} + +static void set_refcount_ro2(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 4)); + ((uint8_t *)refcount_array)[index / 2] &= ~(0xf << (4 * (index % 2))); + ((uint8_t *)refcount_array)[index / 2] |= value << (4 * (index % 2)); +} + +static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index) +{ + return ((const uint8_t *)refcount_array)[index]; +} + +static void set_refcount_ro3(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 8)); + ((uint8_t *)refcount_array)[index] = value; +} + +static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index) +{ + return be16_to_cpu(((const uint16_t *)refcount_array)[index]); +} + +static void set_refcount_ro4(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 16)); + ((uint16_t *)refcount_array)[index] = cpu_to_be16(value); +} + +static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index) +{ + return be32_to_cpu(((const uint32_t *)refcount_array)[index]); +} + +static void set_refcount_ro5(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 32)); + ((uint32_t *)refcount_array)[index] = cpu_to_be32(value); +} + +static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index) +{ + return be64_to_cpu(((const uint64_t *)refcount_array)[index]); +} + +static void set_refcount_ro6(void *refcount_array, uint64_t index, + uint64_t value) +{ + ((uint64_t *)refcount_array)[index] = cpu_to_be64(value); +} + + +static int load_refcount_block(BlockDriverState *bs, + int64_t refcount_block_offset, + void **refcount_block) +{ + BDRVQcowState *s = bs->opaque; + int ret; + + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); + ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, + refcount_block); + + return ret; +} + +/* + * Retrieves the refcount of the cluster given by its index and stores it in + * *refcount. Returns 0 on success and -errno on failure. + */ +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount) +{ + BDRVQcowState *s = bs->opaque; + uint64_t refcount_table_index, block_index; + int64_t refcount_block_offset; + int ret; + void *refcount_block; + + refcount_table_index = cluster_index >> s->refcount_block_bits; + if (refcount_table_index >= s->refcount_table_size) { + *refcount = 0; + return 0; + } + refcount_block_offset = + s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; + if (!refcount_block_offset) { + *refcount = 0; + return 0; + } + + if (offset_into_cluster(s, refcount_block_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64 + " unaligned (reftable index: %#" PRIx64 ")", + refcount_block_offset, refcount_table_index); + return -EIO; + } + + ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, + &refcount_block); + if (ret < 0) { + return ret; + } + + block_index = cluster_index & (s->refcount_block_size - 1); + *refcount = s->get_refcount(refcount_block, block_index); + + qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); + + return 0; +} + +/* + * Rounds the refcount table size up to avoid growing the table for each single + * refcount block that is allocated. + */ +static unsigned int next_refcount_table_size(BDRVQcowState *s, + unsigned int min_size) +{ + unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1; + unsigned int refcount_table_clusters = + MAX(1, s->refcount_table_size >> (s->cluster_bits - 3)); + + while (min_clusters > refcount_table_clusters) { + refcount_table_clusters = (refcount_table_clusters * 3 + 1) / 2; + } + + return refcount_table_clusters << (s->cluster_bits - 3); +} + + +/* Checks if two offsets are described by the same refcount block */ +static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, + uint64_t offset_b) +{ + uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits); + uint64_t block_b = offset_b >> (s->cluster_bits + s->refcount_block_bits); + + return (block_a == block_b); +} + +/* + * Loads a refcount block. If it doesn't exist yet, it is allocated first + * (including growing the refcount table if needed). + * + * Returns 0 on success or -errno in error case + */ +static int alloc_refcount_block(BlockDriverState *bs, + int64_t cluster_index, void **refcount_block) +{ + BDRVQcowState *s = bs->opaque; + unsigned int refcount_table_index; + int ret; + + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC); + + /* Find the refcount block for the given cluster */ + refcount_table_index = cluster_index >> s->refcount_block_bits; + + if (refcount_table_index < s->refcount_table_size) { + + uint64_t refcount_block_offset = + s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; + + /* If it's already there, we're done */ + if (refcount_block_offset) { + if (offset_into_cluster(s, refcount_block_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" + PRIx64 " unaligned (reftable index: " + "%#x)", refcount_block_offset, + refcount_table_index); + return -EIO; + } + + return load_refcount_block(bs, refcount_block_offset, + refcount_block); + } + } + + /* + * If we came here, we need to allocate something. Something is at least + * a cluster for the new refcount block. It may also include a new refcount + * table if the old refcount table is too small. + * + * Note that allocating clusters here needs some special care: + * + * - We can't use the normal qcow2_alloc_clusters(), it would try to + * increase the refcount and very likely we would end up with an endless + * recursion. Instead we must place the refcount blocks in a way that + * they can describe them themselves. + * + * - We need to consider that at this point we are inside update_refcounts + * and potentially doing an initial refcount increase. This means that + * some clusters have already been allocated by the caller, but their + * refcount isn't accurate yet. If we allocate clusters for metadata, we + * need to return -EAGAIN to signal the caller that it needs to restart + * the search for free clusters. + * + * - alloc_clusters_noref and qcow2_free_clusters may load a different + * refcount block into the cache + */ + + *refcount_block = NULL; + + /* We write to the refcount table, so we might depend on L2 tables */ + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret < 0) { + return ret; + } + + /* Allocate the refcount block itself and mark it as used */ + int64_t new_block = alloc_clusters_noref(bs, s->cluster_size); + if (new_block < 0) { + return new_block; + } + +#ifdef DEBUG_ALLOC2 + fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64 + " at %" PRIx64 "\n", + refcount_table_index, cluster_index << s->cluster_bits, new_block); +#endif + + if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { + /* Zero the new refcount block before updating it */ + ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, + refcount_block); + if (ret < 0) { + goto fail_block; + } + + memset(*refcount_block, 0, s->cluster_size); + + /* The block describes itself, need to update the cache */ + int block_index = (new_block >> s->cluster_bits) & + (s->refcount_block_size - 1); + s->set_refcount(*refcount_block, block_index, 1); + } else { + /* Described somewhere else. This can recurse at most twice before we + * arrive at a block that describes itself. */ + ret = update_refcount(bs, new_block, s->cluster_size, 1, false, + QCOW2_DISCARD_NEVER); + if (ret < 0) { + goto fail_block; + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail_block; + } + + /* Initialize the new refcount block only after updating its refcount, + * update_refcount uses the refcount cache itself */ + ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, + refcount_block); + if (ret < 0) { + goto fail_block; + } + + memset(*refcount_block, 0, s->cluster_size); + } + + /* Now the new refcount block needs to be written to disk */ + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); + qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, *refcount_block); + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail_block; + } + + /* If the refcount table is big enough, just hook the block up there */ + if (refcount_table_index < s->refcount_table_size) { + uint64_t data64 = cpu_to_be64(new_block); + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); + ret = bdrv_pwrite_sync(bs->file, + s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), + &data64, sizeof(data64)); + if (ret < 0) { + goto fail_block; + } + + s->refcount_table[refcount_table_index] = new_block; + + /* The new refcount block may be where the caller intended to put its + * data, so let it restart the search. */ + return -EAGAIN; + } + + qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); + + /* + * If we come here, we need to grow the refcount table. Again, a new + * refcount table needs some space and we can't simply allocate to avoid + * endless recursion. + * + * Therefore let's grab new refcount blocks at the end of the image, which + * will describe themselves and the new refcount table. This way we can + * reference them only in the new table and do the switch to the new + * refcount table at once without producing an inconsistent state in + * between. + */ + BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW); + + /* Calculate the number of refcount blocks needed so far; this will be the + * basis for calculating the index of the first cluster used for the + * self-describing refcount structures which we are about to create. + * + * Because we reached this point, there cannot be any refcount entries for + * cluster_index or higher indices yet. However, because new_block has been + * allocated to describe that cluster (and it will assume this role later + * on), we cannot use that index; also, new_block may actually have a higher + * cluster index than cluster_index, so it needs to be taken into account + * here (and 1 needs to be added to its value because that cluster is used). + */ + uint64_t blocks_used = DIV_ROUND_UP(MAX(cluster_index + 1, + (new_block >> s->cluster_bits) + 1), + s->refcount_block_size); + + if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) { + return -EFBIG; + } + + /* And now we need at least one block more for the new metadata */ + uint64_t table_size = next_refcount_table_size(s, blocks_used + 1); + uint64_t last_table_size; + uint64_t blocks_clusters; + do { + uint64_t table_clusters = + size_to_clusters(s, table_size * sizeof(uint64_t)); + blocks_clusters = 1 + + ((table_clusters + s->refcount_block_size - 1) + / s->refcount_block_size); + uint64_t meta_clusters = table_clusters + blocks_clusters; + + last_table_size = table_size; + table_size = next_refcount_table_size(s, blocks_used + + ((meta_clusters + s->refcount_block_size - 1) + / s->refcount_block_size)); + + } while (last_table_size != table_size); + +#ifdef DEBUG_ALLOC2 + fprintf(stderr, "qcow2: Grow refcount table %" PRId32 " => %" PRId64 "\n", + s->refcount_table_size, table_size); +#endif + + /* Create the new refcount table and blocks */ + uint64_t meta_offset = (blocks_used * s->refcount_block_size) * + s->cluster_size; + uint64_t table_offset = meta_offset + blocks_clusters * s->cluster_size; + uint64_t *new_table = g_try_new0(uint64_t, table_size); + void *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size); + + assert(table_size > 0 && blocks_clusters > 0); + if (new_table == NULL || new_blocks == NULL) { + ret = -ENOMEM; + goto fail_table; + } + + /* Fill the new refcount table */ + memcpy(new_table, s->refcount_table, + s->refcount_table_size * sizeof(uint64_t)); + new_table[refcount_table_index] = new_block; + + int i; + for (i = 0; i < blocks_clusters; i++) { + new_table[blocks_used + i] = meta_offset + (i * s->cluster_size); + } + + /* Fill the refcount blocks */ + uint64_t table_clusters = size_to_clusters(s, table_size * sizeof(uint64_t)); + int block = 0; + for (i = 0; i < table_clusters + blocks_clusters; i++) { + s->set_refcount(new_blocks, block++, 1); + } + + /* Write refcount blocks to disk */ + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS); + ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks, + blocks_clusters * s->cluster_size); + g_free(new_blocks); + new_blocks = NULL; + if (ret < 0) { + goto fail_table; + } + + /* Write refcount table to disk */ + for(i = 0; i < table_size; i++) { + cpu_to_be64s(&new_table[i]); + } + + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); + ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, + table_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail_table; + } + + for(i = 0; i < table_size; i++) { + be64_to_cpus(&new_table[i]); + } + + /* Hook up the new refcount table in the qcow2 header */ + uint8_t data[12]; + cpu_to_be64w((uint64_t*)data, table_offset); + cpu_to_be32w((uint32_t*)(data + 8), table_clusters); + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), + data, sizeof(data)); + if (ret < 0) { + goto fail_table; + } + + /* And switch it in memory */ + uint64_t old_table_offset = s->refcount_table_offset; + uint64_t old_table_size = s->refcount_table_size; + + g_free(s->refcount_table); + s->refcount_table = new_table; + s->refcount_table_size = table_size; + s->refcount_table_offset = table_offset; + + /* Free old table. */ + qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t), + QCOW2_DISCARD_OTHER); + + ret = load_refcount_block(bs, new_block, refcount_block); + if (ret < 0) { + return ret; + } + + /* If we were trying to do the initial refcount update for some cluster + * allocation, we might have used the same clusters to store newly + * allocated metadata. Make the caller search some new space. */ + return -EAGAIN; + +fail_table: + g_free(new_blocks); + g_free(new_table); +fail_block: + if (*refcount_block != NULL) { + qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); + } + return ret; +} + +void qcow2_process_discards(BlockDriverState *bs, int ret) +{ + BDRVQcowState *s = bs->opaque; + Qcow2DiscardRegion *d, *next; + + QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) { + QTAILQ_REMOVE(&s->discards, d, next); + + /* Discard is optional, ignore the return value */ + if (ret >= 0) { + bdrv_discard(bs->file, + d->offset >> BDRV_SECTOR_BITS, + d->bytes >> BDRV_SECTOR_BITS); + } + + g_free(d); + } +} + +static void update_refcount_discard(BlockDriverState *bs, + uint64_t offset, uint64_t length) +{ + BDRVQcowState *s = bs->opaque; + Qcow2DiscardRegion *d, *p, *next; + + QTAILQ_FOREACH(d, &s->discards, next) { + uint64_t new_start = MIN(offset, d->offset); + uint64_t new_end = MAX(offset + length, d->offset + d->bytes); + + if (new_end - new_start <= length + d->bytes) { + /* There can't be any overlap, areas ending up here have no + * references any more and therefore shouldn't get freed another + * time. */ + assert(d->bytes + length == new_end - new_start); + d->offset = new_start; + d->bytes = new_end - new_start; + goto found; + } + } + + d = g_malloc(sizeof(*d)); + *d = (Qcow2DiscardRegion) { + .bs = bs, + .offset = offset, + .bytes = length, + }; + QTAILQ_INSERT_TAIL(&s->discards, d, next); + +found: + /* Merge discard requests if they are adjacent now */ + QTAILQ_FOREACH_SAFE(p, &s->discards, next, next) { + if (p == d + || p->offset > d->offset + d->bytes + || d->offset > p->offset + p->bytes) + { + continue; + } + + /* Still no overlap possible */ + assert(p->offset == d->offset + d->bytes + || d->offset == p->offset + p->bytes); + + QTAILQ_REMOVE(&s->discards, p, next); + d->offset = MIN(d->offset, p->offset); + d->bytes += p->bytes; + g_free(p); + } +} + +/* XXX: cache several refcount block clusters ? */ +/* @addend is the absolute value of the addend; if @decrease is set, @addend + * will be subtracted from the current refcount, otherwise it will be added */ +static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, + int64_t offset, + int64_t length, + uint64_t addend, + bool decrease, + enum qcow2_discard_type type) +{ + BDRVQcowState *s = bs->opaque; + int64_t start, last, cluster_offset; + void *refcount_block = NULL; + int64_t old_table_index = -1; + int ret; + +#ifdef DEBUG_ALLOC2 + fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 + " addend=%s%" PRIu64 "\n", offset, length, decrease ? "-" : "", + addend); +#endif + if (length < 0) { + return -EINVAL; + } else if (length == 0) { + return 0; + } + + if (decrease) { + qcow2_cache_set_dependency(bs, s->refcount_block_cache, + s->l2_table_cache); + } + + start = start_of_cluster(s, offset); + last = start_of_cluster(s, offset + length - 1); + for(cluster_offset = start; cluster_offset <= last; + cluster_offset += s->cluster_size) + { + int block_index; + uint64_t refcount; + int64_t cluster_index = cluster_offset >> s->cluster_bits; + int64_t table_index = cluster_index >> s->refcount_block_bits; + + /* Load the refcount block and allocate it if needed */ + if (table_index != old_table_index) { + if (refcount_block) { + qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); + } + ret = alloc_refcount_block(bs, cluster_index, &refcount_block); + if (ret < 0) { + goto fail; + } + } + old_table_index = table_index; + + qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, + refcount_block); + + /* we can update the count and save it */ + block_index = cluster_index & (s->refcount_block_size - 1); + + refcount = s->get_refcount(refcount_block, block_index); + if (decrease ? (refcount - addend > refcount) + : (refcount + addend < refcount || + refcount + addend > s->refcount_max)) + { + ret = -EINVAL; + goto fail; + } + if (decrease) { + refcount -= addend; + } else { + refcount += addend; + } + if (refcount == 0 && cluster_index < s->free_cluster_index) { + s->free_cluster_index = cluster_index; + } + s->set_refcount(refcount_block, block_index, refcount); + + if (refcount == 0 && s->discard_passthrough[type]) { + update_refcount_discard(bs, cluster_offset, s->cluster_size); + } + } + + ret = 0; +fail: + if (!s->cache_discards) { + qcow2_process_discards(bs, ret); + } + + /* Write last changed block to disk */ + if (refcount_block) { + qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); + } + + /* + * Try do undo any updates if an error is returned (This may succeed in + * some cases like ENOSPC for allocating a new refcount block) + */ + if (ret < 0) { + int dummy; + dummy = update_refcount(bs, offset, cluster_offset - offset, addend, + !decrease, QCOW2_DISCARD_NEVER); + (void)dummy; + } + + return ret; +} + +/* + * Increases or decreases the refcount of a given cluster. + * + * @addend is the absolute value of the addend; if @decrease is set, @addend + * will be subtracted from the current refcount, otherwise it will be added. + * + * On success 0 is returned; on failure -errno is returned. + */ +int qcow2_update_cluster_refcount(BlockDriverState *bs, + int64_t cluster_index, + uint64_t addend, bool decrease, + enum qcow2_discard_type type) +{ + BDRVQcowState *s = bs->opaque; + int ret; + + ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, + decrease, type); + if (ret < 0) { + return ret; + } + + return 0; +} + + + +/*********************************************************/ +/* cluster allocation functions */ + + + +/* return < 0 if error */ +static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) +{ + BDRVQcowState *s = bs->opaque; + uint64_t i, nb_clusters, refcount; + int ret; + + /* We can't allocate clusters if they may still be queued for discard. */ + if (s->cache_discards) { + qcow2_process_discards(bs, 0); + } + + nb_clusters = size_to_clusters(s, size); +retry: + for(i = 0; i < nb_clusters; i++) { + uint64_t next_cluster_index = s->free_cluster_index++; + ret = qcow2_get_refcount(bs, next_cluster_index, &refcount); + + if (ret < 0) { + return ret; + } else if (refcount != 0) { + goto retry; + } + } + + /* Make sure that all offsets in the "allocated" range are representable + * in an int64_t */ + if (s->free_cluster_index > 0 && + s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) + { + return -EFBIG; + } + +#ifdef DEBUG_ALLOC2 + fprintf(stderr, "alloc_clusters: size=%" PRId64 " -> %" PRId64 "\n", + size, + (s->free_cluster_index - nb_clusters) << s->cluster_bits); +#endif + return (s->free_cluster_index - nb_clusters) << s->cluster_bits; +} + +int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) +{ + int64_t offset; + int ret; + + BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); + do { + offset = alloc_clusters_noref(bs, size); + if (offset < 0) { + return offset; + } + + ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); + } while (ret == -EAGAIN); + + if (ret < 0) { + return ret; + } + + return offset; +} + +int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, + int nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + uint64_t cluster_index, refcount; + uint64_t i; + int ret; + + assert(nb_clusters >= 0); + if (nb_clusters == 0) { + return 0; + } + + do { + /* Check how many clusters there are free */ + cluster_index = offset >> s->cluster_bits; + for(i = 0; i < nb_clusters; i++) { + ret = qcow2_get_refcount(bs, cluster_index++, &refcount); + if (ret < 0) { + return ret; + } else if (refcount != 0) { + break; + } + } + + /* And then allocate them */ + ret = update_refcount(bs, offset, i << s->cluster_bits, 1, false, + QCOW2_DISCARD_NEVER); + } while (ret == -EAGAIN); + + if (ret < 0) { + return ret; + } + + return i; +} + +/* only used to allocate compressed sectors. We try to allocate + contiguous sectors. size must be <= cluster_size */ +int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) +{ + BDRVQcowState *s = bs->opaque; + int64_t offset; + size_t free_in_cluster; + int ret; + + BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES); + assert(size > 0 && size <= s->cluster_size); + assert(!s->free_byte_offset || offset_into_cluster(s, s->free_byte_offset)); + + offset = s->free_byte_offset; + + if (offset) { + uint64_t refcount; + ret = qcow2_get_refcount(bs, offset >> s->cluster_bits, &refcount); + if (ret < 0) { + return ret; + } + + if (refcount == s->refcount_max) { + offset = 0; + } + } + + free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); + if (!offset || free_in_cluster < size) { + int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size); + if (new_cluster < 0) { + return new_cluster; + } + + if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) { + offset = new_cluster; + } + } + + assert(offset); + ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); + if (ret < 0) { + return ret; + } + + /* The cluster refcount was incremented; refcount blocks must be flushed + * before the caller's L2 table updates. */ + qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); + + s->free_byte_offset = offset + size; + if (!offset_into_cluster(s, s->free_byte_offset)) { + s->free_byte_offset = 0; + } + + return offset; +} + +void qcow2_free_clusters(BlockDriverState *bs, + int64_t offset, int64_t size, + enum qcow2_discard_type type) +{ + int ret; + + BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE); + ret = update_refcount(bs, offset, size, 1, true, type); + if (ret < 0) { + fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret)); + /* TODO Remember the clusters to free them later and avoid leaking */ + } +} + +/* + * Free a cluster using its L2 entry (handles clusters of all types, e.g. + * normal cluster, compressed cluster, etc.) + */ +void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, + int nb_clusters, enum qcow2_discard_type type) +{ + BDRVQcowState *s = bs->opaque; + + switch (qcow2_get_cluster_type(l2_entry)) { + case QCOW2_CLUSTER_COMPRESSED: + { + int nb_csectors; + nb_csectors = ((l2_entry >> s->csize_shift) & + s->csize_mask) + 1; + qcow2_free_clusters(bs, + (l2_entry & s->cluster_offset_mask) & ~511, + nb_csectors * 512, type); + } + break; + case QCOW2_CLUSTER_NORMAL: + case QCOW2_CLUSTER_ZERO: + if (l2_entry & L2E_OFFSET_MASK) { + if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) { + qcow2_signal_corruption(bs, false, -1, -1, + "Cannot free unaligned cluster %#llx", + l2_entry & L2E_OFFSET_MASK); + } else { + qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK, + nb_clusters << s->cluster_bits, type); + } + } + break; + case QCOW2_CLUSTER_UNALLOCATED: + break; + default: + abort(); + } +} + + + +/*********************************************************/ +/* snapshots and image creation */ + + + +/* update the refcounts of snapshots and the copied flag */ +int qcow2_update_snapshot_refcount(BlockDriverState *bs, + int64_t l1_table_offset, int l1_size, int addend) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount; + bool l1_allocated = false; + int64_t old_offset, old_l2_offset; + int i, j, l1_modified = 0, nb_csectors; + int ret; + + assert(addend >= -1 && addend <= 1); + + l2_table = NULL; + l1_table = NULL; + l1_size2 = l1_size * sizeof(uint64_t); + + s->cache_discards = true; + + /* WARNING: qcow2_snapshot_goto relies on this function not using the + * l1_table_offset when it is the current s->l1_table_offset! Be careful + * when changing this! */ + if (l1_table_offset != s->l1_table_offset) { + l1_table = g_try_malloc0(align_offset(l1_size2, 512)); + if (l1_size2 && l1_table == NULL) { + ret = -ENOMEM; + goto fail; + } + l1_allocated = true; + + ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); + if (ret < 0) { + goto fail; + } + + for(i = 0;i < l1_size; i++) + be64_to_cpus(&l1_table[i]); + } else { + assert(l1_size == s->l1_size); + l1_table = s->l1_table; + l1_allocated = false; + } + + for(i = 0; i < l1_size; i++) { + l2_offset = l1_table[i]; + if (l2_offset) { + old_l2_offset = l2_offset; + l2_offset &= L1E_OFFSET_MASK; + + if (offset_into_cluster(s, l2_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" + PRIx64 " unaligned (L1 index: %#x)", + l2_offset, i); + ret = -EIO; + goto fail; + } + + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, + (void**) &l2_table); + if (ret < 0) { + goto fail; + } + + for(j = 0; j < s->l2_size; j++) { + uint64_t cluster_index; + + offset = be64_to_cpu(l2_table[j]); + old_offset = offset; + offset &= ~QCOW_OFLAG_COPIED; + + switch (qcow2_get_cluster_type(offset)) { + case QCOW2_CLUSTER_COMPRESSED: + nb_csectors = ((offset >> s->csize_shift) & + s->csize_mask) + 1; + if (addend != 0) { + ret = update_refcount(bs, + (offset & s->cluster_offset_mask) & ~511, + nb_csectors * 512, abs(addend), addend < 0, + QCOW2_DISCARD_SNAPSHOT); + if (ret < 0) { + goto fail; + } + } + /* compressed clusters are never modified */ + refcount = 2; + break; + + case QCOW2_CLUSTER_NORMAL: + case QCOW2_CLUSTER_ZERO: + if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) { + qcow2_signal_corruption(bs, true, -1, -1, "Data " + "cluster offset %#llx " + "unaligned (L2 offset: %#" + PRIx64 ", L2 index: %#x)", + offset & L2E_OFFSET_MASK, + l2_offset, j); + ret = -EIO; + goto fail; + } + + cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits; + if (!cluster_index) { + /* unallocated */ + refcount = 0; + break; + } + if (addend != 0) { + ret = qcow2_update_cluster_refcount(bs, + cluster_index, abs(addend), addend < 0, + QCOW2_DISCARD_SNAPSHOT); + if (ret < 0) { + goto fail; + } + } + + ret = qcow2_get_refcount(bs, cluster_index, &refcount); + if (ret < 0) { + goto fail; + } + break; + + case QCOW2_CLUSTER_UNALLOCATED: + refcount = 0; + break; + + default: + abort(); + } + + if (refcount == 1) { + offset |= QCOW_OFLAG_COPIED; + } + if (offset != old_offset) { + if (addend > 0) { + qcow2_cache_set_dependency(bs, s->l2_table_cache, + s->refcount_block_cache); + } + l2_table[j] = cpu_to_be64(offset); + qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, + l2_table); + } + } + + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + + if (addend != 0) { + ret = qcow2_update_cluster_refcount(bs, l2_offset >> + s->cluster_bits, + abs(addend), addend < 0, + QCOW2_DISCARD_SNAPSHOT); + if (ret < 0) { + goto fail; + } + } + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { + goto fail; + } else if (refcount == 1) { + l2_offset |= QCOW_OFLAG_COPIED; + } + if (l2_offset != old_l2_offset) { + l1_table[i] = l2_offset; + l1_modified = 1; + } + } + } + + ret = bdrv_flush(bs); +fail: + if (l2_table) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + } + + s->cache_discards = false; + qcow2_process_discards(bs, ret); + + /* Update L1 only if it isn't deleted anyway (addend = -1) */ + if (ret == 0 && addend >= 0 && l1_modified) { + for (i = 0; i < l1_size; i++) { + cpu_to_be64s(&l1_table[i]); + } + + ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table, l1_size2); + + for (i = 0; i < l1_size; i++) { + be64_to_cpus(&l1_table[i]); + } + } + if (l1_allocated) + g_free(l1_table); + return ret; +} + + + + +/*********************************************************/ +/* refcount checking functions */ + + +static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) +{ + /* This assertion holds because there is no way we can address more than + * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because + * offsets have to be representable in bytes); due to every cluster + * corresponding to one refcount entry, we are well below that limit */ + assert(entries < (UINT64_C(1) << (64 - 9))); + + /* Thanks to the assertion this will not overflow, because + * s->refcount_order < 7. + * (note: x << s->refcount_order == x * s->refcount_bits) */ + return DIV_ROUND_UP(entries << s->refcount_order, 8); +} + +/** + * Reallocates *array so that it can hold new_size entries. *size must contain + * the current number of entries in *array. If the reallocation fails, *array + * and *size will not be modified and -errno will be returned. If the + * reallocation is successful, *array will be set to the new buffer, *size + * will be set to new_size and 0 will be returned. The size of the reallocated + * refcount array buffer will be aligned to a cluster boundary, and the newly + * allocated area will be zeroed. + */ +static int realloc_refcount_array(BDRVQcowState *s, void **array, + int64_t *size, int64_t new_size) +{ + size_t old_byte_size, new_byte_size; + void *new_ptr; + + /* Round to clusters so the array can be directly written to disk */ + old_byte_size = size_to_clusters(s, refcount_array_byte_size(s, *size)) + * s->cluster_size; + new_byte_size = size_to_clusters(s, refcount_array_byte_size(s, new_size)) + * s->cluster_size; + + if (new_byte_size == old_byte_size) { + *size = new_size; + return 0; + } + + assert(new_byte_size > 0); + + new_ptr = g_try_realloc(*array, new_byte_size); + if (!new_ptr) { + return -ENOMEM; + } + + if (new_byte_size > old_byte_size) { + memset((void *)((uintptr_t)new_ptr + old_byte_size), 0, + new_byte_size - old_byte_size); + } + + *array = new_ptr; + *size = new_size; + + return 0; +} + +/* + * Increases the refcount for a range of clusters in a given refcount table. + * This is used to construct a temporary refcount table out of L1 and L2 tables + * which can be compared the the refcount table saved in the image. + * + * Modifies the number of errors in res. + */ +static int inc_refcounts(BlockDriverState *bs, + BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size, + int64_t offset, int64_t size) +{ + BDRVQcowState *s = bs->opaque; + uint64_t start, last, cluster_offset, k, refcount; + int ret; + + if (size <= 0) { + return 0; + } + + start = start_of_cluster(s, offset); + last = start_of_cluster(s, offset + size - 1); + for(cluster_offset = start; cluster_offset <= last; + cluster_offset += s->cluster_size) { + k = cluster_offset >> s->cluster_bits; + if (k >= *refcount_table_size) { + ret = realloc_refcount_array(s, refcount_table, + refcount_table_size, k + 1); + if (ret < 0) { + res->check_errors++; + return ret; + } + } + + refcount = s->get_refcount(*refcount_table, k); + if (refcount == s->refcount_max) { + fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 + "\n", cluster_offset); + res->corruptions++; + continue; + } + s->set_refcount(*refcount_table, k, refcount + 1); + } + + return 0; +} + +/* Flags for check_refcounts_l1() and check_refcounts_l2() */ +enum { + CHECK_FRAG_INFO = 0x2, /* update BlockFragInfo counters */ +}; + +/* + * Increases the refcount in the given refcount table for the all clusters + * referenced in the L2 table. While doing so, performs some checks on L2 + * entries. + * + * Returns the number of errors found by the checks or -errno if an internal + * error occurred. + */ +static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size, int64_t l2_offset, + int flags) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l2_table, l2_entry; + uint64_t next_contiguous_offset = 0; + int i, l2_size, nb_csectors, ret; + + /* Read L2 table from disk */ + l2_size = s->l2_size * sizeof(uint64_t); + l2_table = g_malloc(l2_size); + + ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size); + if (ret < 0) { + fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); + res->check_errors++; + goto fail; + } + + /* Do the actual checks */ + for(i = 0; i < s->l2_size; i++) { + l2_entry = be64_to_cpu(l2_table[i]); + + switch (qcow2_get_cluster_type(l2_entry)) { + case QCOW2_CLUSTER_COMPRESSED: + /* Compressed clusters don't have QCOW_OFLAG_COPIED */ + if (l2_entry & QCOW_OFLAG_COPIED) { + fprintf(stderr, "ERROR: cluster %" PRId64 ": " + "copied flag must never be set for compressed " + "clusters\n", l2_entry >> s->cluster_bits); + l2_entry &= ~QCOW_OFLAG_COPIED; + res->corruptions++; + } + + /* Mark cluster as used */ + nb_csectors = ((l2_entry >> s->csize_shift) & + s->csize_mask) + 1; + l2_entry &= s->cluster_offset_mask; + ret = inc_refcounts(bs, res, refcount_table, refcount_table_size, + l2_entry & ~511, nb_csectors * 512); + if (ret < 0) { + goto fail; + } + + if (flags & CHECK_FRAG_INFO) { + res->bfi.allocated_clusters++; + res->bfi.compressed_clusters++; + + /* Compressed clusters are fragmented by nature. Since they + * take up sub-sector space but we only have sector granularity + * I/O we need to re-read the same sectors even for adjacent + * compressed clusters. + */ + res->bfi.fragmented_clusters++; + } + break; + + case QCOW2_CLUSTER_ZERO: + if ((l2_entry & L2E_OFFSET_MASK) == 0) { + break; + } + /* fall through */ + + case QCOW2_CLUSTER_NORMAL: + { + uint64_t offset = l2_entry & L2E_OFFSET_MASK; + + if (flags & CHECK_FRAG_INFO) { + res->bfi.allocated_clusters++; + if (next_contiguous_offset && + offset != next_contiguous_offset) { + res->bfi.fragmented_clusters++; + } + next_contiguous_offset = offset + s->cluster_size; + } + + /* Mark cluster as used */ + ret = inc_refcounts(bs, res, refcount_table, refcount_table_size, + offset, s->cluster_size); + if (ret < 0) { + goto fail; + } + + /* Correct offsets are cluster aligned */ + if (offset_into_cluster(s, offset)) { + fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not " + "properly aligned; L2 entry corrupted.\n", offset); + res->corruptions++; + } + break; + } + + case QCOW2_CLUSTER_UNALLOCATED: + break; + + default: + abort(); + } + } + + g_free(l2_table); + return 0; + +fail: + g_free(l2_table); + return ret; +} + +/* + * Increases the refcount for the L1 table, its L2 tables and all referenced + * clusters in the given refcount table. While doing so, performs some checks + * on L1 and L2 entries. + * + * Returns the number of errors found by the checks or -errno if an internal + * error occurred. + */ +static int check_refcounts_l1(BlockDriverState *bs, + BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size, + int64_t l1_table_offset, int l1_size, + int flags) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l1_table = NULL, l2_offset, l1_size2; + int i, ret; + + l1_size2 = l1_size * sizeof(uint64_t); + + /* Mark L1 table as used */ + ret = inc_refcounts(bs, res, refcount_table, refcount_table_size, + l1_table_offset, l1_size2); + if (ret < 0) { + goto fail; + } + + /* Read L1 table entries from disk */ + if (l1_size2 > 0) { + l1_table = g_try_malloc(l1_size2); + if (l1_table == NULL) { + ret = -ENOMEM; + res->check_errors++; + goto fail; + } + ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); + if (ret < 0) { + fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); + res->check_errors++; + goto fail; + } + for(i = 0;i < l1_size; i++) + be64_to_cpus(&l1_table[i]); + } + + /* Do the actual checks */ + for(i = 0; i < l1_size; i++) { + l2_offset = l1_table[i]; + if (l2_offset) { + /* Mark L2 table as used */ + l2_offset &= L1E_OFFSET_MASK; + ret = inc_refcounts(bs, res, refcount_table, refcount_table_size, + l2_offset, s->cluster_size); + if (ret < 0) { + goto fail; + } + + /* L2 tables are cluster aligned */ + if (offset_into_cluster(s, l2_offset)) { + fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not " + "cluster aligned; L1 entry corrupted\n", l2_offset); + res->corruptions++; + } + + /* Process and check L2 entries */ + ret = check_refcounts_l2(bs, res, refcount_table, + refcount_table_size, l2_offset, flags); + if (ret < 0) { + goto fail; + } + } + } + g_free(l1_table); + return 0; + +fail: + g_free(l1_table); + return ret; +} + +/* + * Checks the OFLAG_COPIED flag for all L1 and L2 entries. + * + * This function does not print an error message nor does it increment + * check_errors if qcow2_get_refcount fails (this is because such an error will + * have been already detected and sufficiently signaled by the calling function + * (qcow2_check_refcounts) by the time this function is called). + */ +static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); + int ret; + uint64_t refcount; + int i, j; + + for (i = 0; i < s->l1_size; i++) { + uint64_t l1_entry = s->l1_table[i]; + uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK; + bool l2_dirty = false; + + if (!l2_offset) { + continue; + } + + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { + /* don't print message nor increment check_errors */ + continue; + } + if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { + fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " + "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : + "ERROR", + i, l1_entry, refcount); + if (fix & BDRV_FIX_ERRORS) { + s->l1_table[i] = refcount == 1 + ? l1_entry | QCOW_OFLAG_COPIED + : l1_entry & ~QCOW_OFLAG_COPIED; + ret = qcow2_write_l1_entry(bs, i); + if (ret < 0) { + res->check_errors++; + goto fail; + } + res->corruptions_fixed++; + } else { + res->corruptions++; + } + } + + ret = bdrv_pread(bs->file, l2_offset, l2_table, + s->l2_size * sizeof(uint64_t)); + if (ret < 0) { + fprintf(stderr, "ERROR: Could not read L2 table: %s\n", + strerror(-ret)); + res->check_errors++; + goto fail; + } + + for (j = 0; j < s->l2_size; j++) { + uint64_t l2_entry = be64_to_cpu(l2_table[j]); + uint64_t data_offset = l2_entry & L2E_OFFSET_MASK; + int cluster_type = qcow2_get_cluster_type(l2_entry); + + if ((cluster_type == QCOW2_CLUSTER_NORMAL) || + ((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) { + ret = qcow2_get_refcount(bs, + data_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { + /* don't print message nor increment check_errors */ + continue; + } + if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { + fprintf(stderr, "%s OFLAG_COPIED data cluster: " + "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : + "ERROR", + l2_entry, refcount); + if (fix & BDRV_FIX_ERRORS) { + l2_table[j] = cpu_to_be64(refcount == 1 + ? l2_entry | QCOW_OFLAG_COPIED + : l2_entry & ~QCOW_OFLAG_COPIED); + l2_dirty = true; + res->corruptions_fixed++; + } else { + res->corruptions++; + } + } + } + } + + if (l2_dirty) { + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, + l2_offset, s->cluster_size); + if (ret < 0) { + fprintf(stderr, "ERROR: Could not write L2 table; metadata " + "overlap check failed: %s\n", strerror(-ret)); + res->check_errors++; + goto fail; + } + + ret = bdrv_pwrite(bs->file, l2_offset, l2_table, s->cluster_size); + if (ret < 0) { + fprintf(stderr, "ERROR: Could not write L2 table: %s\n", + strerror(-ret)); + res->check_errors++; + goto fail; + } + } + } + + ret = 0; + +fail: + qemu_vfree(l2_table); + return ret; +} + +/* + * Checks consistency of refblocks and accounts for each refblock in + * *refcount_table. + */ +static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix, bool *rebuild, + void **refcount_table, int64_t *nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + int64_t i, size; + int ret; + + for(i = 0; i < s->refcount_table_size; i++) { + uint64_t offset, cluster; + offset = s->refcount_table[i]; + cluster = offset >> s->cluster_bits; + + /* Refcount blocks are cluster aligned */ + if (offset_into_cluster(s, offset)) { + fprintf(stderr, "ERROR refcount block %" PRId64 " is not " + "cluster aligned; refcount table entry corrupted\n", i); + res->corruptions++; + *rebuild = true; + continue; + } + + if (cluster >= *nb_clusters) { + fprintf(stderr, "%s refcount block %" PRId64 " is outside image\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); + + if (fix & BDRV_FIX_ERRORS) { + int64_t new_nb_clusters; + + if (offset > INT64_MAX - s->cluster_size) { + ret = -EINVAL; + goto resize_fail; + } + + ret = bdrv_truncate(bs->file, offset + s->cluster_size); + if (ret < 0) { + goto resize_fail; + } + size = bdrv_getlength(bs->file); + if (size < 0) { + ret = size; + goto resize_fail; + } + + new_nb_clusters = size_to_clusters(s, size); + assert(new_nb_clusters >= *nb_clusters); + + ret = realloc_refcount_array(s, refcount_table, + nb_clusters, new_nb_clusters); + if (ret < 0) { + res->check_errors++; + return ret; + } + + if (cluster >= *nb_clusters) { + ret = -EINVAL; + goto resize_fail; + } + + res->corruptions_fixed++; + ret = inc_refcounts(bs, res, refcount_table, nb_clusters, + offset, s->cluster_size); + if (ret < 0) { + return ret; + } + /* No need to check whether the refcount is now greater than 1: + * This area was just allocated and zeroed, so it can only be + * exactly 1 after inc_refcounts() */ + continue; + +resize_fail: + res->corruptions++; + *rebuild = true; + fprintf(stderr, "ERROR could not resize image: %s\n", + strerror(-ret)); + } else { + res->corruptions++; + } + continue; + } + + if (offset != 0) { + ret = inc_refcounts(bs, res, refcount_table, nb_clusters, + offset, s->cluster_size); + if (ret < 0) { + return ret; + } + if (s->get_refcount(*refcount_table, cluster) != 1) { + fprintf(stderr, "ERROR refcount block %" PRId64 + " refcount=%" PRIu64 "\n", i, + s->get_refcount(*refcount_table, cluster)); + res->corruptions++; + *rebuild = true; + } + } + } + + return 0; +} + +/* + * Calculates an in-memory refcount table. + */ +static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix, bool *rebuild, + void **refcount_table, int64_t *nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + int64_t i; + QCowSnapshot *sn; + int ret; + + if (!*refcount_table) { + int64_t old_size = 0; + ret = realloc_refcount_array(s, refcount_table, + &old_size, *nb_clusters); + if (ret < 0) { + res->check_errors++; + return ret; + } + } + + /* header */ + ret = inc_refcounts(bs, res, refcount_table, nb_clusters, + 0, s->cluster_size); + if (ret < 0) { + return ret; + } + + /* current L1 table */ + ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, + s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO); + if (ret < 0) { + return ret; + } + + /* snapshots */ + for (i = 0; i < s->nb_snapshots; i++) { + sn = s->snapshots + i; + ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, + sn->l1_table_offset, sn->l1_size, 0); + if (ret < 0) { + return ret; + } + } + ret = inc_refcounts(bs, res, refcount_table, nb_clusters, + s->snapshots_offset, s->snapshots_size); + if (ret < 0) { + return ret; + } + + /* refcount data */ + ret = inc_refcounts(bs, res, refcount_table, nb_clusters, + s->refcount_table_offset, + s->refcount_table_size * sizeof(uint64_t)); + if (ret < 0) { + return ret; + } + + return check_refblocks(bs, res, fix, rebuild, refcount_table, nb_clusters); +} + +/* + * Compares the actual reference count for each cluster in the image against the + * refcount as reported by the refcount structures on-disk. + */ +static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix, bool *rebuild, + int64_t *highest_cluster, + void *refcount_table, int64_t nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + int64_t i; + uint64_t refcount1, refcount2; + int ret; + + for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) { + ret = qcow2_get_refcount(bs, i, &refcount1); + if (ret < 0) { + fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", + i, strerror(-ret)); + res->check_errors++; + continue; + } + + refcount2 = s->get_refcount(refcount_table, i); + + if (refcount1 > 0 || refcount2 > 0) { + *highest_cluster = i; + } + + if (refcount1 != refcount2) { + /* Check if we're allowed to fix the mismatch */ + int *num_fixed = NULL; + if (refcount1 == 0) { + *rebuild = true; + } else if (refcount1 > refcount2 && (fix & BDRV_FIX_LEAKS)) { + num_fixed = &res->leaks_fixed; + } else if (refcount1 < refcount2 && (fix & BDRV_FIX_ERRORS)) { + num_fixed = &res->corruptions_fixed; + } + + fprintf(stderr, "%s cluster %" PRId64 " refcount=%" PRIu64 + " reference=%" PRIu64 "\n", + num_fixed != NULL ? "Repairing" : + refcount1 < refcount2 ? "ERROR" : + "Leaked", + i, refcount1, refcount2); + + if (num_fixed) { + ret = update_refcount(bs, i << s->cluster_bits, 1, + refcount_diff(refcount1, refcount2), + refcount1 > refcount2, + QCOW2_DISCARD_ALWAYS); + if (ret >= 0) { + (*num_fixed)++; + continue; + } + } + + /* And if we couldn't, print an error */ + if (refcount1 < refcount2) { + res->corruptions++; + } else { + res->leaks++; + } + } + } +} + +/* + * Allocates clusters using an in-memory refcount table (IMRT) in contrast to + * the on-disk refcount structures. + * + * On input, *first_free_cluster tells where to start looking, and need not + * actually be a free cluster; the returned offset will not be before that + * cluster. On output, *first_free_cluster points to the first gap found, even + * if that gap was too small to be used as the returned offset. + * + * Note that *first_free_cluster is a cluster index whereas the return value is + * an offset. + */ +static int64_t alloc_clusters_imrt(BlockDriverState *bs, + int cluster_count, + void **refcount_table, + int64_t *imrt_nb_clusters, + int64_t *first_free_cluster) +{ + BDRVQcowState *s = bs->opaque; + int64_t cluster = *first_free_cluster, i; + bool first_gap = true; + int contiguous_free_clusters; + int ret; + + /* Starting at *first_free_cluster, find a range of at least cluster_count + * continuously free clusters */ + for (contiguous_free_clusters = 0; + cluster < *imrt_nb_clusters && + contiguous_free_clusters < cluster_count; + cluster++) + { + if (!s->get_refcount(*refcount_table, cluster)) { + contiguous_free_clusters++; + if (first_gap) { + /* If this is the first free cluster found, update + * *first_free_cluster accordingly */ + *first_free_cluster = cluster; + first_gap = false; + } + } else if (contiguous_free_clusters) { + contiguous_free_clusters = 0; + } + } + + /* If contiguous_free_clusters is greater than zero, it contains the number + * of continuously free clusters until the current cluster; the first free + * cluster in the current "gap" is therefore + * cluster - contiguous_free_clusters */ + + /* If no such range could be found, grow the in-memory refcount table + * accordingly to append free clusters at the end of the image */ + if (contiguous_free_clusters < cluster_count) { + /* contiguous_free_clusters clusters are already empty at the image end; + * we need cluster_count clusters; therefore, we have to allocate + * cluster_count - contiguous_free_clusters new clusters at the end of + * the image (which is the current value of cluster; note that cluster + * may exceed old_imrt_nb_clusters if *first_free_cluster pointed beyond + * the image end) */ + ret = realloc_refcount_array(s, refcount_table, imrt_nb_clusters, + cluster + cluster_count + - contiguous_free_clusters); + if (ret < 0) { + return ret; + } + } + + /* Go back to the first free cluster */ + cluster -= contiguous_free_clusters; + for (i = 0; i < cluster_count; i++) { + s->set_refcount(*refcount_table, cluster + i, 1); + } + + return cluster << s->cluster_bits; +} + +/* + * Creates a new refcount structure based solely on the in-memory information + * given through *refcount_table. All necessary allocations will be reflected + * in that array. + * + * On success, the old refcount structure is leaked (it will be covered by the + * new refcount structure). + */ +static int rebuild_refcount_structure(BlockDriverState *bs, + BdrvCheckResult *res, + void **refcount_table, + int64_t *nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0; + int64_t refblock_offset, refblock_start, refblock_index; + uint32_t reftable_size = 0; + uint64_t *on_disk_reftable = NULL; + void *on_disk_refblock; + int ret = 0; + struct { + uint64_t reftable_offset; + uint32_t reftable_clusters; + } QEMU_PACKED reftable_offset_and_clusters; + + qcow2_cache_empty(bs, s->refcount_block_cache); + +write_refblocks: + for (; cluster < *nb_clusters; cluster++) { + if (!s->get_refcount(*refcount_table, cluster)) { + continue; + } + + refblock_index = cluster >> s->refcount_block_bits; + refblock_start = refblock_index << s->refcount_block_bits; + + /* Don't allocate a cluster in a refblock already written to disk */ + if (first_free_cluster < refblock_start) { + first_free_cluster = refblock_start; + } + refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table, + nb_clusters, &first_free_cluster); + if (refblock_offset < 0) { + fprintf(stderr, "ERROR allocating refblock: %s\n", + strerror(-refblock_offset)); + res->check_errors++; + ret = refblock_offset; + goto fail; + } + + if (reftable_size <= refblock_index) { + uint32_t old_reftable_size = reftable_size; + uint64_t *new_on_disk_reftable; + + reftable_size = ROUND_UP((refblock_index + 1) * sizeof(uint64_t), + s->cluster_size) / sizeof(uint64_t); + new_on_disk_reftable = g_try_realloc(on_disk_reftable, + reftable_size * + sizeof(uint64_t)); + if (!new_on_disk_reftable) { + res->check_errors++; + ret = -ENOMEM; + goto fail; + } + on_disk_reftable = new_on_disk_reftable; + + memset(on_disk_reftable + old_reftable_size, 0, + (reftable_size - old_reftable_size) * sizeof(uint64_t)); + + /* The offset we have for the reftable is now no longer valid; + * this will leak that range, but we can easily fix that by running + * a leak-fixing check after this rebuild operation */ + reftable_offset = -1; + } + on_disk_reftable[refblock_index] = refblock_offset; + + /* If this is apparently the last refblock (for now), try to squeeze the + * reftable in */ + if (refblock_index == (*nb_clusters - 1) >> s->refcount_block_bits && + reftable_offset < 0) + { + uint64_t reftable_clusters = size_to_clusters(s, reftable_size * + sizeof(uint64_t)); + reftable_offset = alloc_clusters_imrt(bs, reftable_clusters, + refcount_table, nb_clusters, + &first_free_cluster); + if (reftable_offset < 0) { + fprintf(stderr, "ERROR allocating reftable: %s\n", + strerror(-reftable_offset)); + res->check_errors++; + ret = reftable_offset; + goto fail; + } + } + + ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset, + s->cluster_size); + if (ret < 0) { + fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); + goto fail; + } + + /* The size of *refcount_table is always cluster-aligned, therefore the + * write operation will not overflow */ + on_disk_refblock = (void *)((char *) *refcount_table + + refblock_index * s->cluster_size); + + ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, + on_disk_refblock, s->cluster_sectors); + if (ret < 0) { + fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); + goto fail; + } + + /* Go to the end of this refblock */ + cluster = refblock_start + s->refcount_block_size - 1; + } + + if (reftable_offset < 0) { + uint64_t post_refblock_start, reftable_clusters; + + post_refblock_start = ROUND_UP(*nb_clusters, s->refcount_block_size); + reftable_clusters = size_to_clusters(s, + reftable_size * sizeof(uint64_t)); + /* Not pretty but simple */ + if (first_free_cluster < post_refblock_start) { + first_free_cluster = post_refblock_start; + } + reftable_offset = alloc_clusters_imrt(bs, reftable_clusters, + refcount_table, nb_clusters, + &first_free_cluster); + if (reftable_offset < 0) { + fprintf(stderr, "ERROR allocating reftable: %s\n", + strerror(-reftable_offset)); + res->check_errors++; + ret = reftable_offset; + goto fail; + } + + goto write_refblocks; + } + + assert(on_disk_reftable); + + for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) { + cpu_to_be64s(&on_disk_reftable[refblock_index]); + } + + ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, + reftable_size * sizeof(uint64_t)); + if (ret < 0) { + fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); + goto fail; + } + + assert(reftable_size < INT_MAX / sizeof(uint64_t)); + ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, + reftable_size * sizeof(uint64_t)); + if (ret < 0) { + fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); + goto fail; + } + + /* Enter new reftable into the image header */ + cpu_to_be64w(&reftable_offset_and_clusters.reftable_offset, + reftable_offset); + cpu_to_be32w(&reftable_offset_and_clusters.reftable_clusters, + size_to_clusters(s, reftable_size * sizeof(uint64_t))); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, + refcount_table_offset), + &reftable_offset_and_clusters, + sizeof(reftable_offset_and_clusters)); + if (ret < 0) { + fprintf(stderr, "ERROR setting reftable: %s\n", strerror(-ret)); + goto fail; + } + + for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) { + be64_to_cpus(&on_disk_reftable[refblock_index]); + } + s->refcount_table = on_disk_reftable; + s->refcount_table_offset = reftable_offset; + s->refcount_table_size = reftable_size; + + return 0; + +fail: + g_free(on_disk_reftable); + return ret; +} + +/* + * Checks an image for refcount consistency. + * + * Returns 0 if no errors are found, the number of errors in case the image is + * detected as corrupted, and -errno when an internal error occurred. + */ +int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVQcowState *s = bs->opaque; + BdrvCheckResult pre_compare_res; + int64_t size, highest_cluster, nb_clusters; + void *refcount_table = NULL; + bool rebuild = false; + int ret; + + size = bdrv_getlength(bs->file); + if (size < 0) { + res->check_errors++; + return size; + } + + nb_clusters = size_to_clusters(s, size); + if (nb_clusters > INT_MAX) { + res->check_errors++; + return -EFBIG; + } + + res->bfi.total_clusters = + size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE); + + ret = calculate_refcounts(bs, res, fix, &rebuild, &refcount_table, + &nb_clusters); + if (ret < 0) { + goto fail; + } + + /* In case we don't need to rebuild the refcount structure (but want to fix + * something), this function is immediately called again, in which case the + * result should be ignored */ + pre_compare_res = *res; + compare_refcounts(bs, res, 0, &rebuild, &highest_cluster, refcount_table, + nb_clusters); + + if (rebuild && (fix & BDRV_FIX_ERRORS)) { + BdrvCheckResult old_res = *res; + int fresh_leaks = 0; + + fprintf(stderr, "Rebuilding refcount structure\n"); + ret = rebuild_refcount_structure(bs, res, &refcount_table, + &nb_clusters); + if (ret < 0) { + goto fail; + } + + res->corruptions = 0; + res->leaks = 0; + + /* Because the old reftable has been exchanged for a new one the + * references have to be recalculated */ + rebuild = false; + memset(refcount_table, 0, refcount_array_byte_size(s, nb_clusters)); + ret = calculate_refcounts(bs, res, 0, &rebuild, &refcount_table, + &nb_clusters); + if (ret < 0) { + goto fail; + } + + if (fix & BDRV_FIX_LEAKS) { + /* The old refcount structures are now leaked, fix it; the result + * can be ignored, aside from leaks which were introduced by + * rebuild_refcount_structure() that could not be fixed */ + BdrvCheckResult saved_res = *res; + *res = (BdrvCheckResult){ 0 }; + + compare_refcounts(bs, res, BDRV_FIX_LEAKS, &rebuild, + &highest_cluster, refcount_table, nb_clusters); + if (rebuild) { + fprintf(stderr, "ERROR rebuilt refcount structure is still " + "broken\n"); + } + + /* Any leaks accounted for here were introduced by + * rebuild_refcount_structure() because that function has created a + * new refcount structure from scratch */ + fresh_leaks = res->leaks; + *res = saved_res; + } + + if (res->corruptions < old_res.corruptions) { + res->corruptions_fixed += old_res.corruptions - res->corruptions; + } + if (res->leaks < old_res.leaks) { + res->leaks_fixed += old_res.leaks - res->leaks; + } + res->leaks += fresh_leaks; + } else if (fix) { + if (rebuild) { + fprintf(stderr, "ERROR need to rebuild refcount structures\n"); + res->check_errors++; + ret = -EIO; + goto fail; + } + + if (res->leaks || res->corruptions) { + *res = pre_compare_res; + compare_refcounts(bs, res, fix, &rebuild, &highest_cluster, + refcount_table, nb_clusters); + } + } + + /* check OFLAG_COPIED */ + ret = check_oflag_copied(bs, res, fix); + if (ret < 0) { + goto fail; + } + + res->image_end_offset = (highest_cluster + 1) * s->cluster_size; + ret = 0; + +fail: + g_free(refcount_table); + + return ret; +} + +#define overlaps_with(ofs, sz) \ + ranges_overlap(offset, size, ofs, sz) + +/* + * Checks if the given offset into the image file is actually free to use by + * looking for overlaps with important metadata sections (L1/L2 tables etc.), + * i.e. a sanity check without relying on the refcount tables. + * + * The ign parameter specifies what checks not to perform (being a bitmask of + * QCow2MetadataOverlap values), i.e., what sections to ignore. + * + * Returns: + * - 0 if writing to this offset will not affect the mentioned metadata + * - a positive QCow2MetadataOverlap value indicating one overlapping section + * - a negative value (-errno) indicating an error while performing a check, + * e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2 + */ +int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, + int64_t size) +{ + BDRVQcowState *s = bs->opaque; + int chk = s->overlap_check & ~ign; + int i, j; + + if (!size) { + return 0; + } + + if (chk & QCOW2_OL_MAIN_HEADER) { + if (offset < s->cluster_size) { + return QCOW2_OL_MAIN_HEADER; + } + } + + /* align range to test to cluster boundaries */ + size = align_offset(offset_into_cluster(s, offset) + size, s->cluster_size); + offset = start_of_cluster(s, offset); + + if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) { + if (overlaps_with(s->l1_table_offset, s->l1_size * sizeof(uint64_t))) { + return QCOW2_OL_ACTIVE_L1; + } + } + + if ((chk & QCOW2_OL_REFCOUNT_TABLE) && s->refcount_table_size) { + if (overlaps_with(s->refcount_table_offset, + s->refcount_table_size * sizeof(uint64_t))) { + return QCOW2_OL_REFCOUNT_TABLE; + } + } + + if ((chk & QCOW2_OL_SNAPSHOT_TABLE) && s->snapshots_size) { + if (overlaps_with(s->snapshots_offset, s->snapshots_size)) { + return QCOW2_OL_SNAPSHOT_TABLE; + } + } + + if ((chk & QCOW2_OL_INACTIVE_L1) && s->snapshots) { + for (i = 0; i < s->nb_snapshots; i++) { + if (s->snapshots[i].l1_size && + overlaps_with(s->snapshots[i].l1_table_offset, + s->snapshots[i].l1_size * sizeof(uint64_t))) { + return QCOW2_OL_INACTIVE_L1; + } + } + } + + if ((chk & QCOW2_OL_ACTIVE_L2) && s->l1_table) { + for (i = 0; i < s->l1_size; i++) { + if ((s->l1_table[i] & L1E_OFFSET_MASK) && + overlaps_with(s->l1_table[i] & L1E_OFFSET_MASK, + s->cluster_size)) { + return QCOW2_OL_ACTIVE_L2; + } + } + } + + if ((chk & QCOW2_OL_REFCOUNT_BLOCK) && s->refcount_table) { + for (i = 0; i < s->refcount_table_size; i++) { + if ((s->refcount_table[i] & REFT_OFFSET_MASK) && + overlaps_with(s->refcount_table[i] & REFT_OFFSET_MASK, + s->cluster_size)) { + return QCOW2_OL_REFCOUNT_BLOCK; + } + } + } + + if ((chk & QCOW2_OL_INACTIVE_L2) && s->snapshots) { + for (i = 0; i < s->nb_snapshots; i++) { + uint64_t l1_ofs = s->snapshots[i].l1_table_offset; + uint32_t l1_sz = s->snapshots[i].l1_size; + uint64_t l1_sz2 = l1_sz * sizeof(uint64_t); + uint64_t *l1 = g_try_malloc(l1_sz2); + int ret; + + if (l1_sz2 && l1 == NULL) { + return -ENOMEM; + } + + ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2); + if (ret < 0) { + g_free(l1); + return ret; + } + + for (j = 0; j < l1_sz; j++) { + uint64_t l2_ofs = be64_to_cpu(l1[j]) & L1E_OFFSET_MASK; + if (l2_ofs && overlaps_with(l2_ofs, s->cluster_size)) { + g_free(l1); + return QCOW2_OL_INACTIVE_L2; + } + } + + g_free(l1); + } + } + + return 0; +} + +static const char *metadata_ol_names[] = { + [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", + [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", + [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", + [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", + [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", + [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", + [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", + [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", +}; + +/* + * First performs a check for metadata overlaps (through + * qcow2_check_metadata_overlap); if that fails with a negative value (error + * while performing a check), that value is returned. If an impending overlap + * is detected, the BDS will be made unusable, the qcow2 file marked corrupt + * and -EIO returned. + * + * Returns 0 if there were neither overlaps nor errors while checking for + * overlaps; or a negative value (-errno) on error. + */ +int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, + int64_t size) +{ + int ret = qcow2_check_metadata_overlap(bs, ign, offset, size); + + if (ret < 0) { + return ret; + } else if (ret > 0) { + int metadata_ol_bitnr = ctz32(ret); + assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR); + + qcow2_signal_corruption(bs, true, offset, size, "Preventing invalid " + "write on metadata (overlaps with %s)", + metadata_ol_names[metadata_ol_bitnr]); + return -EIO; + } + + return 0; +} diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c new file mode 100644 index 0000000..17bb211 --- /dev/null +++ b/block/qcow2-snapshot.c @@ -0,0 +1,733 @@ +/* + * Block driver for the QCOW version 2 format + * + * Copyright (c) 2004-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "block/qcow2.h" + +void qcow2_free_snapshots(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int i; + + for(i = 0; i < s->nb_snapshots; i++) { + g_free(s->snapshots[i].name); + g_free(s->snapshots[i].id_str); + } + g_free(s->snapshots); + s->snapshots = NULL; + s->nb_snapshots = 0; +} + +int qcow2_read_snapshots(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + QCowSnapshotHeader h; + QCowSnapshotExtraData extra; + QCowSnapshot *sn; + int i, id_str_size, name_size; + int64_t offset; + uint32_t extra_data_size; + int ret; + + if (!s->nb_snapshots) { + s->snapshots = NULL; + s->snapshots_size = 0; + return 0; + } + + offset = s->snapshots_offset; + s->snapshots = g_new0(QCowSnapshot, s->nb_snapshots); + + for(i = 0; i < s->nb_snapshots; i++) { + /* Read statically sized part of the snapshot header */ + offset = align_offset(offset, 8); + ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); + if (ret < 0) { + goto fail; + } + + offset += sizeof(h); + sn = s->snapshots + i; + sn->l1_table_offset = be64_to_cpu(h.l1_table_offset); + sn->l1_size = be32_to_cpu(h.l1_size); + sn->vm_state_size = be32_to_cpu(h.vm_state_size); + sn->date_sec = be32_to_cpu(h.date_sec); + sn->date_nsec = be32_to_cpu(h.date_nsec); + sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec); + extra_data_size = be32_to_cpu(h.extra_data_size); + + id_str_size = be16_to_cpu(h.id_str_size); + name_size = be16_to_cpu(h.name_size); + + /* Read extra data */ + ret = bdrv_pread(bs->file, offset, &extra, + MIN(sizeof(extra), extra_data_size)); + if (ret < 0) { + goto fail; + } + offset += extra_data_size; + + if (extra_data_size >= 8) { + sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large); + } + + if (extra_data_size >= 16) { + sn->disk_size = be64_to_cpu(extra.disk_size); + } else { + sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + } + + /* Read snapshot ID */ + sn->id_str = g_malloc(id_str_size + 1); + ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); + if (ret < 0) { + goto fail; + } + offset += id_str_size; + sn->id_str[id_str_size] = '\0'; + + /* Read snapshot name */ + sn->name = g_malloc(name_size + 1); + ret = bdrv_pread(bs->file, offset, sn->name, name_size); + if (ret < 0) { + goto fail; + } + offset += name_size; + sn->name[name_size] = '\0'; + + if (offset - s->snapshots_offset > QCOW_MAX_SNAPSHOTS_SIZE) { + ret = -EFBIG; + goto fail; + } + } + + assert(offset - s->snapshots_offset <= INT_MAX); + s->snapshots_size = offset - s->snapshots_offset; + return 0; + +fail: + qcow2_free_snapshots(bs); + return ret; +} + +/* add at the end of the file a new list of snapshots */ +static int qcow2_write_snapshots(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + QCowSnapshot *sn; + QCowSnapshotHeader h; + QCowSnapshotExtraData extra; + int i, name_size, id_str_size, snapshots_size; + struct { + uint32_t nb_snapshots; + uint64_t snapshots_offset; + } QEMU_PACKED header_data; + int64_t offset, snapshots_offset = 0; + int ret; + + /* compute the size of the snapshots */ + offset = 0; + for(i = 0; i < s->nb_snapshots; i++) { + sn = s->snapshots + i; + offset = align_offset(offset, 8); + offset += sizeof(h); + offset += sizeof(extra); + offset += strlen(sn->id_str); + offset += strlen(sn->name); + + if (offset > QCOW_MAX_SNAPSHOTS_SIZE) { + ret = -EFBIG; + goto fail; + } + } + + assert(offset <= INT_MAX); + snapshots_size = offset; + + /* Allocate space for the new snapshot list */ + snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); + offset = snapshots_offset; + if (offset < 0) { + ret = offset; + goto fail; + } + ret = bdrv_flush(bs); + if (ret < 0) { + goto fail; + } + + /* The snapshot list position has not yet been updated, so these clusters + * must indeed be completely free */ + ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size); + if (ret < 0) { + goto fail; + } + + + /* Write all snapshots to the new list */ + for(i = 0; i < s->nb_snapshots; i++) { + sn = s->snapshots + i; + memset(&h, 0, sizeof(h)); + h.l1_table_offset = cpu_to_be64(sn->l1_table_offset); + h.l1_size = cpu_to_be32(sn->l1_size); + /* If it doesn't fit in 32 bit, older implementations should treat it + * as a disk-only snapshot rather than truncate the VM state */ + if (sn->vm_state_size <= 0xffffffff) { + h.vm_state_size = cpu_to_be32(sn->vm_state_size); + } + h.date_sec = cpu_to_be32(sn->date_sec); + h.date_nsec = cpu_to_be32(sn->date_nsec); + h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec); + h.extra_data_size = cpu_to_be32(sizeof(extra)); + + memset(&extra, 0, sizeof(extra)); + extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size); + extra.disk_size = cpu_to_be64(sn->disk_size); + + id_str_size = strlen(sn->id_str); + name_size = strlen(sn->name); + assert(id_str_size <= UINT16_MAX && name_size <= UINT16_MAX); + h.id_str_size = cpu_to_be16(id_str_size); + h.name_size = cpu_to_be16(name_size); + offset = align_offset(offset, 8); + + ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); + if (ret < 0) { + goto fail; + } + offset += sizeof(h); + + ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra)); + if (ret < 0) { + goto fail; + } + offset += sizeof(extra); + + ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size); + if (ret < 0) { + goto fail; + } + offset += id_str_size; + + ret = bdrv_pwrite(bs->file, offset, sn->name, name_size); + if (ret < 0) { + goto fail; + } + offset += name_size; + } + + /* + * Update the header to point to the new snapshot table. This requires the + * new table and its refcounts to be stable on disk. + */ + ret = bdrv_flush(bs); + if (ret < 0) { + goto fail; + } + + QEMU_BUILD_BUG_ON(offsetof(QCowHeader, snapshots_offset) != + offsetof(QCowHeader, nb_snapshots) + sizeof(header_data.nb_snapshots)); + + header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots); + header_data.snapshots_offset = cpu_to_be64(snapshots_offset); + + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), + &header_data, sizeof(header_data)); + if (ret < 0) { + goto fail; + } + + /* free the old snapshot table */ + qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size, + QCOW2_DISCARD_SNAPSHOT); + s->snapshots_offset = snapshots_offset; + s->snapshots_size = snapshots_size; + return 0; + +fail: + if (snapshots_offset > 0) { + qcow2_free_clusters(bs, snapshots_offset, snapshots_size, + QCOW2_DISCARD_ALWAYS); + } + return ret; +} + +static void find_new_snapshot_id(BlockDriverState *bs, + char *id_str, int id_str_size) +{ + BDRVQcowState *s = bs->opaque; + QCowSnapshot *sn; + int i; + unsigned long id, id_max = 0; + + for(i = 0; i < s->nb_snapshots; i++) { + sn = s->snapshots + i; + id = strtoul(sn->id_str, NULL, 10); + if (id > id_max) + id_max = id; + } + snprintf(id_str, id_str_size, "%lu", id_max + 1); +} + +static int find_snapshot_by_id_and_name(BlockDriverState *bs, + const char *id, + const char *name) +{ + BDRVQcowState *s = bs->opaque; + int i; + + if (id && name) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].id_str, id) && + !strcmp(s->snapshots[i].name, name)) { + return i; + } + } + } else if (id) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].id_str, id)) { + return i; + } + } + } else if (name) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].name, name)) { + return i; + } + } + } + + return -1; +} + +static int find_snapshot_by_id_or_name(BlockDriverState *bs, + const char *id_or_name) +{ + int ret; + + ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL); + if (ret >= 0) { + return ret; + } + return find_snapshot_by_id_and_name(bs, NULL, id_or_name); +} + +/* if no id is provided, a new one is constructed */ +int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) +{ + BDRVQcowState *s = bs->opaque; + QCowSnapshot *new_snapshot_list = NULL; + QCowSnapshot *old_snapshot_list = NULL; + QCowSnapshot sn1, *sn = &sn1; + int i, ret; + uint64_t *l1_table = NULL; + int64_t l1_table_offset; + + if (s->nb_snapshots >= QCOW_MAX_SNAPSHOTS) { + return -EFBIG; + } + + memset(sn, 0, sizeof(*sn)); + + /* Generate an ID */ + find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str)); + + /* Check that the ID is unique */ + if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) { + return -EEXIST; + } + + /* Populate sn with passed data */ + sn->id_str = g_strdup(sn_info->id_str); + sn->name = g_strdup(sn_info->name); + + sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + sn->vm_state_size = sn_info->vm_state_size; + sn->date_sec = sn_info->date_sec; + sn->date_nsec = sn_info->date_nsec; + sn->vm_clock_nsec = sn_info->vm_clock_nsec; + + /* Allocate the L1 table of the snapshot and copy the current one there. */ + l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); + if (l1_table_offset < 0) { + ret = l1_table_offset; + goto fail; + } + + sn->l1_table_offset = l1_table_offset; + sn->l1_size = s->l1_size; + + l1_table = g_try_new(uint64_t, s->l1_size); + if (s->l1_size && l1_table == NULL) { + ret = -ENOMEM; + goto fail; + } + + for(i = 0; i < s->l1_size; i++) { + l1_table[i] = cpu_to_be64(s->l1_table[i]); + } + + ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + g_free(l1_table); + l1_table = NULL; + + /* + * Increase the refcounts of all clusters and make sure everything is + * stable on disk before updating the snapshot table to contain a pointer + * to the new L1 table. + */ + ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1); + if (ret < 0) { + goto fail; + } + + /* Append the new snapshot to the snapshot list */ + new_snapshot_list = g_new(QCowSnapshot, s->nb_snapshots + 1); + if (s->snapshots) { + memcpy(new_snapshot_list, s->snapshots, + s->nb_snapshots * sizeof(QCowSnapshot)); + old_snapshot_list = s->snapshots; + } + s->snapshots = new_snapshot_list; + s->snapshots[s->nb_snapshots++] = *sn; + + ret = qcow2_write_snapshots(bs); + if (ret < 0) { + g_free(s->snapshots); + s->snapshots = old_snapshot_list; + s->nb_snapshots--; + goto fail; + } + + g_free(old_snapshot_list); + + /* The VM state isn't needed any more in the active L1 table; in fact, it + * hurts by causing expensive COW for the next snapshot. */ + qcow2_discard_clusters(bs, qcow2_vm_state_offset(s), + align_offset(sn->vm_state_size, s->cluster_size) + >> BDRV_SECTOR_BITS, + QCOW2_DISCARD_NEVER, false); + +#ifdef DEBUG_ALLOC + { + BdrvCheckResult result = {0}; + qcow2_check_refcounts(bs, &result, 0); + } +#endif + return 0; + +fail: + g_free(sn->id_str); + g_free(sn->name); + g_free(l1_table); + + return ret; +} + +/* copy the snapshot 'snapshot_name' into the current disk image */ +int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) +{ + BDRVQcowState *s = bs->opaque; + QCowSnapshot *sn; + int i, snapshot_index; + int cur_l1_bytes, sn_l1_bytes; + int ret; + uint64_t *sn_l1_table = NULL; + + /* Search the snapshot */ + snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); + if (snapshot_index < 0) { + return -ENOENT; + } + sn = &s->snapshots[snapshot_index]; + + if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { + error_report("qcow2: Loading snapshots with different disk " + "size is not implemented"); + ret = -ENOTSUP; + goto fail; + } + + /* + * Make sure that the current L1 table is big enough to contain the whole + * L1 table of the snapshot. If the snapshot L1 table is smaller, the + * current one must be padded with zeros. + */ + ret = qcow2_grow_l1_table(bs, sn->l1_size, true); + if (ret < 0) { + goto fail; + } + + cur_l1_bytes = s->l1_size * sizeof(uint64_t); + sn_l1_bytes = sn->l1_size * sizeof(uint64_t); + + /* + * Copy the snapshot L1 table to the current L1 table. + * + * Before overwriting the old current L1 table on disk, make sure to + * increase all refcounts for the clusters referenced by the new one. + * Decrease the refcount referenced by the old one only when the L1 + * table is overwritten. + */ + sn_l1_table = g_try_malloc0(cur_l1_bytes); + if (cur_l1_bytes && sn_l1_table == NULL) { + ret = -ENOMEM; + goto fail; + } + + ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes); + if (ret < 0) { + goto fail; + } + + ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, + sn->l1_size, 1); + if (ret < 0) { + goto fail; + } + + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, + s->l1_table_offset, cur_l1_bytes); + if (ret < 0) { + goto fail; + } + + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table, + cur_l1_bytes); + if (ret < 0) { + goto fail; + } + + /* + * Decrease refcount of clusters of current L1 table. + * + * At this point, the in-memory s->l1_table points to the old L1 table, + * whereas on disk we already have the new one. + * + * qcow2_update_snapshot_refcount special cases the current L1 table to use + * the in-memory data instead of really using the offset to load a new one, + * which is why this works. + */ + ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, + s->l1_size, -1); + + /* + * Now update the in-memory L1 table to be in sync with the on-disk one. We + * need to do this even if updating refcounts failed. + */ + for(i = 0;i < s->l1_size; i++) { + s->l1_table[i] = be64_to_cpu(sn_l1_table[i]); + } + + if (ret < 0) { + goto fail; + } + + g_free(sn_l1_table); + sn_l1_table = NULL; + + /* + * Update QCOW_OFLAG_COPIED in the active L1 table (it may have changed + * when we decreased the refcount of the old snapshot. + */ + ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); + if (ret < 0) { + goto fail; + } + +#ifdef DEBUG_ALLOC + { + BdrvCheckResult result = {0}; + qcow2_check_refcounts(bs, &result, 0); + } +#endif + return 0; + +fail: + g_free(sn_l1_table); + return ret; +} + +int qcow2_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) +{ + BDRVQcowState *s = bs->opaque; + QCowSnapshot sn; + int snapshot_index, ret; + + /* Search the snapshot */ + snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); + if (snapshot_index < 0) { + error_setg(errp, "Can't find the snapshot"); + return -ENOENT; + } + sn = s->snapshots[snapshot_index]; + + /* Remove it from the snapshot list */ + memmove(s->snapshots + snapshot_index, + s->snapshots + snapshot_index + 1, + (s->nb_snapshots - snapshot_index - 1) * sizeof(sn)); + s->nb_snapshots--; + ret = qcow2_write_snapshots(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to remove snapshot from snapshot list"); + return ret; + } + + /* + * The snapshot is now unused, clean up. If we fail after this point, we + * won't recover but just leak clusters. + */ + g_free(sn.id_str); + g_free(sn.name); + + /* + * Now decrease the refcounts of clusters referenced by the snapshot and + * free the L1 table. + */ + ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset, + sn.l1_size, -1); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to free the cluster and L1 table"); + return ret; + } + qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t), + QCOW2_DISCARD_SNAPSHOT); + + /* must update the copied flag on the current cluster offsets */ + ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to update snapshot status in disk"); + return ret; + } + +#ifdef DEBUG_ALLOC + { + BdrvCheckResult result = {0}; + qcow2_check_refcounts(bs, &result, 0); + } +#endif + return 0; +} + +int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) +{ + BDRVQcowState *s = bs->opaque; + QEMUSnapshotInfo *sn_tab, *sn_info; + QCowSnapshot *sn; + int i; + + if (!s->nb_snapshots) { + *psn_tab = NULL; + return s->nb_snapshots; + } + + sn_tab = g_new0(QEMUSnapshotInfo, s->nb_snapshots); + for(i = 0; i < s->nb_snapshots; i++) { + sn_info = sn_tab + i; + sn = s->snapshots + i; + pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), + sn->id_str); + pstrcpy(sn_info->name, sizeof(sn_info->name), + sn->name); + sn_info->vm_state_size = sn->vm_state_size; + sn_info->date_sec = sn->date_sec; + sn_info->date_nsec = sn->date_nsec; + sn_info->vm_clock_nsec = sn->vm_clock_nsec; + } + *psn_tab = sn_tab; + return s->nb_snapshots; +} + +int qcow2_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) +{ + int i, snapshot_index; + BDRVQcowState *s = bs->opaque; + QCowSnapshot *sn; + uint64_t *new_l1_table; + int new_l1_bytes; + int ret; + + assert(bs->read_only); + + /* Search the snapshot */ + snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); + if (snapshot_index < 0) { + error_setg(errp, + "Can't find snapshot"); + return -ENOENT; + } + sn = &s->snapshots[snapshot_index]; + + /* Allocate and read in the snapshot's L1 table */ + if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { + error_setg(errp, "Snapshot L1 table too large"); + return -EFBIG; + } + new_l1_bytes = sn->l1_size * sizeof(uint64_t); + new_l1_table = qemu_try_blockalign(bs->file, + align_offset(new_l1_bytes, 512)); + if (new_l1_table == NULL) { + return -ENOMEM; + } + + ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes); + if (ret < 0) { + error_setg(errp, "Failed to read l1 table for snapshot"); + qemu_vfree(new_l1_table); + return ret; + } + + /* Switch the L1 table */ + qemu_vfree(s->l1_table); + + s->l1_size = sn->l1_size; + s->l1_table_offset = sn->l1_table_offset; + s->l1_table = new_l1_table; + + for(i = 0;i < s->l1_size; i++) { + be64_to_cpus(&s->l1_table[i]); + } + + return 0; +} diff --git a/block/qcow2.c b/block/qcow2.c new file mode 100644 index 0000000..f7b4cc6 --- /dev/null +++ b/block/qcow2.c @@ -0,0 +1,2980 @@ +/* + * Block driver for the QCOW version 2 format + * + * Copyright (c) 2004-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include +#include "qemu/aes.h" +#include "block/qcow2.h" +#include "qemu/error-report.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qmp/qbool.h" +#include "qapi/util.h" +#include "qapi/qmp/types.h" +#include "qapi-event.h" +#include "trace.h" +#include "qemu/option_int.h" + +/* + Differences with QCOW: + + - Support for multiple incremental snapshots. + - Memory management by reference counts. + - Clusters which have a reference count of one have the bit + QCOW_OFLAG_COPIED to optimize write performance. + - Size of compressed clusters is stored in sectors to reduce bit usage + in the cluster offsets. + - Support for storing additional data (such as the VM state) in the + snapshots. + - If a backing store is used, the cluster size is not constrained + (could be backported to QCOW). + - L2 tables have always a size of one cluster. +*/ + + +typedef struct { + uint32_t magic; + uint32_t len; +} QEMU_PACKED QCowExtension; + +#define QCOW2_EXT_MAGIC_END 0 +#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA +#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857 + +static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const QCowHeader *cow_header = (const void *)buf; + + if (buf_size >= sizeof(QCowHeader) && + be32_to_cpu(cow_header->magic) == QCOW_MAGIC && + be32_to_cpu(cow_header->version) >= 2) + return 100; + else + return 0; +} + + +/* + * read qcow2 extension and fill bs + * start reading from start_offset + * finish reading upon magic of value 0 or when end_offset reached + * unknown magic is skipped (future extension this version knows nothing about) + * return 0 upon success, non-0 otherwise + */ +static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, + uint64_t end_offset, void **p_feature_table, + Error **errp) +{ + BDRVQcowState *s = bs->opaque; + QCowExtension ext; + uint64_t offset; + int ret; + +#ifdef DEBUG_EXT + printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset); +#endif + offset = start_offset; + while (offset < end_offset) { + +#ifdef DEBUG_EXT + /* Sanity check */ + if (offset > s->cluster_size) + printf("qcow2_read_extension: suspicious offset %lu\n", offset); + + printf("attempting to read extended header in offset %lu\n", offset); +#endif + + ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext)); + if (ret < 0) { + error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " + "pread fail from offset %" PRIu64, offset); + return 1; + } + be32_to_cpus(&ext.magic); + be32_to_cpus(&ext.len); + offset += sizeof(ext); +#ifdef DEBUG_EXT + printf("ext.magic = 0x%x\n", ext.magic); +#endif + if (offset > end_offset || ext.len > end_offset - offset) { + error_setg(errp, "Header extension too large"); + return -EINVAL; + } + + switch (ext.magic) { + case QCOW2_EXT_MAGIC_END: + return 0; + + case QCOW2_EXT_MAGIC_BACKING_FORMAT: + if (ext.len >= sizeof(bs->backing_format)) { + error_setg(errp, "ERROR: ext_backing_format: len=%" PRIu32 + " too large (>=%zu)", ext.len, + sizeof(bs->backing_format)); + return 2; + } + ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " + "Could not read format name"); + return 3; + } + bs->backing_format[ext.len] = '\0'; + s->image_backing_format = g_strdup(bs->backing_format); +#ifdef DEBUG_EXT + printf("Qcow2: Got format extension %s\n", bs->backing_format); +#endif + break; + + case QCOW2_EXT_MAGIC_FEATURE_TABLE: + if (p_feature_table != NULL) { + void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); + ret = bdrv_pread(bs->file, offset , feature_table, ext.len); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " + "Could not read table"); + return ret; + } + + *p_feature_table = feature_table; + } + break; + + default: + /* unknown magic - save it in case we need to rewrite the header */ + { + Qcow2UnknownHeaderExtension *uext; + + uext = g_malloc0(sizeof(*uext) + ext.len); + uext->magic = ext.magic; + uext->len = ext.len; + QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next); + + ret = bdrv_pread(bs->file, offset , uext->data, uext->len); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: unknown extension: " + "Could not read data"); + return ret; + } + } + break; + } + + offset += ((ext.len + 7) & ~7); + } + + return 0; +} + +static void cleanup_unknown_header_ext(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + Qcow2UnknownHeaderExtension *uext, *next; + + QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) { + QLIST_REMOVE(uext, next); + g_free(uext); + } +} + +static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs, + Error **errp, const char *fmt, ...) +{ + char msg[64]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bdrv_get_device_or_node_name(bs), "qcow2", msg); +} + +static void report_unsupported_feature(BlockDriverState *bs, + Error **errp, Qcow2Feature *table, uint64_t mask) +{ + char *features = g_strdup(""); + char *old; + + while (table && table->name[0] != '\0') { + if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { + if (mask & (1ULL << table->bit)) { + old = features; + features = g_strdup_printf("%s%s%.46s", old, *old ? ", " : "", + table->name); + g_free(old); + mask &= ~(1ULL << table->bit); + } + } + table++; + } + + if (mask) { + old = features; + features = g_strdup_printf("%s%sUnknown incompatible feature: %" PRIx64, + old, *old ? ", " : "", mask); + g_free(old); + } + + report_unsupported(bs, errp, "%s", features); + g_free(features); +} + +/* + * Sets the dirty bit and flushes afterwards if necessary. + * + * The incompatible_features bit is only set if the image file header was + * updated successfully. Therefore it is not required to check the return + * value of this function. + */ +int qcow2_mark_dirty(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint64_t val; + int ret; + + assert(s->qcow_version >= 3); + + if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { + return 0; /* already dirty */ + } + + val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY); + ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features), + &val, sizeof(val)); + if (ret < 0) { + return ret; + } + ret = bdrv_flush(bs->file); + if (ret < 0) { + return ret; + } + + /* Only treat image as dirty if the header was updated successfully */ + s->incompatible_features |= QCOW2_INCOMPAT_DIRTY; + return 0; +} + +/* + * Clears the dirty bit and flushes before if necessary. Only call this + * function when there are no pending requests, it does not guard against + * concurrent requests dirtying the image. + */ +static int qcow2_mark_clean(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + + if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { + int ret; + + s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY; + + ret = bdrv_flush(bs); + if (ret < 0) { + return ret; + } + + return qcow2_update_header(bs); + } + return 0; +} + +/* + * Marks the image as corrupt. + */ +int qcow2_mark_corrupt(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + + s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT; + return qcow2_update_header(bs); +} + +/* + * Marks the image as consistent, i.e., unsets the corrupt bit, and flushes + * before if necessary. + */ +int qcow2_mark_consistent(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + + if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { + int ret = bdrv_flush(bs); + if (ret < 0) { + return ret; + } + + s->incompatible_features &= ~QCOW2_INCOMPAT_CORRUPT; + return qcow2_update_header(bs); + } + return 0; +} + +static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) +{ + int ret = qcow2_check_refcounts(bs, result, fix); + if (ret < 0) { + return ret; + } + + if (fix && result->check_errors == 0 && result->corruptions == 0) { + ret = qcow2_mark_clean(bs); + if (ret < 0) { + return ret; + } + return qcow2_mark_consistent(bs); + } + return ret; +} + +static int validate_table_offset(BlockDriverState *bs, uint64_t offset, + uint64_t entries, size_t entry_len) +{ + BDRVQcowState *s = bs->opaque; + uint64_t size; + + /* Use signed INT64_MAX as the maximum even for uint64_t header fields, + * because values will be passed to qemu functions taking int64_t. */ + if (entries > INT64_MAX / entry_len) { + return -EINVAL; + } + + size = entries * entry_len; + + if (INT64_MAX - size < offset) { + return -EINVAL; + } + + /* Tables must be cluster aligned */ + if (offset & (s->cluster_size - 1)) { + return -EINVAL; + } + + return 0; +} + +static QemuOptsList qcow2_runtime_opts = { + .name = "qcow2", + .head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head), + .desc = { + { + .name = QCOW2_OPT_LAZY_REFCOUNTS, + .type = QEMU_OPT_BOOL, + .help = "Postpone refcount updates", + }, + { + .name = QCOW2_OPT_DISCARD_REQUEST, + .type = QEMU_OPT_BOOL, + .help = "Pass guest discard requests to the layer below", + }, + { + .name = QCOW2_OPT_DISCARD_SNAPSHOT, + .type = QEMU_OPT_BOOL, + .help = "Generate discard requests when snapshot related space " + "is freed", + }, + { + .name = QCOW2_OPT_DISCARD_OTHER, + .type = QEMU_OPT_BOOL, + .help = "Generate discard requests when other clusters are freed", + }, + { + .name = QCOW2_OPT_OVERLAP, + .type = QEMU_OPT_STRING, + .help = "Selects which overlap checks to perform from a range of " + "templates (none, constant, cached, all)", + }, + { + .name = QCOW2_OPT_OVERLAP_TEMPLATE, + .type = QEMU_OPT_STRING, + .help = "Selects which overlap checks to perform from a range of " + "templates (none, constant, cached, all)", + }, + { + .name = QCOW2_OPT_OVERLAP_MAIN_HEADER, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into the main qcow2 header", + }, + { + .name = QCOW2_OPT_OVERLAP_ACTIVE_L1, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into the active L1 table", + }, + { + .name = QCOW2_OPT_OVERLAP_ACTIVE_L2, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into an active L2 table", + }, + { + .name = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into the refcount table", + }, + { + .name = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into a refcount block", + }, + { + .name = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into the snapshot table", + }, + { + .name = QCOW2_OPT_OVERLAP_INACTIVE_L1, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into an inactive L1 table", + }, + { + .name = QCOW2_OPT_OVERLAP_INACTIVE_L2, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into an inactive L2 table", + }, + { + .name = QCOW2_OPT_CACHE_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Maximum combined metadata (L2 tables and refcount blocks) " + "cache size", + }, + { + .name = QCOW2_OPT_L2_CACHE_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Maximum L2 table cache size", + }, + { + .name = QCOW2_OPT_REFCOUNT_CACHE_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Maximum refcount block cache size", + }, + { /* end of list */ } + }, +}; + +static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = { + [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER, + [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1, + [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2, + [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, + [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, + [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, + [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1, + [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, +}; + +static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size, + uint64_t *refcount_cache_size, Error **errp) +{ + uint64_t combined_cache_size; + bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; + + combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE); + l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE); + refcount_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE); + + combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0); + *l2_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE, 0); + *refcount_cache_size = qemu_opt_get_size(opts, + QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0); + + if (combined_cache_size_set) { + if (l2_cache_size_set && refcount_cache_size_set) { + error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE + " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " + "the same time"); + return; + } else if (*l2_cache_size > combined_cache_size) { + error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " + QCOW2_OPT_CACHE_SIZE); + return; + } else if (*refcount_cache_size > combined_cache_size) { + error_setg(errp, QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not exceed " + QCOW2_OPT_CACHE_SIZE); + return; + } + + if (l2_cache_size_set) { + *refcount_cache_size = combined_cache_size - *l2_cache_size; + } else if (refcount_cache_size_set) { + *l2_cache_size = combined_cache_size - *refcount_cache_size; + } else { + *refcount_cache_size = combined_cache_size + / (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1); + *l2_cache_size = combined_cache_size - *refcount_cache_size; + } + } else { + if (!l2_cache_size_set && !refcount_cache_size_set) { + *l2_cache_size = DEFAULT_L2_CACHE_BYTE_SIZE; + *refcount_cache_size = *l2_cache_size + / DEFAULT_L2_REFCOUNT_SIZE_RATIO; + } else if (!l2_cache_size_set) { + *l2_cache_size = *refcount_cache_size + * DEFAULT_L2_REFCOUNT_SIZE_RATIO; + } else if (!refcount_cache_size_set) { + *refcount_cache_size = *l2_cache_size + / DEFAULT_L2_REFCOUNT_SIZE_RATIO; + } + } +} + +static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVQcowState *s = bs->opaque; + unsigned int len, i; + int ret = 0; + QCowHeader header; + QemuOpts *opts = NULL; + Error *local_err = NULL; + uint64_t ext_end; + uint64_t l1_vm_state_index; + const char *opt_overlap_check, *opt_overlap_check_template; + int overlap_check_template = 0; + uint64_t l2_cache_size, refcount_cache_size; + + ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read qcow2 header"); + goto fail; + } + be32_to_cpus(&header.magic); + be32_to_cpus(&header.version); + be64_to_cpus(&header.backing_file_offset); + be32_to_cpus(&header.backing_file_size); + be64_to_cpus(&header.size); + be32_to_cpus(&header.cluster_bits); + be32_to_cpus(&header.crypt_method); + be64_to_cpus(&header.l1_table_offset); + be32_to_cpus(&header.l1_size); + be64_to_cpus(&header.refcount_table_offset); + be32_to_cpus(&header.refcount_table_clusters); + be64_to_cpus(&header.snapshots_offset); + be32_to_cpus(&header.nb_snapshots); + + if (header.magic != QCOW_MAGIC) { + error_setg(errp, "Image is not in qcow2 format"); + ret = -EINVAL; + goto fail; + } + if (header.version < 2 || header.version > 3) { + report_unsupported(bs, errp, "QCOW version %" PRIu32, header.version); + ret = -ENOTSUP; + goto fail; + } + + s->qcow_version = header.version; + + /* Initialise cluster size */ + if (header.cluster_bits < MIN_CLUSTER_BITS || + header.cluster_bits > MAX_CLUSTER_BITS) { + error_setg(errp, "Unsupported cluster size: 2^%" PRIu32, + header.cluster_bits); + ret = -EINVAL; + goto fail; + } + + s->cluster_bits = header.cluster_bits; + s->cluster_size = 1 << s->cluster_bits; + s->cluster_sectors = 1 << (s->cluster_bits - 9); + + /* Initialise version 3 header fields */ + if (header.version == 2) { + header.incompatible_features = 0; + header.compatible_features = 0; + header.autoclear_features = 0; + header.refcount_order = 4; + header.header_length = 72; + } else { + be64_to_cpus(&header.incompatible_features); + be64_to_cpus(&header.compatible_features); + be64_to_cpus(&header.autoclear_features); + be32_to_cpus(&header.refcount_order); + be32_to_cpus(&header.header_length); + + if (header.header_length < 104) { + error_setg(errp, "qcow2 header too short"); + ret = -EINVAL; + goto fail; + } + } + + if (header.header_length > s->cluster_size) { + error_setg(errp, "qcow2 header exceeds cluster size"); + ret = -EINVAL; + goto fail; + } + + if (header.header_length > sizeof(header)) { + s->unknown_header_fields_size = header.header_length - sizeof(header); + s->unknown_header_fields = g_malloc(s->unknown_header_fields_size); + ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, + s->unknown_header_fields_size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read unknown qcow2 header " + "fields"); + goto fail; + } + } + + if (header.backing_file_offset > s->cluster_size) { + error_setg(errp, "Invalid backing file offset"); + ret = -EINVAL; + goto fail; + } + + if (header.backing_file_offset) { + ext_end = header.backing_file_offset; + } else { + ext_end = 1 << header.cluster_bits; + } + + /* Handle feature bits */ + s->incompatible_features = header.incompatible_features; + s->compatible_features = header.compatible_features; + s->autoclear_features = header.autoclear_features; + + if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) { + void *feature_table = NULL; + qcow2_read_extensions(bs, header.header_length, ext_end, + &feature_table, NULL); + report_unsupported_feature(bs, errp, feature_table, + s->incompatible_features & + ~QCOW2_INCOMPAT_MASK); + ret = -ENOTSUP; + g_free(feature_table); + goto fail; + } + + if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { + /* Corrupt images may not be written to unless they are being repaired + */ + if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { + error_setg(errp, "qcow2: Image is corrupt; cannot be opened " + "read/write"); + ret = -EACCES; + goto fail; + } + } + + /* Check support for various header values */ + if (header.refcount_order > 6) { + error_setg(errp, "Reference count entry width too large; may not " + "exceed 64 bits"); + ret = -EINVAL; + goto fail; + } + s->refcount_order = header.refcount_order; + s->refcount_bits = 1 << s->refcount_order; + s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); + s->refcount_max += s->refcount_max - 1; + + if (header.crypt_method > QCOW_CRYPT_AES) { + error_setg(errp, "Unsupported encryption method: %" PRIu32, + header.crypt_method); + ret = -EINVAL; + goto fail; + } + s->crypt_method_header = header.crypt_method; + if (s->crypt_method_header) { + bs->encrypted = 1; + } + + s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */ + s->l2_size = 1 << s->l2_bits; + /* 2^(s->refcount_order - 3) is the refcount width in bytes */ + s->refcount_block_bits = s->cluster_bits - (s->refcount_order - 3); + s->refcount_block_size = 1 << s->refcount_block_bits; + bs->total_sectors = header.size / 512; + s->csize_shift = (62 - (s->cluster_bits - 8)); + s->csize_mask = (1 << (s->cluster_bits - 8)) - 1; + s->cluster_offset_mask = (1LL << s->csize_shift) - 1; + + s->refcount_table_offset = header.refcount_table_offset; + s->refcount_table_size = + header.refcount_table_clusters << (s->cluster_bits - 3); + + if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) { + error_setg(errp, "Reference count table too large"); + ret = -EINVAL; + goto fail; + } + + ret = validate_table_offset(bs, s->refcount_table_offset, + s->refcount_table_size, sizeof(uint64_t)); + if (ret < 0) { + error_setg(errp, "Invalid reference count table offset"); + goto fail; + } + + /* Snapshot table offset/length */ + if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) { + error_setg(errp, "Too many snapshots"); + ret = -EINVAL; + goto fail; + } + + ret = validate_table_offset(bs, header.snapshots_offset, + header.nb_snapshots, + sizeof(QCowSnapshotHeader)); + if (ret < 0) { + error_setg(errp, "Invalid snapshot table offset"); + goto fail; + } + + /* read the level 1 table */ + if (header.l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { + error_setg(errp, "Active L1 table too large"); + ret = -EFBIG; + goto fail; + } + s->l1_size = header.l1_size; + + l1_vm_state_index = size_to_l1(s, header.size); + if (l1_vm_state_index > INT_MAX) { + error_setg(errp, "Image is too big"); + ret = -EFBIG; + goto fail; + } + s->l1_vm_state_index = l1_vm_state_index; + + /* the L1 table must contain at least enough entries to put + header.size bytes */ + if (s->l1_size < s->l1_vm_state_index) { + error_setg(errp, "L1 table is too small"); + ret = -EINVAL; + goto fail; + } + + ret = validate_table_offset(bs, header.l1_table_offset, + header.l1_size, sizeof(uint64_t)); + if (ret < 0) { + error_setg(errp, "Invalid L1 table offset"); + goto fail; + } + s->l1_table_offset = header.l1_table_offset; + + + if (s->l1_size > 0) { + s->l1_table = qemu_try_blockalign(bs->file, + align_offset(s->l1_size * sizeof(uint64_t), 512)); + if (s->l1_table == NULL) { + error_setg(errp, "Could not allocate L1 table"); + ret = -ENOMEM; + goto fail; + } + ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read L1 table"); + goto fail; + } + for(i = 0;i < s->l1_size; i++) { + be64_to_cpus(&s->l1_table[i]); + } + } + + /* get L2 table/refcount block cache size from command line options */ + opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + read_cache_sizes(opts, &l2_cache_size, &refcount_cache_size, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + l2_cache_size /= s->cluster_size; + if (l2_cache_size < MIN_L2_CACHE_SIZE) { + l2_cache_size = MIN_L2_CACHE_SIZE; + } + if (l2_cache_size > INT_MAX) { + error_setg(errp, "L2 cache size too big"); + ret = -EINVAL; + goto fail; + } + + refcount_cache_size /= s->cluster_size; + if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) { + refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE; + } + if (refcount_cache_size > INT_MAX) { + error_setg(errp, "Refcount cache size too big"); + ret = -EINVAL; + goto fail; + } + + /* alloc L2 table/refcount block cache */ + s->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); + s->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); + if (s->l2_table_cache == NULL || s->refcount_block_cache == NULL) { + error_setg(errp, "Could not allocate metadata caches"); + ret = -ENOMEM; + goto fail; + } + + s->cluster_cache = g_malloc(s->cluster_size); + /* one more sector for decompressed data alignment */ + s->cluster_data = qemu_try_blockalign(bs->file, QCOW_MAX_CRYPT_CLUSTERS + * s->cluster_size + 512); + if (s->cluster_data == NULL) { + error_setg(errp, "Could not allocate temporary cluster buffer"); + ret = -ENOMEM; + goto fail; + } + + s->cluster_cache_offset = -1; + s->flags = flags; + + ret = qcow2_refcount_init(bs); + if (ret != 0) { + error_setg_errno(errp, -ret, "Could not initialize refcount handling"); + goto fail; + } + + QLIST_INIT(&s->cluster_allocs); + QTAILQ_INIT(&s->discards); + + /* read qcow2 extensions */ + if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL, + &local_err)) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + /* read the backing file name */ + if (header.backing_file_offset != 0) { + len = header.backing_file_size; + if (len > MIN(1023, s->cluster_size - header.backing_file_offset) || + len >= sizeof(bs->backing_file)) { + error_setg(errp, "Backing file name too long"); + ret = -EINVAL; + goto fail; + } + ret = bdrv_pread(bs->file, header.backing_file_offset, + bs->backing_file, len); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read backing file name"); + goto fail; + } + bs->backing_file[len] = '\0'; + s->image_backing_file = g_strdup(bs->backing_file); + } + + /* Internal snapshots */ + s->snapshots_offset = header.snapshots_offset; + s->nb_snapshots = header.nb_snapshots; + + ret = qcow2_read_snapshots(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read snapshots"); + goto fail; + } + + /* Clear unknown autoclear feature bits */ + if (!bs->read_only && !(flags & BDRV_O_INCOMING) && s->autoclear_features) { + s->autoclear_features = 0; + ret = qcow2_update_header(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not update qcow2 header"); + goto fail; + } + } + + /* Initialise locks */ + qemu_co_mutex_init(&s->lock); + + /* Repair image if dirty */ + if (!(flags & (BDRV_O_CHECK | BDRV_O_INCOMING)) && !bs->read_only && + (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) { + BdrvCheckResult result = {0}; + + ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not repair dirty image"); + goto fail; + } + } + + /* Enable lazy_refcounts according to image and command line options */ + s->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS, + (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)); + + s->discard_passthrough[QCOW2_DISCARD_NEVER] = false; + s->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true; + s->discard_passthrough[QCOW2_DISCARD_REQUEST] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST, + flags & BDRV_O_UNMAP); + s->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true); + s->discard_passthrough[QCOW2_DISCARD_OTHER] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); + + opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP); + opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE); + if (opt_overlap_check_template && opt_overlap_check && + strcmp(opt_overlap_check_template, opt_overlap_check)) + { + error_setg(errp, "Conflicting values for qcow2 options '" + QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE + "' ('%s')", opt_overlap_check, opt_overlap_check_template); + ret = -EINVAL; + goto fail; + } + if (!opt_overlap_check) { + opt_overlap_check = opt_overlap_check_template ?: "cached"; + } + + if (!strcmp(opt_overlap_check, "none")) { + overlap_check_template = 0; + } else if (!strcmp(opt_overlap_check, "constant")) { + overlap_check_template = QCOW2_OL_CONSTANT; + } else if (!strcmp(opt_overlap_check, "cached")) { + overlap_check_template = QCOW2_OL_CACHED; + } else if (!strcmp(opt_overlap_check, "all")) { + overlap_check_template = QCOW2_OL_ALL; + } else { + error_setg(errp, "Unsupported value '%s' for qcow2 option " + "'overlap-check'. Allowed are either of the following: " + "none, constant, cached, all", opt_overlap_check); + ret = -EINVAL; + goto fail; + } + + s->overlap_check = 0; + for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) { + /* overlap-check defines a template bitmask, but every flag may be + * overwritten through the associated boolean option */ + s->overlap_check |= + qemu_opt_get_bool(opts, overlap_bool_option_names[i], + overlap_check_template & (1 << i)) << i; + } + + qemu_opts_del(opts); + opts = NULL; + + if (s->use_lazy_refcounts && s->qcow_version < 3) { + error_setg(errp, "Lazy refcounts require a qcow2 image with at least " + "qemu 1.1 compatibility level"); + ret = -EINVAL; + goto fail; + } + +#ifdef DEBUG_ALLOC + { + BdrvCheckResult result = {0}; + qcow2_check_refcounts(bs, &result, 0); + } +#endif + return ret; + + fail: + qemu_opts_del(opts); + g_free(s->unknown_header_fields); + cleanup_unknown_header_ext(bs); + qcow2_free_snapshots(bs); + qcow2_refcount_close(bs); + qemu_vfree(s->l1_table); + /* else pre-write overlap checks in cache_destroy may crash */ + s->l1_table = NULL; + if (s->l2_table_cache) { + qcow2_cache_destroy(bs, s->l2_table_cache); + } + if (s->refcount_block_cache) { + qcow2_cache_destroy(bs, s->refcount_block_cache); + } + g_free(s->cluster_cache); + qemu_vfree(s->cluster_data); + return ret; +} + +static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BDRVQcowState *s = bs->opaque; + + bs->bl.write_zeroes_alignment = s->cluster_sectors; +} + +static int qcow2_set_key(BlockDriverState *bs, const char *key) +{ + BDRVQcowState *s = bs->opaque; + uint8_t keybuf[16]; + int len, i; + + memset(keybuf, 0, 16); + len = strlen(key); + if (len > 16) + len = 16; + /* XXX: we could compress the chars to 7 bits to increase + entropy */ + for(i = 0;i < len;i++) { + keybuf[i] = key[i]; + } + assert(bs->encrypted); + s->crypt_method = s->crypt_method_header; + + if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) + return -1; + if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) + return -1; +#if 0 + /* test */ + { + uint8_t in[16]; + uint8_t out[16]; + uint8_t tmp[16]; + for(i=0;i<16;i++) + in[i] = i; + AES_encrypt(in, tmp, &s->aes_encrypt_key); + AES_decrypt(tmp, out, &s->aes_decrypt_key); + for(i = 0; i < 16; i++) + printf(" %02x", tmp[i]); + printf("\n"); + for(i = 0; i < 16; i++) + printf(" %02x", out[i]); + printf("\n"); + } +#endif + return 0; +} + +/* We have no actual commit/abort logic for qcow2, but we need to write out any + * unwritten data if we reopen read-only. */ +static int qcow2_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + int ret; + + if ((state->flags & BDRV_O_RDWR) == 0) { + ret = bdrv_flush(state->bs); + if (ret < 0) { + return ret; + } + + ret = qcow2_mark_clean(state->bs); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + BDRVQcowState *s = bs->opaque; + uint64_t cluster_offset; + int index_in_cluster, ret; + int64_t status = 0; + + *pnum = nb_sectors; + qemu_co_mutex_lock(&s->lock); + ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset); + qemu_co_mutex_unlock(&s->lock); + if (ret < 0) { + return ret; + } + + if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED && + !s->crypt_method) { + index_in_cluster = sector_num & (s->cluster_sectors - 1); + cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); + status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; + } + if (ret == QCOW2_CLUSTER_ZERO) { + status |= BDRV_BLOCK_ZERO; + } else if (ret != QCOW2_CLUSTER_UNALLOCATED) { + status |= BDRV_BLOCK_DATA; + } + return status; +} + +/* handle reading after the end of the backing file */ +int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t sector_num, int nb_sectors) +{ + int n1; + if ((sector_num + nb_sectors) <= bs->total_sectors) + return nb_sectors; + if (sector_num >= bs->total_sectors) + n1 = 0; + else + n1 = bs->total_sectors - sector_num; + + qemu_iovec_memset(qiov, 512 * n1, 0, 512 * (nb_sectors - n1)); + + return n1; +} + +static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, + int remaining_sectors, QEMUIOVector *qiov) +{ + BDRVQcowState *s = bs->opaque; + int index_in_cluster, n1; + int ret; + int cur_nr_sectors; /* number of sectors in current iteration */ + uint64_t cluster_offset = 0; + uint64_t bytes_done = 0; + QEMUIOVector hd_qiov; + uint8_t *cluster_data = NULL; + + qemu_iovec_init(&hd_qiov, qiov->niov); + + qemu_co_mutex_lock(&s->lock); + + while (remaining_sectors != 0) { + + /* prepare next request */ + cur_nr_sectors = remaining_sectors; + if (s->crypt_method) { + cur_nr_sectors = MIN(cur_nr_sectors, + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); + } + + ret = qcow2_get_cluster_offset(bs, sector_num << 9, + &cur_nr_sectors, &cluster_offset); + if (ret < 0) { + goto fail; + } + + index_in_cluster = sector_num & (s->cluster_sectors - 1); + + qemu_iovec_reset(&hd_qiov); + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, + cur_nr_sectors * 512); + + switch (ret) { + case QCOW2_CLUSTER_UNALLOCATED: + + if (bs->backing_hd) { + /* read from the base image */ + n1 = qcow2_backing_read1(bs->backing_hd, &hd_qiov, + sector_num, cur_nr_sectors); + if (n1 > 0) { + QEMUIOVector local_qiov; + + qemu_iovec_init(&local_qiov, hd_qiov.niov); + qemu_iovec_concat(&local_qiov, &hd_qiov, 0, + n1 * BDRV_SECTOR_SIZE); + + BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->backing_hd, sector_num, + n1, &local_qiov); + qemu_co_mutex_lock(&s->lock); + + qemu_iovec_destroy(&local_qiov); + + if (ret < 0) { + goto fail; + } + } + } else { + /* Note: in this case, no need to wait */ + qemu_iovec_memset(&hd_qiov, 0, 0, 512 * cur_nr_sectors); + } + break; + + case QCOW2_CLUSTER_ZERO: + qemu_iovec_memset(&hd_qiov, 0, 0, 512 * cur_nr_sectors); + break; + + case QCOW2_CLUSTER_COMPRESSED: + /* add AIO support for compressed blocks ? */ + ret = qcow2_decompress_cluster(bs, cluster_offset); + if (ret < 0) { + goto fail; + } + + qemu_iovec_from_buf(&hd_qiov, 0, + s->cluster_cache + index_in_cluster * 512, + 512 * cur_nr_sectors); + break; + + case QCOW2_CLUSTER_NORMAL: + if ((cluster_offset & 511) != 0) { + ret = -EIO; + goto fail; + } + + if (bs->encrypted) { + assert(s->crypt_method); + + /* + * For encrypted images, read everything into a temporary + * contiguous buffer on which the AES functions can work. + */ + if (!cluster_data) { + cluster_data = + qemu_try_blockalign(bs->file, QCOW_MAX_CRYPT_CLUSTERS + * s->cluster_size); + if (cluster_data == NULL) { + ret = -ENOMEM; + goto fail; + } + } + + assert(cur_nr_sectors <= + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); + qemu_iovec_reset(&hd_qiov); + qemu_iovec_add(&hd_qiov, cluster_data, + 512 * cur_nr_sectors); + } + + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->file, + (cluster_offset >> 9) + index_in_cluster, + cur_nr_sectors, &hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto fail; + } + if (bs->encrypted) { + assert(s->crypt_method); + qcow2_encrypt_sectors(s, sector_num, cluster_data, + cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key); + qemu_iovec_from_buf(qiov, bytes_done, + cluster_data, 512 * cur_nr_sectors); + } + break; + + default: + g_assert_not_reached(); + ret = -EIO; + goto fail; + } + + remaining_sectors -= cur_nr_sectors; + sector_num += cur_nr_sectors; + bytes_done += cur_nr_sectors * 512; + } + ret = 0; + +fail: + qemu_co_mutex_unlock(&s->lock); + + qemu_iovec_destroy(&hd_qiov); + qemu_vfree(cluster_data); + + return ret; +} + +static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, + int64_t sector_num, + int remaining_sectors, + QEMUIOVector *qiov) +{ + BDRVQcowState *s = bs->opaque; + int index_in_cluster; + int ret; + int cur_nr_sectors; /* number of sectors in current iteration */ + uint64_t cluster_offset; + QEMUIOVector hd_qiov; + uint64_t bytes_done = 0; + uint8_t *cluster_data = NULL; + QCowL2Meta *l2meta = NULL; + + trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num, + remaining_sectors); + + qemu_iovec_init(&hd_qiov, qiov->niov); + + s->cluster_cache_offset = -1; /* disable compressed cache */ + + qemu_co_mutex_lock(&s->lock); + + while (remaining_sectors != 0) { + + l2meta = NULL; + + trace_qcow2_writev_start_part(qemu_coroutine_self()); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + cur_nr_sectors = remaining_sectors; + if (bs->encrypted && + cur_nr_sectors > + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster) { + cur_nr_sectors = + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster; + } + + ret = qcow2_alloc_cluster_offset(bs, sector_num << 9, + &cur_nr_sectors, &cluster_offset, &l2meta); + if (ret < 0) { + goto fail; + } + + assert((cluster_offset & 511) == 0); + + qemu_iovec_reset(&hd_qiov); + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, + cur_nr_sectors * 512); + + if (bs->encrypted) { + assert(s->crypt_method); + if (!cluster_data) { + cluster_data = qemu_try_blockalign(bs->file, + QCOW_MAX_CRYPT_CLUSTERS + * s->cluster_size); + if (cluster_data == NULL) { + ret = -ENOMEM; + goto fail; + } + } + + assert(hd_qiov.size <= + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); + qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size); + + qcow2_encrypt_sectors(s, sector_num, cluster_data, + cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key); + + qemu_iovec_reset(&hd_qiov); + qemu_iovec_add(&hd_qiov, cluster_data, + cur_nr_sectors * 512); + } + + ret = qcow2_pre_write_overlap_check(bs, 0, + cluster_offset + index_in_cluster * BDRV_SECTOR_SIZE, + cur_nr_sectors * BDRV_SECTOR_SIZE); + if (ret < 0) { + goto fail; + } + + qemu_co_mutex_unlock(&s->lock); + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + trace_qcow2_writev_data(qemu_coroutine_self(), + (cluster_offset >> 9) + index_in_cluster); + ret = bdrv_co_writev(bs->file, + (cluster_offset >> 9) + index_in_cluster, + cur_nr_sectors, &hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto fail; + } + + while (l2meta != NULL) { + QCowL2Meta *next; + + ret = qcow2_alloc_cluster_link_l2(bs, l2meta); + if (ret < 0) { + goto fail; + } + + /* Take the request off the list of running requests */ + if (l2meta->nb_clusters != 0) { + QLIST_REMOVE(l2meta, next_in_flight); + } + + qemu_co_queue_restart_all(&l2meta->dependent_requests); + + next = l2meta->next; + g_free(l2meta); + l2meta = next; + } + + remaining_sectors -= cur_nr_sectors; + sector_num += cur_nr_sectors; + bytes_done += cur_nr_sectors * 512; + trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_nr_sectors); + } + ret = 0; + +fail: + qemu_co_mutex_unlock(&s->lock); + + while (l2meta != NULL) { + QCowL2Meta *next; + + if (l2meta->nb_clusters != 0) { + QLIST_REMOVE(l2meta, next_in_flight); + } + qemu_co_queue_restart_all(&l2meta->dependent_requests); + + next = l2meta->next; + g_free(l2meta); + l2meta = next; + } + + qemu_iovec_destroy(&hd_qiov); + qemu_vfree(cluster_data); + trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); + + return ret; +} + +static void qcow2_close(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + qemu_vfree(s->l1_table); + /* else pre-write overlap checks in cache_destroy may crash */ + s->l1_table = NULL; + + if (!(bs->open_flags & BDRV_O_INCOMING)) { + int ret1, ret2; + + ret1 = qcow2_cache_flush(bs, s->l2_table_cache); + ret2 = qcow2_cache_flush(bs, s->refcount_block_cache); + + if (ret1) { + error_report("Failed to flush the L2 table cache: %s", + strerror(-ret1)); + } + if (ret2) { + error_report("Failed to flush the refcount block cache: %s", + strerror(-ret2)); + } + + if (!ret1 && !ret2) { + qcow2_mark_clean(bs); + } + } + + qcow2_cache_destroy(bs, s->l2_table_cache); + qcow2_cache_destroy(bs, s->refcount_block_cache); + + g_free(s->unknown_header_fields); + cleanup_unknown_header_ext(bs); + + g_free(s->image_backing_file); + g_free(s->image_backing_format); + + g_free(s->cluster_cache); + qemu_vfree(s->cluster_data); + qcow2_refcount_close(bs); + qcow2_free_snapshots(bs); +} + +static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) +{ + BDRVQcowState *s = bs->opaque; + int flags = s->flags; + AES_KEY aes_encrypt_key; + AES_KEY aes_decrypt_key; + uint32_t crypt_method = 0; + QDict *options; + Error *local_err = NULL; + int ret; + + /* + * Backing files are read-only which makes all of their metadata immutable, + * that means we don't have to worry about reopening them here. + */ + + if (bs->encrypted) { + assert(s->crypt_method); + crypt_method = s->crypt_method; + memcpy(&aes_encrypt_key, &s->aes_encrypt_key, sizeof(aes_encrypt_key)); + memcpy(&aes_decrypt_key, &s->aes_decrypt_key, sizeof(aes_decrypt_key)); + } + + qcow2_close(bs); + + bdrv_invalidate_cache(bs->file, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + memset(s, 0, sizeof(BDRVQcowState)); + options = qdict_clone_shallow(bs->options); + + ret = qcow2_open(bs, options, flags, &local_err); + QDECREF(options); + if (local_err) { + error_setg(errp, "Could not reopen qcow2 layer: %s", + error_get_pretty(local_err)); + error_free(local_err); + return; + } else if (ret < 0) { + error_setg_errno(errp, -ret, "Could not reopen qcow2 layer"); + return; + } + + if (bs->encrypted) { + s->crypt_method = crypt_method; + memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key)); + memcpy(&s->aes_decrypt_key, &aes_decrypt_key, sizeof(aes_decrypt_key)); + } +} + +static size_t header_ext_add(char *buf, uint32_t magic, const void *s, + size_t len, size_t buflen) +{ + QCowExtension *ext_backing_fmt = (QCowExtension*) buf; + size_t ext_len = sizeof(QCowExtension) + ((len + 7) & ~7); + + if (buflen < ext_len) { + return -ENOSPC; + } + + *ext_backing_fmt = (QCowExtension) { + .magic = cpu_to_be32(magic), + .len = cpu_to_be32(len), + }; + memcpy(buf + sizeof(QCowExtension), s, len); + + return ext_len; +} + +/* + * Updates the qcow2 header, including the variable length parts of it, i.e. + * the backing file name and all extensions. qcow2 was not designed to allow + * such changes, so if we run out of space (we can only use the first cluster) + * this function may fail. + * + * Returns 0 on success, -errno in error cases. + */ +int qcow2_update_header(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + QCowHeader *header; + char *buf; + size_t buflen = s->cluster_size; + int ret; + uint64_t total_size; + uint32_t refcount_table_clusters; + size_t header_length; + Qcow2UnknownHeaderExtension *uext; + + buf = qemu_blockalign(bs, buflen); + + /* Header structure */ + header = (QCowHeader*) buf; + + if (buflen < sizeof(*header)) { + ret = -ENOSPC; + goto fail; + } + + header_length = sizeof(*header) + s->unknown_header_fields_size; + total_size = bs->total_sectors * BDRV_SECTOR_SIZE; + refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); + + *header = (QCowHeader) { + /* Version 2 fields */ + .magic = cpu_to_be32(QCOW_MAGIC), + .version = cpu_to_be32(s->qcow_version), + .backing_file_offset = 0, + .backing_file_size = 0, + .cluster_bits = cpu_to_be32(s->cluster_bits), + .size = cpu_to_be64(total_size), + .crypt_method = cpu_to_be32(s->crypt_method_header), + .l1_size = cpu_to_be32(s->l1_size), + .l1_table_offset = cpu_to_be64(s->l1_table_offset), + .refcount_table_offset = cpu_to_be64(s->refcount_table_offset), + .refcount_table_clusters = cpu_to_be32(refcount_table_clusters), + .nb_snapshots = cpu_to_be32(s->nb_snapshots), + .snapshots_offset = cpu_to_be64(s->snapshots_offset), + + /* Version 3 fields */ + .incompatible_features = cpu_to_be64(s->incompatible_features), + .compatible_features = cpu_to_be64(s->compatible_features), + .autoclear_features = cpu_to_be64(s->autoclear_features), + .refcount_order = cpu_to_be32(s->refcount_order), + .header_length = cpu_to_be32(header_length), + }; + + /* For older versions, write a shorter header */ + switch (s->qcow_version) { + case 2: + ret = offsetof(QCowHeader, incompatible_features); + break; + case 3: + ret = sizeof(*header); + break; + default: + ret = -EINVAL; + goto fail; + } + + buf += ret; + buflen -= ret; + memset(buf, 0, buflen); + + /* Preserve any unknown field in the header */ + if (s->unknown_header_fields_size) { + if (buflen < s->unknown_header_fields_size) { + ret = -ENOSPC; + goto fail; + } + + memcpy(buf, s->unknown_header_fields, s->unknown_header_fields_size); + buf += s->unknown_header_fields_size; + buflen -= s->unknown_header_fields_size; + } + + /* Backing file format header extension */ + if (s->image_backing_format) { + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT, + s->image_backing_format, + strlen(s->image_backing_format), + buflen); + if (ret < 0) { + goto fail; + } + + buf += ret; + buflen -= ret; + } + + /* Feature table */ + Qcow2Feature features[] = { + { + .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit = QCOW2_INCOMPAT_DIRTY_BITNR, + .name = "dirty bit", + }, + { + .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit = QCOW2_INCOMPAT_CORRUPT_BITNR, + .name = "corrupt bit", + }, + { + .type = QCOW2_FEAT_TYPE_COMPATIBLE, + .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, + .name = "lazy refcounts", + }, + }; + + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, + features, sizeof(features), buflen); + if (ret < 0) { + goto fail; + } + buf += ret; + buflen -= ret; + + /* Keep unknown header extensions */ + QLIST_FOREACH(uext, &s->unknown_header_ext, next) { + ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen); + if (ret < 0) { + goto fail; + } + + buf += ret; + buflen -= ret; + } + + /* End of header extensions */ + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_END, NULL, 0, buflen); + if (ret < 0) { + goto fail; + } + + buf += ret; + buflen -= ret; + + /* Backing file name */ + if (s->image_backing_file) { + size_t backing_file_len = strlen(s->image_backing_file); + + if (buflen < backing_file_len) { + ret = -ENOSPC; + goto fail; + } + + /* Using strncpy is ok here, since buf is not NUL-terminated. */ + strncpy(buf, s->image_backing_file, buflen); + + header->backing_file_offset = cpu_to_be64(buf - ((char*) header)); + header->backing_file_size = cpu_to_be32(backing_file_len); + } + + /* Write the new header */ + ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size); + if (ret < 0) { + goto fail; + } + + ret = 0; +fail: + qemu_vfree(header); + return ret; +} + +static int qcow2_change_backing_file(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt) +{ + BDRVQcowState *s = bs->opaque; + + pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); + pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); + + g_free(s->image_backing_file); + g_free(s->image_backing_format); + + s->image_backing_file = backing_file ? g_strdup(bs->backing_file) : NULL; + s->image_backing_format = backing_fmt ? g_strdup(bs->backing_format) : NULL; + + return qcow2_update_header(bs); +} + +static int preallocate(BlockDriverState *bs) +{ + uint64_t nb_sectors; + uint64_t offset; + uint64_t host_offset = 0; + int num; + int ret; + QCowL2Meta *meta; + + nb_sectors = bdrv_nb_sectors(bs); + offset = 0; + + while (nb_sectors) { + num = MIN(nb_sectors, INT_MAX >> BDRV_SECTOR_BITS); + ret = qcow2_alloc_cluster_offset(bs, offset, &num, + &host_offset, &meta); + if (ret < 0) { + return ret; + } + + while (meta) { + QCowL2Meta *next = meta->next; + + ret = qcow2_alloc_cluster_link_l2(bs, meta); + if (ret < 0) { + qcow2_free_any_clusters(bs, meta->alloc_offset, + meta->nb_clusters, QCOW2_DISCARD_NEVER); + return ret; + } + + /* There are no dependent requests, but we need to remove our + * request from the list of in-flight requests */ + QLIST_REMOVE(meta, next_in_flight); + + g_free(meta); + meta = next; + } + + /* TODO Preallocate data if requested */ + + nb_sectors -= num; + offset += num << BDRV_SECTOR_BITS; + } + + /* + * It is expected that the image file is large enough to actually contain + * all of the allocated clusters (otherwise we get failing reads after + * EOF). Extend the image to the last allocated sector. + */ + if (host_offset != 0) { + uint8_t buf[BDRV_SECTOR_SIZE]; + memset(buf, 0, BDRV_SECTOR_SIZE); + ret = bdrv_write(bs->file, (host_offset >> BDRV_SECTOR_BITS) + num - 1, + buf, 1); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static int qcow2_create2(const char *filename, int64_t total_size, + const char *backing_file, const char *backing_format, + int flags, size_t cluster_size, PreallocMode prealloc, + QemuOpts *opts, int version, int refcount_order, + Error **errp) +{ + /* Calculate cluster_bits */ + int cluster_bits; + cluster_bits = ctz32(cluster_size); + if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || + (1 << cluster_bits) != cluster_size) + { + error_setg(errp, "Cluster size must be a power of two between %d and " + "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); + return -EINVAL; + } + + /* + * Open the image file and write a minimal qcow2 header. + * + * We keep things simple and start with a zero-sized image. We also + * do without refcount blocks or a L1 table for now. We'll fix the + * inconsistency later. + * + * We do need a refcount table because growing the refcount table means + * allocating two new refcount blocks - the seconds of which would be at + * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file + * size for any qcow2 image. + */ + BlockDriverState* bs; + QCowHeader *header; + uint64_t* refcount_table; + Error *local_err = NULL; + int ret; + + if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { + /* Note: The following calculation does not need to be exact; if it is a + * bit off, either some bytes will be "leaked" (which is fine) or we + * will need to increase the file size by some bytes (which is fine, + * too, as long as the bulk is allocated here). Therefore, using + * floating point arithmetic is fine. */ + int64_t meta_size = 0; + uint64_t nreftablee, nrefblocke, nl1e, nl2e; + int64_t aligned_total_size = align_offset(total_size, cluster_size); + int refblock_bits, refblock_size; + /* refcount entry size in bytes */ + double rces = (1 << refcount_order) / 8.; + + /* see qcow2_open() */ + refblock_bits = cluster_bits - (refcount_order - 3); + refblock_size = 1 << refblock_bits; + + /* header: 1 cluster */ + meta_size += cluster_size; + + /* total size of L2 tables */ + nl2e = aligned_total_size / cluster_size; + nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t)); + meta_size += nl2e * sizeof(uint64_t); + + /* total size of L1 tables */ + nl1e = nl2e * sizeof(uint64_t) / cluster_size; + nl1e = align_offset(nl1e, cluster_size / sizeof(uint64_t)); + meta_size += nl1e * sizeof(uint64_t); + + /* total size of refcount blocks + * + * note: every host cluster is reference-counted, including metadata + * (even refcount blocks are recursively included). + * Let: + * a = total_size (this is the guest disk size) + * m = meta size not including refcount blocks and refcount tables + * c = cluster size + * y1 = number of refcount blocks entries + * y2 = meta size including everything + * rces = refcount entry size in bytes + * then, + * y1 = (y2 + a)/c + * y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m + * we can get y1: + * y1 = (a + m) / (c - rces - rces * sizeof(u64) / c) + */ + nrefblocke = (aligned_total_size + meta_size + cluster_size) + / (cluster_size - rces - rces * sizeof(uint64_t) + / cluster_size); + meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size; + + /* total size of refcount tables */ + nreftablee = nrefblocke / refblock_size; + nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t)); + meta_size += nreftablee * sizeof(uint64_t); + + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, + aligned_total_size + meta_size, &error_abort); + qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_lookup[prealloc], + &error_abort); + } + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return ret; + } + + bs = NULL; + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return ret; + } + + /* Write the header */ + QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header)); + header = g_malloc0(cluster_size); + *header = (QCowHeader) { + .magic = cpu_to_be32(QCOW_MAGIC), + .version = cpu_to_be32(version), + .cluster_bits = cpu_to_be32(cluster_bits), + .size = cpu_to_be64(0), + .l1_table_offset = cpu_to_be64(0), + .l1_size = cpu_to_be32(0), + .refcount_table_offset = cpu_to_be64(cluster_size), + .refcount_table_clusters = cpu_to_be32(1), + .refcount_order = cpu_to_be32(refcount_order), + .header_length = cpu_to_be32(sizeof(*header)), + }; + + if (flags & BLOCK_FLAG_ENCRYPT) { + header->crypt_method = cpu_to_be32(QCOW_CRYPT_AES); + } else { + header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); + } + + if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) { + header->compatible_features |= + cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); + } + + ret = bdrv_pwrite(bs, 0, header, cluster_size); + g_free(header); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write qcow2 header"); + goto out; + } + + /* Write a refcount table with one refcount block */ + refcount_table = g_malloc0(2 * cluster_size); + refcount_table[0] = cpu_to_be64(2 * cluster_size); + ret = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size); + g_free(refcount_table); + + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write refcount table"); + goto out; + } + + bdrv_unref(bs); + bs = NULL; + + /* + * And now open the image and make it consistent first (i.e. increase the + * refcount of the cluster that is occupied by the header and the refcount + * table) + */ + ret = bdrv_open(&bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, + &bdrv_qcow2, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto out; + } + + ret = qcow2_alloc_clusters(bs, 3 * cluster_size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 " + "header and refcount table"); + goto out; + + } else if (ret != 0) { + error_report("Huh, first cluster in empty image is already in use?"); + abort(); + } + + /* Okay, now that we have a valid image, let's give it the right size */ + ret = bdrv_truncate(bs, total_size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not resize image"); + goto out; + } + + /* Want a backing file? There you go.*/ + if (backing_file) { + ret = bdrv_change_backing_file(bs, backing_file, backing_format); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not assign backing file '%s' " + "with format '%s'", backing_file, backing_format); + goto out; + } + } + + /* And if we're supposed to preallocate metadata, do that now */ + if (prealloc != PREALLOC_MODE_OFF) { + BDRVQcowState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = preallocate(bs); + qemu_co_mutex_unlock(&s->lock); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not preallocate metadata"); + goto out; + } + } + + bdrv_unref(bs); + bs = NULL; + + /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */ + ret = bdrv_open(&bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING, + &bdrv_qcow2, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + ret = 0; +out: + if (bs) { + bdrv_unref(bs); + } + return ret; +} + +static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) +{ + char *backing_file = NULL; + char *backing_fmt = NULL; + char *buf = NULL; + uint64_t size = 0; + int flags = 0; + size_t cluster_size = DEFAULT_CLUSTER_SIZE; + PreallocMode prealloc; + int version = 3; + uint64_t refcount_bits = 16; + int refcount_order; + Error *local_err = NULL; + int ret; + + /* Read out options */ + size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); + backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT); + if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) { + flags |= BLOCK_FLAG_ENCRYPT; + } + cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, + DEFAULT_CLUSTER_SIZE); + buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); + prealloc = qapi_enum_parse(PreallocMode_lookup, buf, + PREALLOC_MODE_MAX, PREALLOC_MODE_OFF, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto finish; + } + g_free(buf); + buf = qemu_opt_get_del(opts, BLOCK_OPT_COMPAT_LEVEL); + if (!buf) { + /* keep the default */ + } else if (!strcmp(buf, "0.10")) { + version = 2; + } else if (!strcmp(buf, "1.1")) { + version = 3; + } else { + error_setg(errp, "Invalid compatibility level: '%s'", buf); + ret = -EINVAL; + goto finish; + } + + if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) { + flags |= BLOCK_FLAG_LAZY_REFCOUNTS; + } + + if (backing_file && prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, "Backing file and preallocation cannot be used at " + "the same time"); + ret = -EINVAL; + goto finish; + } + + if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) { + error_setg(errp, "Lazy refcounts only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)"); + ret = -EINVAL; + goto finish; + } + + refcount_bits = qemu_opt_get_number_del(opts, BLOCK_OPT_REFCOUNT_BITS, + refcount_bits); + if (refcount_bits > 64 || !is_power_of_2(refcount_bits)) { + error_setg(errp, "Refcount width must be a power of two and may not " + "exceed 64 bits"); + ret = -EINVAL; + goto finish; + } + + if (version < 3 && refcount_bits != 16) { + error_setg(errp, "Different refcount widths than 16 bits require " + "compatibility level 1.1 or above (use compat=1.1 or " + "greater)"); + ret = -EINVAL; + goto finish; + } + + refcount_order = ctz32(refcount_bits); + + ret = qcow2_create2(filename, size, backing_file, backing_fmt, flags, + cluster_size, prealloc, opts, version, refcount_order, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + } + +finish: + g_free(backing_file); + g_free(backing_fmt); + g_free(buf); + return ret; +} + +static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) +{ + int ret; + BDRVQcowState *s = bs->opaque; + + /* Emulate misaligned zero writes */ + if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) { + return -ENOTSUP; + } + + /* Whatever is left can use real zero clusters */ + qemu_co_mutex_lock(&s->lock); + ret = qcow2_zero_clusters(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + +static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors) +{ + int ret; + BDRVQcowState *s = bs->opaque; + + qemu_co_mutex_lock(&s->lock); + ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors, QCOW2_DISCARD_REQUEST, false); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int qcow2_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVQcowState *s = bs->opaque; + int64_t new_l1_size; + int ret; + + if (offset & 511) { + error_report("The new size must be a multiple of 512"); + return -EINVAL; + } + + /* cannot proceed if image has snapshots */ + if (s->nb_snapshots) { + error_report("Can't resize an image which has snapshots"); + return -ENOTSUP; + } + + /* shrinking is currently not supported */ + if (offset < bs->total_sectors * 512) { + error_report("qcow2 doesn't support shrinking images yet"); + return -ENOTSUP; + } + + new_l1_size = size_to_l1(s, offset); + ret = qcow2_grow_l1_table(bs, new_l1_size, true); + if (ret < 0) { + return ret; + } + + /* write updated header.size */ + offset = cpu_to_be64(offset); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), + &offset, sizeof(uint64_t)); + if (ret < 0) { + return ret; + } + + s->l1_vm_state_index = new_l1_size; + return 0; +} + +/* XXX: put compressed sectors first, then all the cluster aligned + tables to avoid losing bytes in alignment */ +static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + z_stream strm; + int ret, out_len; + uint8_t *out_buf; + uint64_t cluster_offset; + + if (nb_sectors == 0) { + /* align end of file to a sector boundary to ease reading with + sector based I/Os */ + cluster_offset = bdrv_getlength(bs->file); + return bdrv_truncate(bs->file, cluster_offset); + } + + if (nb_sectors != s->cluster_sectors) { + ret = -EINVAL; + + /* Zero-pad last write if image size is not cluster aligned */ + if (sector_num + nb_sectors == bs->total_sectors && + nb_sectors < s->cluster_sectors) { + uint8_t *pad_buf = qemu_blockalign(bs, s->cluster_size); + memset(pad_buf, 0, s->cluster_size); + memcpy(pad_buf, buf, nb_sectors * BDRV_SECTOR_SIZE); + ret = qcow2_write_compressed(bs, sector_num, + pad_buf, s->cluster_sectors); + qemu_vfree(pad_buf); + } + return ret; + } + + out_buf = g_malloc(s->cluster_size + (s->cluster_size / 1000) + 128); + + /* best compression, small window, no zlib header */ + memset(&strm, 0, sizeof(strm)); + ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, + Z_DEFLATED, -12, + 9, Z_DEFAULT_STRATEGY); + if (ret != 0) { + ret = -EINVAL; + goto fail; + } + + strm.avail_in = s->cluster_size; + strm.next_in = (uint8_t *)buf; + strm.avail_out = s->cluster_size; + strm.next_out = out_buf; + + ret = deflate(&strm, Z_FINISH); + if (ret != Z_STREAM_END && ret != Z_OK) { + deflateEnd(&strm); + ret = -EINVAL; + goto fail; + } + out_len = strm.next_out - out_buf; + + deflateEnd(&strm); + + if (ret != Z_STREAM_END || out_len >= s->cluster_size) { + /* could not compress: write normal cluster */ + ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors); + if (ret < 0) { + goto fail; + } + } else { + cluster_offset = qcow2_alloc_compressed_cluster_offset(bs, + sector_num << 9, out_len); + if (!cluster_offset) { + ret = -EIO; + goto fail; + } + cluster_offset &= s->cluster_offset_mask; + + ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len); + if (ret < 0) { + goto fail; + } + + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); + ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len); + if (ret < 0) { + goto fail; + } + } + + ret = 0; +fail: + g_free(out_buf); + return ret; +} + +static int make_completely_empty(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int ret, l1_clusters; + int64_t offset; + uint64_t *new_reftable = NULL; + uint64_t rt_entry, l1_size2; + struct { + uint64_t l1_offset; + uint64_t reftable_offset; + uint32_t reftable_clusters; + } QEMU_PACKED l1_ofs_rt_ofs_cls; + + ret = qcow2_cache_empty(bs, s->l2_table_cache); + if (ret < 0) { + goto fail; + } + + ret = qcow2_cache_empty(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail; + } + + /* Refcounts will be broken utterly */ + ret = qcow2_mark_dirty(bs); + if (ret < 0) { + goto fail; + } + + BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); + + l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); + l1_size2 = (uint64_t)s->l1_size * sizeof(uint64_t); + + /* After this call, neither the in-memory nor the on-disk refcount + * information accurately describe the actual references */ + + ret = bdrv_write_zeroes(bs->file, s->l1_table_offset / BDRV_SECTOR_SIZE, + l1_clusters * s->cluster_sectors, 0); + if (ret < 0) { + goto fail_broken_refcounts; + } + memset(s->l1_table, 0, l1_size2); + + BLKDBG_EVENT(bs->file, BLKDBG_EMPTY_IMAGE_PREPARE); + + /* Overwrite enough clusters at the beginning of the sectors to place + * the refcount table, a refcount block and the L1 table in; this may + * overwrite parts of the existing refcount and L1 table, which is not + * an issue because the dirty flag is set, complete data loss is in fact + * desired and partial data loss is consequently fine as well */ + ret = bdrv_write_zeroes(bs->file, s->cluster_size / BDRV_SECTOR_SIZE, + (2 + l1_clusters) * s->cluster_size / + BDRV_SECTOR_SIZE, 0); + /* This call (even if it failed overall) may have overwritten on-disk + * refcount structures; in that case, the in-memory refcount information + * will probably differ from the on-disk information which makes the BDS + * unusable */ + if (ret < 0) { + goto fail_broken_refcounts; + } + + BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); + BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_UPDATE); + + /* "Create" an empty reftable (one cluster) directly after the image + * header and an empty L1 table three clusters after the image header; + * the cluster between those two will be used as the first refblock */ + cpu_to_be64w(&l1_ofs_rt_ofs_cls.l1_offset, 3 * s->cluster_size); + cpu_to_be64w(&l1_ofs_rt_ofs_cls.reftable_offset, s->cluster_size); + cpu_to_be32w(&l1_ofs_rt_ofs_cls.reftable_clusters, 1); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset), + &l1_ofs_rt_ofs_cls, sizeof(l1_ofs_rt_ofs_cls)); + if (ret < 0) { + goto fail_broken_refcounts; + } + + s->l1_table_offset = 3 * s->cluster_size; + + new_reftable = g_try_new0(uint64_t, s->cluster_size / sizeof(uint64_t)); + if (!new_reftable) { + ret = -ENOMEM; + goto fail_broken_refcounts; + } + + s->refcount_table_offset = s->cluster_size; + s->refcount_table_size = s->cluster_size / sizeof(uint64_t); + + g_free(s->refcount_table); + s->refcount_table = new_reftable; + new_reftable = NULL; + + /* Now the in-memory refcount information again corresponds to the on-disk + * information (reftable is empty and no refblocks (the refblock cache is + * empty)); however, this means some clusters (e.g. the image header) are + * referenced, but not refcounted, but the normal qcow2 code assumes that + * the in-memory information is always correct */ + + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC); + + /* Enter the first refblock into the reftable */ + rt_entry = cpu_to_be64(2 * s->cluster_size); + ret = bdrv_pwrite_sync(bs->file, s->cluster_size, + &rt_entry, sizeof(rt_entry)); + if (ret < 0) { + goto fail_broken_refcounts; + } + s->refcount_table[0] = 2 * s->cluster_size; + + s->free_cluster_index = 0; + assert(3 + l1_clusters <= s->refcount_block_size); + offset = qcow2_alloc_clusters(bs, 3 * s->cluster_size + l1_size2); + if (offset < 0) { + ret = offset; + goto fail_broken_refcounts; + } else if (offset > 0) { + error_report("First cluster in emptied image is in use"); + abort(); + } + + /* Now finally the in-memory information corresponds to the on-disk + * structures and is correct */ + ret = qcow2_mark_clean(bs); + if (ret < 0) { + goto fail; + } + + ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size); + if (ret < 0) { + goto fail; + } + + return 0; + +fail_broken_refcounts: + /* The BDS is unusable at this point. If we wanted to make it usable, we + * would have to call qcow2_refcount_close(), qcow2_refcount_init(), + * qcow2_check_refcounts(), qcow2_refcount_close() and qcow2_refcount_init() + * again. However, because the functions which could have caused this error + * path to be taken are used by those functions as well, it's very likely + * that that sequence will fail as well. Therefore, just eject the BDS. */ + bs->drv = NULL; + +fail: + g_free(new_reftable); + return ret; +} + +static int qcow2_make_empty(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint64_t start_sector; + int sector_step = INT_MAX / BDRV_SECTOR_SIZE; + int l1_clusters, ret = 0; + + l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); + + if (s->qcow_version >= 3 && !s->snapshots && + 3 + l1_clusters <= s->refcount_block_size) { + /* The following function only works for qcow2 v3 images (it requires + * the dirty flag) and only as long as there are no snapshots (because + * it completely empties the image). Furthermore, the L1 table and three + * additional clusters (image header, refcount table, one refcount + * block) have to fit inside one refcount block. */ + return make_completely_empty(bs); + } + + /* This fallback code simply discards every active cluster; this is slow, + * but works in all cases */ + for (start_sector = 0; start_sector < bs->total_sectors; + start_sector += sector_step) + { + /* As this function is generally used after committing an external + * snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the + * default action for this kind of discard is to pass the discard, + * which will ideally result in an actually smaller image file, as + * is probably desired. */ + ret = qcow2_discard_clusters(bs, start_sector * BDRV_SECTOR_SIZE, + MIN(sector_step, + bs->total_sectors - start_sector), + QCOW2_DISCARD_SNAPSHOT, true); + if (ret < 0) { + break; + } + } + + return ret; +} + +static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int ret; + + qemu_co_mutex_lock(&s->lock); + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret < 0) { + qemu_co_mutex_unlock(&s->lock); + return ret; + } + + if (qcow2_need_accurate_refcounts(s)) { + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + qemu_co_mutex_unlock(&s->lock); + return ret; + } + } + qemu_co_mutex_unlock(&s->lock); + + return 0; +} + +static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVQcowState *s = bs->opaque; + bdi->unallocated_blocks_are_zero = true; + bdi->can_write_zeroes_with_unmap = (s->qcow_version >= 3); + bdi->cluster_size = s->cluster_size; + bdi->vm_state_offset = qcow2_vm_state_offset(s); + return 0; +} + +static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1); + + *spec_info = (ImageInfoSpecific){ + .kind = IMAGE_INFO_SPECIFIC_KIND_QCOW2, + { + .qcow2 = g_new(ImageInfoSpecificQCow2, 1), + }, + }; + if (s->qcow_version == 2) { + *spec_info->qcow2 = (ImageInfoSpecificQCow2){ + .compat = g_strdup("0.10"), + .refcount_bits = s->refcount_bits, + }; + } else if (s->qcow_version == 3) { + *spec_info->qcow2 = (ImageInfoSpecificQCow2){ + .compat = g_strdup("1.1"), + .lazy_refcounts = s->compatible_features & + QCOW2_COMPAT_LAZY_REFCOUNTS, + .has_lazy_refcounts = true, + .corrupt = s->incompatible_features & + QCOW2_INCOMPAT_CORRUPT, + .has_corrupt = true, + .refcount_bits = s->refcount_bits, + }; + } + + return spec_info; +} + +#if 0 +static void dump_refcounts(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int64_t nb_clusters, k, k1, size; + int refcount; + + size = bdrv_getlength(bs->file); + nb_clusters = size_to_clusters(s, size); + for(k = 0; k < nb_clusters;) { + k1 = k; + refcount = get_refcount(bs, k); + k++; + while (k < nb_clusters && get_refcount(bs, k) == refcount) + k++; + printf("%" PRId64 ": refcount=%d nb=%" PRId64 "\n", k, refcount, + k - k1); + } +} +#endif + +static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t pos) +{ + BDRVQcowState *s = bs->opaque; + int64_t total_sectors = bs->total_sectors; + bool zero_beyond_eof = bs->zero_beyond_eof; + int ret; + + BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); + bs->zero_beyond_eof = false; + ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov); + bs->zero_beyond_eof = zero_beyond_eof; + + /* bdrv_co_do_writev will have increased the total_sectors value to include + * the VM state - the VM state is however not an actual part of the block + * device, therefore, we need to restore the old value. */ + bs->total_sectors = total_sectors; + + return ret; +} + +static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, + int64_t pos, int size) +{ + BDRVQcowState *s = bs->opaque; + bool zero_beyond_eof = bs->zero_beyond_eof; + int ret; + + BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); + bs->zero_beyond_eof = false; + ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size); + bs->zero_beyond_eof = zero_beyond_eof; + + return ret; +} + +/* + * Downgrades an image's version. To achieve this, any incompatible features + * have to be removed. + */ +static int qcow2_downgrade(BlockDriverState *bs, int target_version, + BlockDriverAmendStatusCB *status_cb) +{ + BDRVQcowState *s = bs->opaque; + int current_version = s->qcow_version; + int ret; + + if (target_version == current_version) { + return 0; + } else if (target_version > current_version) { + return -EINVAL; + } else if (target_version != 2) { + return -EINVAL; + } + + if (s->refcount_order != 4) { + /* we would have to convert the image to a refcount_order == 4 image + * here; however, since qemu (at the time of writing this) does not + * support anything different than 4 anyway, there is no point in doing + * so right now; however, we should error out (if qemu supports this in + * the future and this code has not been adapted) */ + error_report("qcow2_downgrade: Image refcount orders other than 4 are " + "currently not supported."); + return -ENOTSUP; + } + + /* clear incompatible features */ + if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { + ret = qcow2_mark_clean(bs); + if (ret < 0) { + return ret; + } + } + + /* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in + * the first place; if that happens nonetheless, returning -ENOTSUP is the + * best thing to do anyway */ + + if (s->incompatible_features) { + return -ENOTSUP; + } + + /* since we can ignore compatible features, we can set them to 0 as well */ + s->compatible_features = 0; + /* if lazy refcounts have been used, they have already been fixed through + * clearing the dirty flag */ + + /* clearing autoclear features is trivial */ + s->autoclear_features = 0; + + ret = qcow2_expand_zero_clusters(bs, status_cb); + if (ret < 0) { + return ret; + } + + s->qcow_version = target_version; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->qcow_version = current_version; + return ret; + } + return 0; +} + +static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb) +{ + BDRVQcowState *s = bs->opaque; + int old_version = s->qcow_version, new_version = old_version; + uint64_t new_size = 0; + const char *backing_file = NULL, *backing_format = NULL; + bool lazy_refcounts = s->use_lazy_refcounts; + const char *compat = NULL; + uint64_t cluster_size = s->cluster_size; + bool encrypt; + int ret; + QemuOptDesc *desc = opts->list->desc; + + while (desc && desc->name) { + if (!qemu_opt_find(opts, desc->name)) { + /* only change explicitly defined options */ + desc++; + continue; + } + + if (!strcmp(desc->name, BLOCK_OPT_COMPAT_LEVEL)) { + compat = qemu_opt_get(opts, BLOCK_OPT_COMPAT_LEVEL); + if (!compat) { + /* preserve default */ + } else if (!strcmp(compat, "0.10")) { + new_version = 2; + } else if (!strcmp(compat, "1.1")) { + new_version = 3; + } else { + fprintf(stderr, "Unknown compatibility level %s.\n", compat); + return -EINVAL; + } + } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { + fprintf(stderr, "Cannot change preallocation mode.\n"); + return -ENOTSUP; + } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { + new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) { + backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { + backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); + } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) { + encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, + s->crypt_method); + if (encrypt != !!s->crypt_method) { + fprintf(stderr, "Changing the encryption flag is not " + "supported.\n"); + return -ENOTSUP; + } + } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { + cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, + cluster_size); + if (cluster_size != s->cluster_size) { + fprintf(stderr, "Changing the cluster size is not " + "supported.\n"); + return -ENOTSUP; + } + } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { + lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, + lazy_refcounts); + } else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) { + error_report("Cannot change refcount entry width"); + return -ENOTSUP; + } else { + /* if this assertion fails, this probably means a new option was + * added without having it covered here */ + assert(false); + } + + desc++; + } + + if (new_version != old_version) { + if (new_version > old_version) { + /* Upgrade */ + s->qcow_version = new_version; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->qcow_version = old_version; + return ret; + } + } else { + ret = qcow2_downgrade(bs, new_version, status_cb); + if (ret < 0) { + return ret; + } + } + } + + if (backing_file || backing_format) { + ret = qcow2_change_backing_file(bs, + backing_file ?: s->image_backing_file, + backing_format ?: s->image_backing_format); + if (ret < 0) { + return ret; + } + } + + if (s->use_lazy_refcounts != lazy_refcounts) { + if (lazy_refcounts) { + if (s->qcow_version < 3) { + fprintf(stderr, "Lazy refcounts only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)\n"); + return -EINVAL; + } + s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; + return ret; + } + s->use_lazy_refcounts = true; + } else { + /* make image clean first */ + ret = qcow2_mark_clean(bs); + if (ret < 0) { + return ret; + } + /* now disallow lazy refcounts */ + s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; + return ret; + } + s->use_lazy_refcounts = false; + } + } + + if (new_size) { + ret = bdrv_truncate(bs, new_size); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +/* + * If offset or size are negative, respectively, they will not be included in + * the BLOCK_IMAGE_CORRUPTED event emitted. + * fatal will be ignored for read-only BDS; corruptions found there will always + * be considered non-fatal. + */ +void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + int64_t size, const char *message_format, ...) +{ + BDRVQcowState *s = bs->opaque; + const char *node_name; + char *message; + va_list ap; + + fatal = fatal && !bs->read_only; + + if (s->signaled_corruption && + (!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT))) + { + return; + } + + va_start(ap, message_format); + message = g_strdup_vprintf(message_format, ap); + va_end(ap); + + if (fatal) { + fprintf(stderr, "qcow2: Marking image as corrupt: %s; further " + "corruption events will be suppressed\n", message); + } else { + fprintf(stderr, "qcow2: Image is corrupt: %s; further non-fatal " + "corruption events will be suppressed\n", message); + } + + node_name = bdrv_get_node_name(bs); + qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs), + *node_name != '\0', node_name, + message, offset >= 0, offset, + size >= 0, size, + fatal, &error_abort); + g_free(message); + + if (fatal) { + qcow2_mark_corrupt(bs); + bs->drv = NULL; /* make BDS unusable */ + } + + s->signaled_corruption = true; +} + +static QemuOptsList qcow2_create_opts = { + .name = "qcow2-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_COMPAT_LEVEL, + .type = QEMU_OPT_STRING, + .help = "Compatibility level (0.10 or 1.1)" + }, + { + .name = BLOCK_OPT_BACKING_FILE, + .type = QEMU_OPT_STRING, + .help = "File name of a base image" + }, + { + .name = BLOCK_OPT_BACKING_FMT, + .type = QEMU_OPT_STRING, + .help = "Image format of the base image" + }, + { + .name = BLOCK_OPT_ENCRYPT, + .type = QEMU_OPT_BOOL, + .help = "Encrypt the image", + .def_value_str = "off" + }, + { + .name = BLOCK_OPT_CLUSTER_SIZE, + .type = QEMU_OPT_SIZE, + .help = "qcow2 cluster size", + .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) + }, + { + .name = BLOCK_OPT_PREALLOC, + .type = QEMU_OPT_STRING, + .help = "Preallocation mode (allowed values: off, metadata, " + "falloc, full)" + }, + { + .name = BLOCK_OPT_LAZY_REFCOUNTS, + .type = QEMU_OPT_BOOL, + .help = "Postpone refcount updates", + .def_value_str = "off" + }, + { + .name = BLOCK_OPT_REFCOUNT_BITS, + .type = QEMU_OPT_NUMBER, + .help = "Width of a reference count entry in bits", + .def_value_str = "16" + }, + { /* end of list */ } + } +}; + +BlockDriver bdrv_qcow2 = { + .format_name = "qcow2", + .instance_size = sizeof(BDRVQcowState), + .bdrv_probe = qcow2_probe, + .bdrv_open = qcow2_open, + .bdrv_close = qcow2_close, + .bdrv_reopen_prepare = qcow2_reopen_prepare, + .bdrv_create = qcow2_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_co_get_block_status = qcow2_co_get_block_status, + .bdrv_set_key = qcow2_set_key, + + .bdrv_co_readv = qcow2_co_readv, + .bdrv_co_writev = qcow2_co_writev, + .bdrv_co_flush_to_os = qcow2_co_flush_to_os, + + .bdrv_co_write_zeroes = qcow2_co_write_zeroes, + .bdrv_co_discard = qcow2_co_discard, + .bdrv_truncate = qcow2_truncate, + .bdrv_write_compressed = qcow2_write_compressed, + .bdrv_make_empty = qcow2_make_empty, + + .bdrv_snapshot_create = qcow2_snapshot_create, + .bdrv_snapshot_goto = qcow2_snapshot_goto, + .bdrv_snapshot_delete = qcow2_snapshot_delete, + .bdrv_snapshot_list = qcow2_snapshot_list, + .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, + .bdrv_get_info = qcow2_get_info, + .bdrv_get_specific_info = qcow2_get_specific_info, + + .bdrv_save_vmstate = qcow2_save_vmstate, + .bdrv_load_vmstate = qcow2_load_vmstate, + + .supports_backing = true, + .bdrv_change_backing_file = qcow2_change_backing_file, + + .bdrv_refresh_limits = qcow2_refresh_limits, + .bdrv_invalidate_cache = qcow2_invalidate_cache, + + .create_opts = &qcow2_create_opts, + .bdrv_check = qcow2_check, + .bdrv_amend_options = qcow2_amend_options, +}; + +static void bdrv_qcow2_init(void) +{ + bdrv_register(&bdrv_qcow2); +} + +block_init(bdrv_qcow2_init); diff --git a/block/qcow2.h b/block/qcow2.h new file mode 100644 index 0000000..0076512 --- /dev/null +++ b/block/qcow2.h @@ -0,0 +1,592 @@ +/* + * Block driver for the QCOW version 2 format + * + * Copyright (c) 2004-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BLOCK_QCOW2_H +#define BLOCK_QCOW2_H + +#include "qemu/aes.h" +#include "block/coroutine.h" + +//#define DEBUG_ALLOC +//#define DEBUG_ALLOC2 +//#define DEBUG_EXT + +#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) + +#define QCOW_CRYPT_NONE 0 +#define QCOW_CRYPT_AES 1 + +#define QCOW_MAX_CRYPT_CLUSTERS 32 +#define QCOW_MAX_SNAPSHOTS 65536 + +/* 8 MB refcount table is enough for 2 PB images at 64k cluster size + * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ +#define QCOW_MAX_REFTABLE_SIZE 0x800000 + +/* 32 MB L1 table is enough for 2 PB images at 64k cluster size + * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ +#define QCOW_MAX_L1_SIZE 0x2000000 + +/* Allow for an average of 1k per snapshot table entry, should be plenty of + * space for snapshot names and IDs */ +#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS) + +/* indicate that the refcount of the referenced cluster is exactly one. */ +#define QCOW_OFLAG_COPIED (1ULL << 63) +/* indicate that the cluster is compressed (they never have the copied flag) */ +#define QCOW_OFLAG_COMPRESSED (1ULL << 62) +/* The cluster reads as all zeros */ +#define QCOW_OFLAG_ZERO (1ULL << 0) + +#define MIN_CLUSTER_BITS 9 +#define MAX_CLUSTER_BITS 21 + +#define MIN_L2_CACHE_SIZE 1 /* cluster */ + +/* Must be at least 4 to cover all cases of refcount table growth */ +#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ + +#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ + +/* The refblock cache needs only a fourth of the L2 cache size to cover as many + * clusters */ +#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4 + +#define DEFAULT_CLUSTER_SIZE 65536 + + +#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" +#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" +#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" +#define QCOW2_OPT_DISCARD_OTHER "pass-discard-other" +#define QCOW2_OPT_OVERLAP "overlap-check" +#define QCOW2_OPT_OVERLAP_TEMPLATE "overlap-check.template" +#define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header" +#define QCOW2_OPT_OVERLAP_ACTIVE_L1 "overlap-check.active-l1" +#define QCOW2_OPT_OVERLAP_ACTIVE_L2 "overlap-check.active-l2" +#define QCOW2_OPT_OVERLAP_REFCOUNT_TABLE "overlap-check.refcount-table" +#define QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK "overlap-check.refcount-block" +#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" +#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" +#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" +#define QCOW2_OPT_CACHE_SIZE "cache-size" +#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" +#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size" + +typedef struct QCowHeader { + uint32_t magic; + uint32_t version; + uint64_t backing_file_offset; + uint32_t backing_file_size; + uint32_t cluster_bits; + uint64_t size; /* in bytes */ + uint32_t crypt_method; + uint32_t l1_size; /* XXX: save number of clusters instead ? */ + uint64_t l1_table_offset; + uint64_t refcount_table_offset; + uint32_t refcount_table_clusters; + uint32_t nb_snapshots; + uint64_t snapshots_offset; + + /* The following fields are only valid for version >= 3 */ + uint64_t incompatible_features; + uint64_t compatible_features; + uint64_t autoclear_features; + + uint32_t refcount_order; + uint32_t header_length; +} QEMU_PACKED QCowHeader; + +typedef struct QEMU_PACKED QCowSnapshotHeader { + /* header is 8 byte aligned */ + uint64_t l1_table_offset; + + uint32_t l1_size; + uint16_t id_str_size; + uint16_t name_size; + + uint32_t date_sec; + uint32_t date_nsec; + + uint64_t vm_clock_nsec; + + uint32_t vm_state_size; + uint32_t extra_data_size; /* for extension */ + /* extra data follows */ + /* id_str follows */ + /* name follows */ +} QCowSnapshotHeader; + +typedef struct QEMU_PACKED QCowSnapshotExtraData { + uint64_t vm_state_size_large; + uint64_t disk_size; +} QCowSnapshotExtraData; + + +typedef struct QCowSnapshot { + uint64_t l1_table_offset; + uint32_t l1_size; + char *id_str; + char *name; + uint64_t disk_size; + uint64_t vm_state_size; + uint32_t date_sec; + uint32_t date_nsec; + uint64_t vm_clock_nsec; +} QCowSnapshot; + +struct Qcow2Cache; +typedef struct Qcow2Cache Qcow2Cache; + +typedef struct Qcow2UnknownHeaderExtension { + uint32_t magic; + uint32_t len; + QLIST_ENTRY(Qcow2UnknownHeaderExtension) next; + uint8_t data[]; +} Qcow2UnknownHeaderExtension; + +enum { + QCOW2_FEAT_TYPE_INCOMPATIBLE = 0, + QCOW2_FEAT_TYPE_COMPATIBLE = 1, + QCOW2_FEAT_TYPE_AUTOCLEAR = 2, +}; + +/* Incompatible feature bits */ +enum { + QCOW2_INCOMPAT_DIRTY_BITNR = 0, + QCOW2_INCOMPAT_CORRUPT_BITNR = 1, + QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, + QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, + + QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY + | QCOW2_INCOMPAT_CORRUPT, +}; + +/* Compatible feature bits */ +enum { + QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR = 0, + QCOW2_COMPAT_LAZY_REFCOUNTS = 1 << QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, + + QCOW2_COMPAT_FEAT_MASK = QCOW2_COMPAT_LAZY_REFCOUNTS, +}; + +enum qcow2_discard_type { + QCOW2_DISCARD_NEVER = 0, + QCOW2_DISCARD_ALWAYS, + QCOW2_DISCARD_REQUEST, + QCOW2_DISCARD_SNAPSHOT, + QCOW2_DISCARD_OTHER, + QCOW2_DISCARD_MAX +}; + +typedef struct Qcow2Feature { + uint8_t type; + uint8_t bit; + char name[46]; +} QEMU_PACKED Qcow2Feature; + +typedef struct Qcow2DiscardRegion { + BlockDriverState *bs; + uint64_t offset; + uint64_t bytes; + QTAILQ_ENTRY(Qcow2DiscardRegion) next; +} Qcow2DiscardRegion; + +typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, + uint64_t index); +typedef void Qcow2SetRefcountFunc(void *refcount_array, + uint64_t index, uint64_t value); + +typedef struct BDRVQcowState { + int cluster_bits; + int cluster_size; + int cluster_sectors; + int l2_bits; + int l2_size; + int l1_size; + int l1_vm_state_index; + int refcount_block_bits; + int refcount_block_size; + int csize_shift; + int csize_mask; + uint64_t cluster_offset_mask; + uint64_t l1_table_offset; + uint64_t *l1_table; + + Qcow2Cache* l2_table_cache; + Qcow2Cache* refcount_block_cache; + + uint8_t *cluster_cache; + uint8_t *cluster_data; + uint64_t cluster_cache_offset; + QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs; + + uint64_t *refcount_table; + uint64_t refcount_table_offset; + uint32_t refcount_table_size; + uint64_t free_cluster_index; + uint64_t free_byte_offset; + + CoMutex lock; + + uint32_t crypt_method; /* current crypt method, 0 if no key yet */ + uint32_t crypt_method_header; + AES_KEY aes_encrypt_key; + AES_KEY aes_decrypt_key; + uint64_t snapshots_offset; + int snapshots_size; + unsigned int nb_snapshots; + QCowSnapshot *snapshots; + + int flags; + int qcow_version; + bool use_lazy_refcounts; + int refcount_order; + int refcount_bits; + uint64_t refcount_max; + + Qcow2GetRefcountFunc *get_refcount; + Qcow2SetRefcountFunc *set_refcount; + + bool discard_passthrough[QCOW2_DISCARD_MAX]; + + int overlap_check; /* bitmask of Qcow2MetadataOverlap values */ + bool signaled_corruption; + + uint64_t incompatible_features; + uint64_t compatible_features; + uint64_t autoclear_features; + + size_t unknown_header_fields_size; + void* unknown_header_fields; + QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; + QTAILQ_HEAD (, Qcow2DiscardRegion) discards; + bool cache_discards; + + /* Backing file path and format as stored in the image (this is not the + * effective path/format, which may be the result of a runtime option + * override) */ + char *image_backing_file; + char *image_backing_format; +} BDRVQcowState; + +struct QCowAIOCB; + +typedef struct Qcow2COWRegion { + /** + * Offset of the COW region in bytes from the start of the first cluster + * touched by the request. + */ + uint64_t offset; + + /** Number of sectors to copy */ + int nb_sectors; +} Qcow2COWRegion; + +/** + * Describes an in-flight (part of a) write request that writes to clusters + * that are not referenced in their L2 table yet. + */ +typedef struct QCowL2Meta +{ + /** Guest offset of the first newly allocated cluster */ + uint64_t offset; + + /** Host offset of the first newly allocated cluster */ + uint64_t alloc_offset; + + /** + * Number of sectors from the start of the first allocated cluster to + * the end of the (possibly shortened) request + */ + int nb_available; + + /** Number of newly allocated clusters */ + int nb_clusters; + + /** + * Requests that overlap with this allocation and wait to be restarted + * when the allocating request has completed. + */ + CoQueue dependent_requests; + + /** + * The COW Region between the start of the first allocated cluster and the + * area the guest actually writes to. + */ + Qcow2COWRegion cow_start; + + /** + * The COW Region between the area the guest actually writes to and the + * end of the last allocated cluster. + */ + Qcow2COWRegion cow_end; + + /** Pointer to next L2Meta of the same write request */ + struct QCowL2Meta *next; + + QLIST_ENTRY(QCowL2Meta) next_in_flight; +} QCowL2Meta; + +enum { + QCOW2_CLUSTER_UNALLOCATED, + QCOW2_CLUSTER_NORMAL, + QCOW2_CLUSTER_COMPRESSED, + QCOW2_CLUSTER_ZERO +}; + +typedef enum QCow2MetadataOverlap { + QCOW2_OL_MAIN_HEADER_BITNR = 0, + QCOW2_OL_ACTIVE_L1_BITNR = 1, + QCOW2_OL_ACTIVE_L2_BITNR = 2, + QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, + QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, + QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, + QCOW2_OL_INACTIVE_L1_BITNR = 6, + QCOW2_OL_INACTIVE_L2_BITNR = 7, + + QCOW2_OL_MAX_BITNR = 8, + + QCOW2_OL_NONE = 0, + QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR), + QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR), + QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR), + QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), + QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), + QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), + QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR), + /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv + * reads. */ + QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), +} QCow2MetadataOverlap; + +/* Perform all overlap checks which can be done in constant time */ +#define QCOW2_OL_CONSTANT \ + (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \ + QCOW2_OL_SNAPSHOT_TABLE) + +/* Perform all overlap checks which don't require disk access */ +#define QCOW2_OL_CACHED \ + (QCOW2_OL_CONSTANT | QCOW2_OL_ACTIVE_L2 | QCOW2_OL_REFCOUNT_BLOCK | \ + QCOW2_OL_INACTIVE_L1) + +/* Perform all overlap checks */ +#define QCOW2_OL_ALL \ + (QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2) + +#define L1E_OFFSET_MASK 0x00fffffffffffe00ULL +#define L2E_OFFSET_MASK 0x00fffffffffffe00ULL +#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL + +#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL + +static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset) +{ + return offset & ~(s->cluster_size - 1); +} + +static inline int64_t offset_into_cluster(BDRVQcowState *s, int64_t offset) +{ + return offset & (s->cluster_size - 1); +} + +static inline int size_to_clusters(BDRVQcowState *s, int64_t size) +{ + return (size + (s->cluster_size - 1)) >> s->cluster_bits; +} + +static inline int64_t size_to_l1(BDRVQcowState *s, int64_t size) +{ + int shift = s->cluster_bits + s->l2_bits; + return (size + (1ULL << shift) - 1) >> shift; +} + +static inline int offset_to_l2_index(BDRVQcowState *s, int64_t offset) +{ + return (offset >> s->cluster_bits) & (s->l2_size - 1); +} + +static inline int64_t align_offset(int64_t offset, int n) +{ + offset = (offset + n - 1) & ~(n - 1); + return offset; +} + +static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s) +{ + return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); +} + +static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s) +{ + return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits; +} + +static inline int qcow2_get_cluster_type(uint64_t l2_entry) +{ + if (l2_entry & QCOW_OFLAG_COMPRESSED) { + return QCOW2_CLUSTER_COMPRESSED; + } else if (l2_entry & QCOW_OFLAG_ZERO) { + return QCOW2_CLUSTER_ZERO; + } else if (!(l2_entry & L2E_OFFSET_MASK)) { + return QCOW2_CLUSTER_UNALLOCATED; + } else { + return QCOW2_CLUSTER_NORMAL; + } +} + +/* Check whether refcounts are eager or lazy */ +static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s) +{ + return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY); +} + +static inline uint64_t l2meta_cow_start(QCowL2Meta *m) +{ + return m->offset + m->cow_start.offset; +} + +static inline uint64_t l2meta_cow_end(QCowL2Meta *m) +{ + return m->offset + m->cow_end.offset + + (m->cow_end.nb_sectors << BDRV_SECTOR_BITS); +} + +static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2) +{ + return r1 > r2 ? r1 - r2 : r2 - r1; +} + +// FIXME Need qcow2_ prefix to global functions + +/* qcow2.c functions */ +int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t sector_num, int nb_sectors); + +int qcow2_mark_dirty(BlockDriverState *bs); +int qcow2_mark_corrupt(BlockDriverState *bs); +int qcow2_mark_consistent(BlockDriverState *bs); +int qcow2_update_header(BlockDriverState *bs); + +void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + int64_t size, const char *message_format, ...) + GCC_FMT_ATTR(5, 6); + +/* qcow2-refcount.c functions */ +int qcow2_refcount_init(BlockDriverState *bs); +void qcow2_refcount_close(BlockDriverState *bs); + +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount); + +int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t addend, bool decrease, + enum qcow2_discard_type type); + +int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); +int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, + int nb_clusters); +int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size); +void qcow2_free_clusters(BlockDriverState *bs, + int64_t offset, int64_t size, + enum qcow2_discard_type type); +void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, + int nb_clusters, enum qcow2_discard_type type); + +int qcow2_update_snapshot_refcount(BlockDriverState *bs, + int64_t l1_table_offset, int l1_size, int addend); + +int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix); + +void qcow2_process_discards(BlockDriverState *bs, int ret); + +int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, + int64_t size); +int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, + int64_t size); + +/* qcow2-cluster.c functions */ +int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, + bool exact_size); +int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); +void qcow2_l2_cache_reset(BlockDriverState *bs); +int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); +void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, + uint8_t *out_buf, const uint8_t *in_buf, + int nb_sectors, int enc, + const AES_KEY *key); + +int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, + int *num, uint64_t *cluster_offset); +int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, + int *num, uint64_t *host_offset, QCowL2Meta **m); +uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, + uint64_t offset, + int compressed_size); + +int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); +int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, + int nb_sectors, enum qcow2_discard_type type, bool full_discard); +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); + +int qcow2_expand_zero_clusters(BlockDriverState *bs, + BlockDriverAmendStatusCB *status_cb); + +/* qcow2-snapshot.c functions */ +int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); +int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); +int qcow2_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp); +int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); +int qcow2_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp); + +void qcow2_free_snapshots(BlockDriverState *bs); +int qcow2_read_snapshots(BlockDriverState *bs); + +/* qcow2-cache.c functions */ +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); +int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); + +void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, + void *table); +int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); +int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, + Qcow2Cache *dependency); +void qcow2_cache_depends_on_flush(Qcow2Cache *c); + +int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); + +int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table); +int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table); +void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); + +#endif diff --git a/block/qed-check.c b/block/qed-check.c new file mode 100644 index 0000000..36ecd29 --- /dev/null +++ b/block/qed-check.c @@ -0,0 +1,250 @@ +/* + * QEMU Enhanced Disk Format Consistency Check + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qed.h" + +typedef struct { + BDRVQEDState *s; + BdrvCheckResult *result; + bool fix; /* whether to fix invalid offsets */ + + uint64_t nclusters; + uint32_t *used_clusters; /* referenced cluster bitmap */ + + QEDRequest request; +} QEDCheck; + +static bool qed_test_bit(uint32_t *bitmap, uint64_t n) { + return !!(bitmap[n / 32] & (1 << (n % 32))); +} + +static void qed_set_bit(uint32_t *bitmap, uint64_t n) { + bitmap[n / 32] |= 1 << (n % 32); +} + +/** + * Set bitmap bits for clusters + * + * @check: Check structure + * @offset: Starting offset in bytes + * @n: Number of clusters + */ +static bool qed_set_used_clusters(QEDCheck *check, uint64_t offset, + unsigned int n) +{ + uint64_t cluster = qed_bytes_to_clusters(check->s, offset); + unsigned int corruptions = 0; + + while (n-- != 0) { + /* Clusters should only be referenced once */ + if (qed_test_bit(check->used_clusters, cluster)) { + corruptions++; + } + + qed_set_bit(check->used_clusters, cluster); + cluster++; + } + + check->result->corruptions += corruptions; + return corruptions == 0; +} + +/** + * Check an L2 table + * + * @ret: Number of invalid cluster offsets + */ +static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table) +{ + BDRVQEDState *s = check->s; + unsigned int i, num_invalid = 0; + uint64_t last_offset = 0; + + for (i = 0; i < s->table_nelems; i++) { + uint64_t offset = table->offsets[i]; + + if (qed_offset_is_unalloc_cluster(offset) || + qed_offset_is_zero_cluster(offset)) { + continue; + } + check->result->bfi.allocated_clusters++; + if (last_offset && (last_offset + s->header.cluster_size != offset)) { + check->result->bfi.fragmented_clusters++; + } + last_offset = offset; + + /* Detect invalid cluster offset */ + if (!qed_check_cluster_offset(s, offset)) { + if (check->fix) { + table->offsets[i] = 0; + check->result->corruptions_fixed++; + } else { + check->result->corruptions++; + } + + num_invalid++; + continue; + } + + qed_set_used_clusters(check, offset, 1); + } + + return num_invalid; +} + +/** + * Descend tables and check each cluster is referenced once only + */ +static int qed_check_l1_table(QEDCheck *check, QEDTable *table) +{ + BDRVQEDState *s = check->s; + unsigned int i, num_invalid_l1 = 0; + int ret, last_error = 0; + + /* Mark L1 table clusters used */ + qed_set_used_clusters(check, s->header.l1_table_offset, + s->header.table_size); + + for (i = 0; i < s->table_nelems; i++) { + unsigned int num_invalid_l2; + uint64_t offset = table->offsets[i]; + + if (qed_offset_is_unalloc_cluster(offset)) { + continue; + } + + /* Detect invalid L2 offset */ + if (!qed_check_table_offset(s, offset)) { + /* Clear invalid offset */ + if (check->fix) { + table->offsets[i] = 0; + check->result->corruptions_fixed++; + } else { + check->result->corruptions++; + } + + num_invalid_l1++; + continue; + } + + if (!qed_set_used_clusters(check, offset, s->header.table_size)) { + continue; /* skip an invalid table */ + } + + ret = qed_read_l2_table_sync(s, &check->request, offset); + if (ret) { + check->result->check_errors++; + last_error = ret; + continue; + } + + num_invalid_l2 = qed_check_l2_table(check, + check->request.l2_table->table); + + /* Write out fixed L2 table */ + if (num_invalid_l2 > 0 && check->fix) { + ret = qed_write_l2_table_sync(s, &check->request, 0, + s->table_nelems, false); + if (ret) { + check->result->check_errors++; + last_error = ret; + continue; + } + } + } + + /* Drop reference to final table */ + qed_unref_l2_cache_entry(check->request.l2_table); + check->request.l2_table = NULL; + + /* Write out fixed L1 table */ + if (num_invalid_l1 > 0 && check->fix) { + ret = qed_write_l1_table_sync(s, 0, s->table_nelems); + if (ret) { + check->result->check_errors++; + last_error = ret; + } + } + + return last_error; +} + +/** + * Check for unreferenced (leaked) clusters + */ +static void qed_check_for_leaks(QEDCheck *check) +{ + BDRVQEDState *s = check->s; + uint64_t i; + + for (i = s->header.header_size; i < check->nclusters; i++) { + if (!qed_test_bit(check->used_clusters, i)) { + check->result->leaks++; + } + } +} + +/** + * Mark an image clean once it passes check or has been repaired + */ +static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result) +{ + /* Skip if there were unfixable corruptions or I/O errors */ + if (result->corruptions > 0 || result->check_errors > 0) { + return; + } + + /* Skip if image is already marked clean */ + if (!(s->header.features & QED_F_NEED_CHECK)) { + return; + } + + /* Ensure fixes reach storage before clearing check bit */ + bdrv_flush(s->bs); + + s->header.features &= ~QED_F_NEED_CHECK; + qed_write_header_sync(s); +} + +int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix) +{ + QEDCheck check = { + .s = s, + .result = result, + .nclusters = qed_bytes_to_clusters(s, s->file_size), + .request = { .l2_table = NULL }, + .fix = fix, + }; + int ret; + + check.used_clusters = g_try_new0(uint32_t, (check.nclusters + 31) / 32); + if (check.nclusters && check.used_clusters == NULL) { + return -ENOMEM; + } + + check.result->bfi.total_clusters = + (s->header.image_size + s->header.cluster_size - 1) / + s->header.cluster_size; + ret = qed_check_l1_table(&check, s->l1_table); + if (ret == 0) { + /* Only check for leaks if entire image was scanned successfully */ + qed_check_for_leaks(&check); + + if (fix) { + qed_check_mark_clean(s, result); + } + } + + g_free(check.used_clusters); + return ret; +} diff --git a/block/qed-cluster.c b/block/qed-cluster.c new file mode 100644 index 0000000..f64b2af --- /dev/null +++ b/block/qed-cluster.c @@ -0,0 +1,165 @@ +/* + * QEMU Enhanced Disk Format Cluster functions + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qed.h" + +/** + * Count the number of contiguous data clusters + * + * @s: QED state + * @table: L2 table + * @index: First cluster index + * @n: Maximum number of clusters + * @offset: Set to first cluster offset + * + * This function scans tables for contiguous clusters. A contiguous run of + * clusters may be allocated, unallocated, or zero. + */ +static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s, + QEDTable *table, + unsigned int index, + unsigned int n, + uint64_t *offset) +{ + unsigned int end = MIN(index + n, s->table_nelems); + uint64_t last = table->offsets[index]; + unsigned int i; + + *offset = last; + + for (i = index + 1; i < end; i++) { + if (qed_offset_is_unalloc_cluster(last)) { + /* Counting unallocated clusters */ + if (!qed_offset_is_unalloc_cluster(table->offsets[i])) { + break; + } + } else if (qed_offset_is_zero_cluster(last)) { + /* Counting zero clusters */ + if (!qed_offset_is_zero_cluster(table->offsets[i])) { + break; + } + } else { + /* Counting allocated clusters */ + if (table->offsets[i] != last + s->header.cluster_size) { + break; + } + last = table->offsets[i]; + } + } + return i - index; +} + +typedef struct { + BDRVQEDState *s; + uint64_t pos; + size_t len; + + QEDRequest *request; + + /* User callback */ + QEDFindClusterFunc *cb; + void *opaque; +} QEDFindClusterCB; + +static void qed_find_cluster_cb(void *opaque, int ret) +{ + QEDFindClusterCB *find_cluster_cb = opaque; + BDRVQEDState *s = find_cluster_cb->s; + QEDRequest *request = find_cluster_cb->request; + uint64_t offset = 0; + size_t len = 0; + unsigned int index; + unsigned int n; + + if (ret) { + goto out; + } + + index = qed_l2_index(s, find_cluster_cb->pos); + n = qed_bytes_to_clusters(s, + qed_offset_into_cluster(s, find_cluster_cb->pos) + + find_cluster_cb->len); + n = qed_count_contiguous_clusters(s, request->l2_table->table, + index, n, &offset); + + if (qed_offset_is_unalloc_cluster(offset)) { + ret = QED_CLUSTER_L2; + } else if (qed_offset_is_zero_cluster(offset)) { + ret = QED_CLUSTER_ZERO; + } else if (qed_check_cluster_offset(s, offset)) { + ret = QED_CLUSTER_FOUND; + } else { + ret = -EINVAL; + } + + len = MIN(find_cluster_cb->len, n * s->header.cluster_size - + qed_offset_into_cluster(s, find_cluster_cb->pos)); + +out: + find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len); + g_free(find_cluster_cb); +} + +/** + * Find the offset of a data cluster + * + * @s: QED state + * @request: L2 cache entry + * @pos: Byte position in device + * @len: Number of bytes + * @cb: Completion function + * @opaque: User data for completion function + * + * This function translates a position in the block device to an offset in the + * image file. It invokes the cb completion callback to report back the + * translated offset or unallocated range in the image file. + * + * If the L2 table exists, request->l2_table points to the L2 table cache entry + * and the caller must free the reference when they are finished. The cache + * entry is exposed in this way to avoid callers having to read the L2 table + * again later during request processing. If request->l2_table is non-NULL it + * will be unreferenced before taking on the new cache entry. + */ +void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, + size_t len, QEDFindClusterFunc *cb, void *opaque) +{ + QEDFindClusterCB *find_cluster_cb; + uint64_t l2_offset; + + /* Limit length to L2 boundary. Requests are broken up at the L2 boundary + * so that a request acts on one L2 table at a time. + */ + len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos); + + l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)]; + if (qed_offset_is_unalloc_cluster(l2_offset)) { + cb(opaque, QED_CLUSTER_L1, 0, len); + return; + } + if (!qed_check_table_offset(s, l2_offset)) { + cb(opaque, -EINVAL, 0, 0); + return; + } + + find_cluster_cb = g_malloc(sizeof(*find_cluster_cb)); + find_cluster_cb->s = s; + find_cluster_cb->pos = pos; + find_cluster_cb->len = len; + find_cluster_cb->cb = cb; + find_cluster_cb->opaque = opaque; + find_cluster_cb->request = request; + + qed_read_l2_table(s, request, l2_offset, + qed_find_cluster_cb, find_cluster_cb); +} diff --git a/block/qed-gencb.c b/block/qed-gencb.c new file mode 100644 index 0000000..b817a8b --- /dev/null +++ b/block/qed-gencb.c @@ -0,0 +1,32 @@ +/* + * QEMU Enhanced Disk Format + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qed.h" + +void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque) +{ + GenericCB *gencb = g_malloc(len); + gencb->cb = cb; + gencb->opaque = opaque; + return gencb; +} + +void gencb_complete(void *opaque, int ret) +{ + GenericCB *gencb = opaque; + BlockCompletionFunc *cb = gencb->cb; + void *user_opaque = gencb->opaque; + + g_free(gencb); + cb(user_opaque, ret); +} diff --git a/block/qed-l2-cache.c b/block/qed-l2-cache.c new file mode 100644 index 0000000..e9b2aae --- /dev/null +++ b/block/qed-l2-cache.c @@ -0,0 +1,187 @@ +/* + * QEMU Enhanced Disk Format L2 Cache + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +/* + * L2 table cache usage is as follows: + * + * An open image has one L2 table cache that is used to avoid accessing the + * image file for recently referenced L2 tables. + * + * Cluster offset lookup translates the logical offset within the block device + * to a cluster offset within the image file. This is done by indexing into + * the L1 and L2 tables which store cluster offsets. It is here where the L2 + * table cache serves up recently referenced L2 tables. + * + * If there is a cache miss, that L2 table is read from the image file and + * committed to the cache. Subsequent accesses to that L2 table will be served + * from the cache until the table is evicted from the cache. + * + * L2 tables are also committed to the cache when new L2 tables are allocated + * in the image file. Since the L2 table cache is write-through, the new L2 + * table is first written out to the image file and then committed to the + * cache. + * + * Multiple I/O requests may be using an L2 table cache entry at any given + * time. That means an entry may be in use across several requests and + * reference counting is needed to free the entry at the correct time. In + * particular, an entry evicted from the cache will only be freed once all + * references are dropped. + * + * An in-flight I/O request will hold a reference to a L2 table cache entry for + * the period during which it needs to access the L2 table. This includes + * cluster offset lookup, L2 table allocation, and L2 table update when a new + * data cluster has been allocated. + * + * An interesting case occurs when two requests need to access an L2 table that + * is not in the cache. Since the operation to read the table from the image + * file takes some time to complete, both requests may see a cache miss and + * start reading the L2 table from the image file. The first to finish will + * commit its L2 table into the cache. When the second tries to commit its + * table will be deleted in favor of the existing cache entry. + */ + +#include "trace.h" +#include "qed.h" + +/* Each L2 holds 2GB so this let's us fully cache a 100GB disk */ +#define MAX_L2_CACHE_SIZE 50 + +/** + * Initialize the L2 cache + */ +void qed_init_l2_cache(L2TableCache *l2_cache) +{ + QTAILQ_INIT(&l2_cache->entries); + l2_cache->n_entries = 0; +} + +/** + * Free the L2 cache + */ +void qed_free_l2_cache(L2TableCache *l2_cache) +{ + CachedL2Table *entry, *next_entry; + + QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next_entry) { + qemu_vfree(entry->table); + g_free(entry); + } +} + +/** + * Allocate an uninitialized entry from the cache + * + * The returned entry has a reference count of 1 and is owned by the caller. + * The caller must allocate the actual table field for this entry and it must + * be freeable using qemu_vfree(). + */ +CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache) +{ + CachedL2Table *entry; + + entry = g_malloc0(sizeof(*entry)); + entry->ref++; + + trace_qed_alloc_l2_cache_entry(l2_cache, entry); + + return entry; +} + +/** + * Decrease an entry's reference count and free if necessary when the reference + * count drops to zero. + */ +void qed_unref_l2_cache_entry(CachedL2Table *entry) +{ + if (!entry) { + return; + } + + entry->ref--; + trace_qed_unref_l2_cache_entry(entry, entry->ref); + if (entry->ref == 0) { + qemu_vfree(entry->table); + g_free(entry); + } +} + +/** + * Find an entry in the L2 cache. This may return NULL and it's up to the + * caller to satisfy the cache miss. + * + * For a cached entry, this function increases the reference count and returns + * the entry. + */ +CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset) +{ + CachedL2Table *entry; + + QTAILQ_FOREACH(entry, &l2_cache->entries, node) { + if (entry->offset == offset) { + trace_qed_find_l2_cache_entry(l2_cache, entry, offset, entry->ref); + entry->ref++; + return entry; + } + } + return NULL; +} + +/** + * Commit an L2 cache entry into the cache. This is meant to be used as part of + * the process to satisfy a cache miss. A caller would allocate an entry which + * is not actually in the L2 cache and then once the entry was valid and + * present on disk, the entry can be committed into the cache. + * + * Since the cache is write-through, it's important that this function is not + * called until the entry is present on disk and the L1 has been updated to + * point to the entry. + * + * N.B. This function steals a reference to the l2_table from the caller so the + * caller must obtain a new reference by issuing a call to + * qed_find_l2_cache_entry(). + */ +void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table) +{ + CachedL2Table *entry; + + entry = qed_find_l2_cache_entry(l2_cache, l2_table->offset); + if (entry) { + qed_unref_l2_cache_entry(entry); + qed_unref_l2_cache_entry(l2_table); + return; + } + + /* Evict an unused cache entry so we have space. If all entries are in use + * we can grow the cache temporarily and we try to shrink back down later. + */ + if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) { + CachedL2Table *next; + QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next) { + if (entry->ref > 1) { + continue; + } + + QTAILQ_REMOVE(&l2_cache->entries, entry, node); + l2_cache->n_entries--; + qed_unref_l2_cache_entry(entry); + + /* Stop evicting when we've shrunk back to max size */ + if (l2_cache->n_entries < MAX_L2_CACHE_SIZE) { + break; + } + } + } + + l2_cache->n_entries++; + QTAILQ_INSERT_TAIL(&l2_cache->entries, l2_table, node); +} diff --git a/block/qed-table.c b/block/qed-table.c new file mode 100644 index 0000000..513aa87 --- /dev/null +++ b/block/qed-table.c @@ -0,0 +1,296 @@ +/* + * QEMU Enhanced Disk Format Table I/O + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "trace.h" +#include "qemu/sockets.h" /* for EINPROGRESS on Windows */ +#include "qed.h" + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + QEDTable *table; + + struct iovec iov; + QEMUIOVector qiov; +} QEDReadTableCB; + +static void qed_read_table_cb(void *opaque, int ret) +{ + QEDReadTableCB *read_table_cb = opaque; + QEDTable *table = read_table_cb->table; + int noffsets = read_table_cb->qiov.size / sizeof(uint64_t); + int i; + + /* Handle I/O error */ + if (ret) { + goto out; + } + + /* Byteswap offsets */ + for (i = 0; i < noffsets; i++) { + table->offsets[i] = le64_to_cpu(table->offsets[i]); + } + +out: + /* Completion */ + trace_qed_read_table_cb(read_table_cb->s, read_table_cb->table, ret); + gencb_complete(&read_table_cb->gencb, ret); +} + +static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, + BlockCompletionFunc *cb, void *opaque) +{ + QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb), + cb, opaque); + QEMUIOVector *qiov = &read_table_cb->qiov; + + trace_qed_read_table(s, offset, table); + + read_table_cb->s = s; + read_table_cb->table = table; + read_table_cb->iov.iov_base = table->offsets, + read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size, + + qemu_iovec_init_external(qiov, &read_table_cb->iov, 1); + bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, + qiov->size / BDRV_SECTOR_SIZE, + qed_read_table_cb, read_table_cb); +} + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + QEDTable *orig_table; + QEDTable *table; + bool flush; /* flush after write? */ + + struct iovec iov; + QEMUIOVector qiov; +} QEDWriteTableCB; + +static void qed_write_table_cb(void *opaque, int ret) +{ + QEDWriteTableCB *write_table_cb = opaque; + + trace_qed_write_table_cb(write_table_cb->s, + write_table_cb->orig_table, + write_table_cb->flush, + ret); + + if (ret) { + goto out; + } + + if (write_table_cb->flush) { + /* We still need to flush first */ + write_table_cb->flush = false; + bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb, + write_table_cb); + return; + } + +out: + qemu_vfree(write_table_cb->table); + gencb_complete(&write_table_cb->gencb, ret); +} + +/** + * Write out an updated part or all of a table + * + * @s: QED state + * @offset: Offset of table in image file, in bytes + * @table: Table + * @index: Index of first element + * @n: Number of elements + * @flush: Whether or not to sync to disk + * @cb: Completion function + * @opaque: Argument for completion function + */ +static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, + unsigned int index, unsigned int n, bool flush, + BlockCompletionFunc *cb, void *opaque) +{ + QEDWriteTableCB *write_table_cb; + unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1; + unsigned int start, end, i; + size_t len_bytes; + + trace_qed_write_table(s, offset, table, index, n); + + /* Calculate indices of the first and one after last elements */ + start = index & ~sector_mask; + end = (index + n + sector_mask) & ~sector_mask; + + len_bytes = (end - start) * sizeof(uint64_t); + + write_table_cb = gencb_alloc(sizeof(*write_table_cb), cb, opaque); + write_table_cb->s = s; + write_table_cb->orig_table = table; + write_table_cb->flush = flush; + write_table_cb->table = qemu_blockalign(s->bs, len_bytes); + write_table_cb->iov.iov_base = write_table_cb->table->offsets; + write_table_cb->iov.iov_len = len_bytes; + qemu_iovec_init_external(&write_table_cb->qiov, &write_table_cb->iov, 1); + + /* Byteswap table */ + for (i = start; i < end; i++) { + uint64_t le_offset = cpu_to_le64(table->offsets[i]); + write_table_cb->table->offsets[i - start] = le_offset; + } + + /* Adjust for offset into table */ + offset += start * sizeof(uint64_t); + + bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, + &write_table_cb->qiov, + write_table_cb->qiov.size / BDRV_SECTOR_SIZE, + qed_write_table_cb, write_table_cb); +} + +/** + * Propagate return value from async callback + */ +static void qed_sync_cb(void *opaque, int ret) +{ + *(int *)opaque = ret; +} + +int qed_read_l1_table_sync(BDRVQEDState *s) +{ + int ret = -EINPROGRESS; + + qed_read_table(s, s->header.l1_table_offset, + s->l1_table, qed_sync_cb, &ret); + while (ret == -EINPROGRESS) { + aio_poll(bdrv_get_aio_context(s->bs), true); + } + + return ret; +} + +void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, + BlockCompletionFunc *cb, void *opaque) +{ + BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE); + qed_write_table(s, s->header.l1_table_offset, + s->l1_table, index, n, false, cb, opaque); +} + +int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, + unsigned int n) +{ + int ret = -EINPROGRESS; + + qed_write_l1_table(s, index, n, qed_sync_cb, &ret); + while (ret == -EINPROGRESS) { + aio_poll(bdrv_get_aio_context(s->bs), true); + } + + return ret; +} + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + uint64_t l2_offset; + QEDRequest *request; +} QEDReadL2TableCB; + +static void qed_read_l2_table_cb(void *opaque, int ret) +{ + QEDReadL2TableCB *read_l2_table_cb = opaque; + QEDRequest *request = read_l2_table_cb->request; + BDRVQEDState *s = read_l2_table_cb->s; + CachedL2Table *l2_table = request->l2_table; + uint64_t l2_offset = read_l2_table_cb->l2_offset; + + if (ret) { + /* can't trust loaded L2 table anymore */ + qed_unref_l2_cache_entry(l2_table); + request->l2_table = NULL; + } else { + l2_table->offset = l2_offset; + + qed_commit_l2_cache_entry(&s->l2_cache, l2_table); + + /* This is guaranteed to succeed because we just committed the entry + * to the cache. + */ + request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); + assert(request->l2_table != NULL); + } + + gencb_complete(&read_l2_table_cb->gencb, ret); +} + +void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, + BlockCompletionFunc *cb, void *opaque) +{ + QEDReadL2TableCB *read_l2_table_cb; + + qed_unref_l2_cache_entry(request->l2_table); + + /* Check for cached L2 entry */ + request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset); + if (request->l2_table) { + cb(opaque, 0); + return; + } + + request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); + request->l2_table->table = qed_alloc_table(s); + + read_l2_table_cb = gencb_alloc(sizeof(*read_l2_table_cb), cb, opaque); + read_l2_table_cb->s = s; + read_l2_table_cb->l2_offset = offset; + read_l2_table_cb->request = request; + + BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD); + qed_read_table(s, offset, request->l2_table->table, + qed_read_l2_table_cb, read_l2_table_cb); +} + +int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset) +{ + int ret = -EINPROGRESS; + + qed_read_l2_table(s, request, offset, qed_sync_cb, &ret); + while (ret == -EINPROGRESS) { + aio_poll(bdrv_get_aio_context(s->bs), true); + } + + return ret; +} + +void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush, + BlockCompletionFunc *cb, void *opaque) +{ + BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE); + qed_write_table(s, request->l2_table->offset, + request->l2_table->table, index, n, flush, cb, opaque); +} + +int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush) +{ + int ret = -EINPROGRESS; + + qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret); + while (ret == -EINPROGRESS) { + aio_poll(bdrv_get_aio_context(s->bs), true); + } + + return ret; +} diff --git a/block/qed.c b/block/qed.c new file mode 100644 index 0000000..5bbe069 --- /dev/null +++ b/block/qed.c @@ -0,0 +1,1693 @@ +/* + * QEMU Enhanced Disk Format + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/timer.h" +#include "trace.h" +#include "qed.h" +#include "qapi/qmp/qerror.h" +#include "migration/migration.h" + +static const AIOCBInfo qed_aiocb_info = { + .aiocb_size = sizeof(QEDAIOCB), +}; + +static int bdrv_qed_probe(const uint8_t *buf, int buf_size, + const char *filename) +{ + const QEDHeader *header = (const QEDHeader *)buf; + + if (buf_size < sizeof(*header)) { + return 0; + } + if (le32_to_cpu(header->magic) != QED_MAGIC) { + return 0; + } + return 100; +} + +/** + * Check whether an image format is raw + * + * @fmt: Backing file format, may be NULL + */ +static bool qed_fmt_is_raw(const char *fmt) +{ + return fmt && strcmp(fmt, "raw") == 0; +} + +static void qed_header_le_to_cpu(const QEDHeader *le, QEDHeader *cpu) +{ + cpu->magic = le32_to_cpu(le->magic); + cpu->cluster_size = le32_to_cpu(le->cluster_size); + cpu->table_size = le32_to_cpu(le->table_size); + cpu->header_size = le32_to_cpu(le->header_size); + cpu->features = le64_to_cpu(le->features); + cpu->compat_features = le64_to_cpu(le->compat_features); + cpu->autoclear_features = le64_to_cpu(le->autoclear_features); + cpu->l1_table_offset = le64_to_cpu(le->l1_table_offset); + cpu->image_size = le64_to_cpu(le->image_size); + cpu->backing_filename_offset = le32_to_cpu(le->backing_filename_offset); + cpu->backing_filename_size = le32_to_cpu(le->backing_filename_size); +} + +static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le) +{ + le->magic = cpu_to_le32(cpu->magic); + le->cluster_size = cpu_to_le32(cpu->cluster_size); + le->table_size = cpu_to_le32(cpu->table_size); + le->header_size = cpu_to_le32(cpu->header_size); + le->features = cpu_to_le64(cpu->features); + le->compat_features = cpu_to_le64(cpu->compat_features); + le->autoclear_features = cpu_to_le64(cpu->autoclear_features); + le->l1_table_offset = cpu_to_le64(cpu->l1_table_offset); + le->image_size = cpu_to_le64(cpu->image_size); + le->backing_filename_offset = cpu_to_le32(cpu->backing_filename_offset); + le->backing_filename_size = cpu_to_le32(cpu->backing_filename_size); +} + +int qed_write_header_sync(BDRVQEDState *s) +{ + QEDHeader le; + int ret; + + qed_header_cpu_to_le(&s->header, &le); + ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le)); + if (ret != sizeof(le)) { + return ret; + } + return 0; +} + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + struct iovec iov; + QEMUIOVector qiov; + int nsectors; + uint8_t *buf; +} QEDWriteHeaderCB; + +static void qed_write_header_cb(void *opaque, int ret) +{ + QEDWriteHeaderCB *write_header_cb = opaque; + + qemu_vfree(write_header_cb->buf); + gencb_complete(write_header_cb, ret); +} + +static void qed_write_header_read_cb(void *opaque, int ret) +{ + QEDWriteHeaderCB *write_header_cb = opaque; + BDRVQEDState *s = write_header_cb->s; + + if (ret) { + qed_write_header_cb(write_header_cb, ret); + return; + } + + /* Update header */ + qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf); + + bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov, + write_header_cb->nsectors, qed_write_header_cb, + write_header_cb); +} + +/** + * Update header in-place (does not rewrite backing filename or other strings) + * + * This function only updates known header fields in-place and does not affect + * extra data after the QED header. + */ +static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb, + void *opaque) +{ + /* We must write full sectors for O_DIRECT but cannot necessarily generate + * the data following the header if an unrecognized compat feature is + * active. Therefore, first read the sectors containing the header, update + * them, and write back. + */ + + int nsectors = (sizeof(QEDHeader) + BDRV_SECTOR_SIZE - 1) / + BDRV_SECTOR_SIZE; + size_t len = nsectors * BDRV_SECTOR_SIZE; + QEDWriteHeaderCB *write_header_cb = gencb_alloc(sizeof(*write_header_cb), + cb, opaque); + + write_header_cb->s = s; + write_header_cb->nsectors = nsectors; + write_header_cb->buf = qemu_blockalign(s->bs, len); + write_header_cb->iov.iov_base = write_header_cb->buf; + write_header_cb->iov.iov_len = len; + qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1); + + bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors, + qed_write_header_read_cb, write_header_cb); +} + +static uint64_t qed_max_image_size(uint32_t cluster_size, uint32_t table_size) +{ + uint64_t table_entries; + uint64_t l2_size; + + table_entries = (table_size * cluster_size) / sizeof(uint64_t); + l2_size = table_entries * cluster_size; + + return l2_size * table_entries; +} + +static bool qed_is_cluster_size_valid(uint32_t cluster_size) +{ + if (cluster_size < QED_MIN_CLUSTER_SIZE || + cluster_size > QED_MAX_CLUSTER_SIZE) { + return false; + } + if (cluster_size & (cluster_size - 1)) { + return false; /* not power of 2 */ + } + return true; +} + +static bool qed_is_table_size_valid(uint32_t table_size) +{ + if (table_size < QED_MIN_TABLE_SIZE || + table_size > QED_MAX_TABLE_SIZE) { + return false; + } + if (table_size & (table_size - 1)) { + return false; /* not power of 2 */ + } + return true; +} + +static bool qed_is_image_size_valid(uint64_t image_size, uint32_t cluster_size, + uint32_t table_size) +{ + if (image_size % BDRV_SECTOR_SIZE != 0) { + return false; /* not multiple of sector size */ + } + if (image_size > qed_max_image_size(cluster_size, table_size)) { + return false; /* image is too large */ + } + return true; +} + +/** + * Read a string of known length from the image file + * + * @file: Image file + * @offset: File offset to start of string, in bytes + * @n: String length in bytes + * @buf: Destination buffer + * @buflen: Destination buffer length in bytes + * @ret: 0 on success, -errno on failure + * + * The string is NUL-terminated. + */ +static int qed_read_string(BlockDriverState *file, uint64_t offset, size_t n, + char *buf, size_t buflen) +{ + int ret; + if (n >= buflen) { + return -EINVAL; + } + ret = bdrv_pread(file, offset, buf, n); + if (ret < 0) { + return ret; + } + buf[n] = '\0'; + return 0; +} + +/** + * Allocate new clusters + * + * @s: QED state + * @n: Number of contiguous clusters to allocate + * @ret: Offset of first allocated cluster + * + * This function only produces the offset where the new clusters should be + * written. It updates BDRVQEDState but does not make any changes to the image + * file. + */ +static uint64_t qed_alloc_clusters(BDRVQEDState *s, unsigned int n) +{ + uint64_t offset = s->file_size; + s->file_size += n * s->header.cluster_size; + return offset; +} + +QEDTable *qed_alloc_table(BDRVQEDState *s) +{ + /* Honor O_DIRECT memory alignment requirements */ + return qemu_blockalign(s->bs, + s->header.cluster_size * s->header.table_size); +} + +/** + * Allocate a new zeroed L2 table + */ +static CachedL2Table *qed_new_l2_table(BDRVQEDState *s) +{ + CachedL2Table *l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); + + l2_table->table = qed_alloc_table(s); + l2_table->offset = qed_alloc_clusters(s, s->header.table_size); + + memset(l2_table->table->offsets, 0, + s->header.cluster_size * s->header.table_size); + return l2_table; +} + +static void qed_aio_next_io(void *opaque, int ret); + +static void qed_plug_allocating_write_reqs(BDRVQEDState *s) +{ + assert(!s->allocating_write_reqs_plugged); + + s->allocating_write_reqs_plugged = true; +} + +static void qed_unplug_allocating_write_reqs(BDRVQEDState *s) +{ + QEDAIOCB *acb; + + assert(s->allocating_write_reqs_plugged); + + s->allocating_write_reqs_plugged = false; + + acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); + if (acb) { + qed_aio_next_io(acb, 0); + } +} + +static void qed_finish_clear_need_check(void *opaque, int ret) +{ + /* Do nothing */ +} + +static void qed_flush_after_clear_need_check(void *opaque, int ret) +{ + BDRVQEDState *s = opaque; + + bdrv_aio_flush(s->bs, qed_finish_clear_need_check, s); + + /* No need to wait until flush completes */ + qed_unplug_allocating_write_reqs(s); +} + +static void qed_clear_need_check(void *opaque, int ret) +{ + BDRVQEDState *s = opaque; + + if (ret) { + qed_unplug_allocating_write_reqs(s); + return; + } + + s->header.features &= ~QED_F_NEED_CHECK; + qed_write_header(s, qed_flush_after_clear_need_check, s); +} + +static void qed_need_check_timer_cb(void *opaque) +{ + BDRVQEDState *s = opaque; + + /* The timer should only fire when allocating writes have drained */ + assert(!QSIMPLEQ_FIRST(&s->allocating_write_reqs)); + + trace_qed_need_check_timer_cb(s); + + qed_plug_allocating_write_reqs(s); + + /* Ensure writes are on disk before clearing flag */ + bdrv_aio_flush(s->bs, qed_clear_need_check, s); +} + +static void qed_start_need_check_timer(BDRVQEDState *s) +{ + trace_qed_start_need_check_timer(s); + + /* Use QEMU_CLOCK_VIRTUAL so we don't alter the image file while suspended for + * migration. + */ + timer_mod(s->need_check_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + get_ticks_per_sec() * QED_NEED_CHECK_TIMEOUT); +} + +/* It's okay to call this multiple times or when no timer is started */ +static void qed_cancel_need_check_timer(BDRVQEDState *s) +{ + trace_qed_cancel_need_check_timer(s); + timer_del(s->need_check_timer); +} + +static void bdrv_qed_rebind(BlockDriverState *bs) +{ + BDRVQEDState *s = bs->opaque; + s->bs = bs; +} + +static void bdrv_qed_detach_aio_context(BlockDriverState *bs) +{ + BDRVQEDState *s = bs->opaque; + + qed_cancel_need_check_timer(s); + timer_free(s->need_check_timer); +} + +static void bdrv_qed_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVQEDState *s = bs->opaque; + + s->need_check_timer = aio_timer_new(new_context, + QEMU_CLOCK_VIRTUAL, SCALE_NS, + qed_need_check_timer_cb, s); + if (s->header.features & QED_F_NEED_CHECK) { + qed_start_need_check_timer(s); + } +} + +static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVQEDState *s = bs->opaque; + QEDHeader le_header; + int64_t file_size; + int ret; + + s->bs = bs; + QSIMPLEQ_INIT(&s->allocating_write_reqs); + + ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); + if (ret < 0) { + return ret; + } + qed_header_le_to_cpu(&le_header, &s->header); + + if (s->header.magic != QED_MAGIC) { + error_setg(errp, "Image not in QED format"); + return -EINVAL; + } + if (s->header.features & ~QED_FEATURE_MASK) { + /* image uses unsupported feature bits */ + char buf[64]; + snprintf(buf, sizeof(buf), "%" PRIx64, + s->header.features & ~QED_FEATURE_MASK); + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bdrv_get_device_or_node_name(bs), "QED", buf); + return -ENOTSUP; + } + if (!qed_is_cluster_size_valid(s->header.cluster_size)) { + return -EINVAL; + } + + /* Round down file size to the last cluster */ + file_size = bdrv_getlength(bs->file); + if (file_size < 0) { + return file_size; + } + s->file_size = qed_start_of_cluster(s, file_size); + + if (!qed_is_table_size_valid(s->header.table_size)) { + return -EINVAL; + } + if (!qed_is_image_size_valid(s->header.image_size, + s->header.cluster_size, + s->header.table_size)) { + return -EINVAL; + } + if (!qed_check_table_offset(s, s->header.l1_table_offset)) { + return -EINVAL; + } + + s->table_nelems = (s->header.cluster_size * s->header.table_size) / + sizeof(uint64_t); + s->l2_shift = ctz32(s->header.cluster_size); + s->l2_mask = s->table_nelems - 1; + s->l1_shift = s->l2_shift + ctz32(s->table_nelems); + + /* Header size calculation must not overflow uint32_t */ + if (s->header.header_size > UINT32_MAX / s->header.cluster_size) { + return -EINVAL; + } + + if ((s->header.features & QED_F_BACKING_FILE)) { + if ((uint64_t)s->header.backing_filename_offset + + s->header.backing_filename_size > + s->header.cluster_size * s->header.header_size) { + return -EINVAL; + } + + ret = qed_read_string(bs->file, s->header.backing_filename_offset, + s->header.backing_filename_size, bs->backing_file, + sizeof(bs->backing_file)); + if (ret < 0) { + return ret; + } + + if (s->header.features & QED_F_BACKING_FORMAT_NO_PROBE) { + pstrcpy(bs->backing_format, sizeof(bs->backing_format), "raw"); + } + } + + /* Reset unknown autoclear feature bits. This is a backwards + * compatibility mechanism that allows images to be opened by older + * programs, which "knock out" unknown feature bits. When an image is + * opened by a newer program again it can detect that the autoclear + * feature is no longer valid. + */ + if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 && + !bdrv_is_read_only(bs->file) && !(flags & BDRV_O_INCOMING)) { + s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK; + + ret = qed_write_header_sync(s); + if (ret) { + return ret; + } + + /* From here on only known autoclear feature bits are valid */ + bdrv_flush(bs->file); + } + + s->l1_table = qed_alloc_table(s); + qed_init_l2_cache(&s->l2_cache); + + ret = qed_read_l1_table_sync(s); + if (ret) { + goto out; + } + + /* If image was not closed cleanly, check consistency */ + if (!(flags & BDRV_O_CHECK) && (s->header.features & QED_F_NEED_CHECK)) { + /* Read-only images cannot be fixed. There is no risk of corruption + * since write operations are not possible. Therefore, allow + * potentially inconsistent images to be opened read-only. This can + * aid data recovery from an otherwise inconsistent image. + */ + if (!bdrv_is_read_only(bs->file) && + !(flags & BDRV_O_INCOMING)) { + BdrvCheckResult result = {0}; + + ret = qed_check(s, &result, true); + if (ret) { + goto out; + } + } + } + + bdrv_qed_attach_aio_context(bs, bdrv_get_aio_context(bs)); + +out: + if (ret) { + qed_free_l2_cache(&s->l2_cache); + qemu_vfree(s->l1_table); + } + return ret; +} + +static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BDRVQEDState *s = bs->opaque; + + bs->bl.write_zeroes_alignment = s->header.cluster_size >> BDRV_SECTOR_BITS; +} + +/* We have nothing to do for QED reopen, stubs just return + * success */ +static int bdrv_qed_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + return 0; +} + +static void bdrv_qed_close(BlockDriverState *bs) +{ + BDRVQEDState *s = bs->opaque; + + bdrv_qed_detach_aio_context(bs); + + /* Ensure writes reach stable storage */ + bdrv_flush(bs->file); + + /* Clean shutdown, no check required on next open */ + if (s->header.features & QED_F_NEED_CHECK) { + s->header.features &= ~QED_F_NEED_CHECK; + qed_write_header_sync(s); + } + + qed_free_l2_cache(&s->l2_cache); + qemu_vfree(s->l1_table); +} + +static int qed_create(const char *filename, uint32_t cluster_size, + uint64_t image_size, uint32_t table_size, + const char *backing_file, const char *backing_fmt, + QemuOpts *opts, Error **errp) +{ + QEDHeader header = { + .magic = QED_MAGIC, + .cluster_size = cluster_size, + .table_size = table_size, + .header_size = 1, + .features = 0, + .compat_features = 0, + .l1_table_offset = cluster_size, + .image_size = image_size, + }; + QEDHeader le_header; + uint8_t *l1_table = NULL; + size_t l1_size = header.cluster_size * header.table_size; + Error *local_err = NULL; + int ret = 0; + BlockDriverState *bs; + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return ret; + } + + bs = NULL; + ret = bdrv_open(&bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL, + &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return ret; + } + + /* File must start empty and grow, check truncate is supported */ + ret = bdrv_truncate(bs, 0); + if (ret < 0) { + goto out; + } + + if (backing_file) { + header.features |= QED_F_BACKING_FILE; + header.backing_filename_offset = sizeof(le_header); + header.backing_filename_size = strlen(backing_file); + + if (qed_fmt_is_raw(backing_fmt)) { + header.features |= QED_F_BACKING_FORMAT_NO_PROBE; + } + } + + qed_header_cpu_to_le(&header, &le_header); + ret = bdrv_pwrite(bs, 0, &le_header, sizeof(le_header)); + if (ret < 0) { + goto out; + } + ret = bdrv_pwrite(bs, sizeof(le_header), backing_file, + header.backing_filename_size); + if (ret < 0) { + goto out; + } + + l1_table = g_malloc0(l1_size); + ret = bdrv_pwrite(bs, header.l1_table_offset, l1_table, l1_size); + if (ret < 0) { + goto out; + } + + ret = 0; /* success */ +out: + g_free(l1_table); + bdrv_unref(bs); + return ret; +} + +static int bdrv_qed_create(const char *filename, QemuOpts *opts, Error **errp) +{ + uint64_t image_size = 0; + uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE; + uint32_t table_size = QED_DEFAULT_TABLE_SIZE; + char *backing_file = NULL; + char *backing_fmt = NULL; + int ret; + + image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); + backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT); + cluster_size = qemu_opt_get_size_del(opts, + BLOCK_OPT_CLUSTER_SIZE, + QED_DEFAULT_CLUSTER_SIZE); + table_size = qemu_opt_get_size_del(opts, BLOCK_OPT_TABLE_SIZE, + QED_DEFAULT_TABLE_SIZE); + + if (!qed_is_cluster_size_valid(cluster_size)) { + error_setg(errp, "QED cluster size must be within range [%u, %u] " + "and power of 2", + QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE); + ret = -EINVAL; + goto finish; + } + if (!qed_is_table_size_valid(table_size)) { + error_setg(errp, "QED table size must be within range [%u, %u] " + "and power of 2", + QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE); + ret = -EINVAL; + goto finish; + } + if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) { + error_setg(errp, "QED image size must be a non-zero multiple of " + "cluster size and less than %" PRIu64 " bytes", + qed_max_image_size(cluster_size, table_size)); + ret = -EINVAL; + goto finish; + } + + ret = qed_create(filename, cluster_size, image_size, table_size, + backing_file, backing_fmt, opts, errp); + +finish: + g_free(backing_file); + g_free(backing_fmt); + return ret; +} + +typedef struct { + BlockDriverState *bs; + Coroutine *co; + uint64_t pos; + int64_t status; + int *pnum; +} QEDIsAllocatedCB; + +static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len) +{ + QEDIsAllocatedCB *cb = opaque; + BDRVQEDState *s = cb->bs->opaque; + *cb->pnum = len / BDRV_SECTOR_SIZE; + switch (ret) { + case QED_CLUSTER_FOUND: + offset |= qed_offset_into_cluster(s, cb->pos); + cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; + break; + case QED_CLUSTER_ZERO: + cb->status = BDRV_BLOCK_ZERO; + break; + case QED_CLUSTER_L2: + case QED_CLUSTER_L1: + cb->status = 0; + break; + default: + assert(ret < 0); + cb->status = ret; + break; + } + + if (cb->co) { + qemu_coroutine_enter(cb->co, NULL); + } +} + +static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + BDRVQEDState *s = bs->opaque; + size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE; + QEDIsAllocatedCB cb = { + .bs = bs, + .pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE, + .status = BDRV_BLOCK_OFFSET_MASK, + .pnum = pnum, + }; + QEDRequest request = { .l2_table = NULL }; + + qed_find_cluster(s, &request, cb.pos, len, qed_is_allocated_cb, &cb); + + /* Now sleep if the callback wasn't invoked immediately */ + while (cb.status == BDRV_BLOCK_OFFSET_MASK) { + cb.co = qemu_coroutine_self(); + qemu_coroutine_yield(); + } + + qed_unref_l2_cache_entry(request.l2_table); + + return cb.status; +} + +static BDRVQEDState *acb_to_s(QEDAIOCB *acb) +{ + return acb->common.bs->opaque; +} + +/** + * Read from the backing file or zero-fill if no backing file + * + * @s: QED state + * @pos: Byte position in device + * @qiov: Destination I/O vector + * @backing_qiov: Possibly shortened copy of qiov, to be allocated here + * @cb: Completion function + * @opaque: User data for completion function + * + * This function reads qiov->size bytes starting at pos from the backing file. + * If there is no backing file then zeroes are read. + */ +static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, + QEMUIOVector *qiov, + QEMUIOVector **backing_qiov, + BlockCompletionFunc *cb, void *opaque) +{ + uint64_t backing_length = 0; + size_t size; + + /* If there is a backing file, get its length. Treat the absence of a + * backing file like a zero length backing file. + */ + if (s->bs->backing_hd) { + int64_t l = bdrv_getlength(s->bs->backing_hd); + if (l < 0) { + cb(opaque, l); + return; + } + backing_length = l; + } + + /* Zero all sectors if reading beyond the end of the backing file */ + if (pos >= backing_length || + pos + qiov->size > backing_length) { + qemu_iovec_memset(qiov, 0, 0, qiov->size); + } + + /* Complete now if there are no backing file sectors to read */ + if (pos >= backing_length) { + cb(opaque, 0); + return; + } + + /* If the read straddles the end of the backing file, shorten it */ + size = MIN((uint64_t)backing_length - pos, qiov->size); + + assert(*backing_qiov == NULL); + *backing_qiov = g_new(QEMUIOVector, 1); + qemu_iovec_init(*backing_qiov, qiov->niov); + qemu_iovec_concat(*backing_qiov, qiov, 0, size); + + BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO); + bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE, + *backing_qiov, size / BDRV_SECTOR_SIZE, cb, opaque); +} + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + QEMUIOVector qiov; + QEMUIOVector *backing_qiov; + struct iovec iov; + uint64_t offset; +} CopyFromBackingFileCB; + +static void qed_copy_from_backing_file_cb(void *opaque, int ret) +{ + CopyFromBackingFileCB *copy_cb = opaque; + qemu_vfree(copy_cb->iov.iov_base); + gencb_complete(©_cb->gencb, ret); +} + +static void qed_copy_from_backing_file_write(void *opaque, int ret) +{ + CopyFromBackingFileCB *copy_cb = opaque; + BDRVQEDState *s = copy_cb->s; + + if (copy_cb->backing_qiov) { + qemu_iovec_destroy(copy_cb->backing_qiov); + g_free(copy_cb->backing_qiov); + copy_cb->backing_qiov = NULL; + } + + if (ret) { + qed_copy_from_backing_file_cb(copy_cb, ret); + return; + } + + BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE); + bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE, + ©_cb->qiov, copy_cb->qiov.size / BDRV_SECTOR_SIZE, + qed_copy_from_backing_file_cb, copy_cb); +} + +/** + * Copy data from backing file into the image + * + * @s: QED state + * @pos: Byte position in device + * @len: Number of bytes + * @offset: Byte offset in image file + * @cb: Completion function + * @opaque: User data for completion function + */ +static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos, + uint64_t len, uint64_t offset, + BlockCompletionFunc *cb, + void *opaque) +{ + CopyFromBackingFileCB *copy_cb; + + /* Skip copy entirely if there is no work to do */ + if (len == 0) { + cb(opaque, 0); + return; + } + + copy_cb = gencb_alloc(sizeof(*copy_cb), cb, opaque); + copy_cb->s = s; + copy_cb->offset = offset; + copy_cb->backing_qiov = NULL; + copy_cb->iov.iov_base = qemu_blockalign(s->bs, len); + copy_cb->iov.iov_len = len; + qemu_iovec_init_external(©_cb->qiov, ©_cb->iov, 1); + + qed_read_backing_file(s, pos, ©_cb->qiov, ©_cb->backing_qiov, + qed_copy_from_backing_file_write, copy_cb); +} + +/** + * Link one or more contiguous clusters into a table + * + * @s: QED state + * @table: L2 table + * @index: First cluster index + * @n: Number of contiguous clusters + * @cluster: First cluster offset + * + * The cluster offset may be an allocated byte offset in the image file, the + * zero cluster marker, or the unallocated cluster marker. + */ +static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index, + unsigned int n, uint64_t cluster) +{ + int i; + for (i = index; i < index + n; i++) { + table->offsets[i] = cluster; + if (!qed_offset_is_unalloc_cluster(cluster) && + !qed_offset_is_zero_cluster(cluster)) { + cluster += s->header.cluster_size; + } + } +} + +static void qed_aio_complete_bh(void *opaque) +{ + QEDAIOCB *acb = opaque; + BlockCompletionFunc *cb = acb->common.cb; + void *user_opaque = acb->common.opaque; + int ret = acb->bh_ret; + + qemu_bh_delete(acb->bh); + qemu_aio_unref(acb); + + /* Invoke callback */ + cb(user_opaque, ret); +} + +static void qed_aio_complete(QEDAIOCB *acb, int ret) +{ + BDRVQEDState *s = acb_to_s(acb); + + trace_qed_aio_complete(s, acb, ret); + + /* Free resources */ + qemu_iovec_destroy(&acb->cur_qiov); + qed_unref_l2_cache_entry(acb->request.l2_table); + + /* Free the buffer we may have allocated for zero writes */ + if (acb->flags & QED_AIOCB_ZERO) { + qemu_vfree(acb->qiov->iov[0].iov_base); + acb->qiov->iov[0].iov_base = NULL; + } + + /* Arrange for a bh to invoke the completion function */ + acb->bh_ret = ret; + acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs), + qed_aio_complete_bh, acb); + qemu_bh_schedule(acb->bh); + + /* Start next allocating write request waiting behind this one. Note that + * requests enqueue themselves when they first hit an unallocated cluster + * but they wait until the entire request is finished before waking up the + * next request in the queue. This ensures that we don't cycle through + * requests multiple times but rather finish one at a time completely. + */ + if (acb == QSIMPLEQ_FIRST(&s->allocating_write_reqs)) { + QSIMPLEQ_REMOVE_HEAD(&s->allocating_write_reqs, next); + acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); + if (acb) { + qed_aio_next_io(acb, 0); + } else if (s->header.features & QED_F_NEED_CHECK) { + qed_start_need_check_timer(s); + } + } +} + +/** + * Commit the current L2 table to the cache + */ +static void qed_commit_l2_update(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + CachedL2Table *l2_table = acb->request.l2_table; + uint64_t l2_offset = l2_table->offset; + + qed_commit_l2_cache_entry(&s->l2_cache, l2_table); + + /* This is guaranteed to succeed because we just committed the entry to the + * cache. + */ + acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); + assert(acb->request.l2_table != NULL); + + qed_aio_next_io(opaque, ret); +} + +/** + * Update L1 table with new L2 table offset and write it out + */ +static void qed_aio_write_l1_update(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + int index; + + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + index = qed_l1_index(s, acb->cur_pos); + s->l1_table->offsets[index] = acb->request.l2_table->offset; + + qed_write_l1_table(s, index, 1, qed_commit_l2_update, acb); +} + +/** + * Update L2 table with new cluster offsets and write them out + */ +static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset) +{ + BDRVQEDState *s = acb_to_s(acb); + bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1; + int index; + + if (ret) { + goto err; + } + + if (need_alloc) { + qed_unref_l2_cache_entry(acb->request.l2_table); + acb->request.l2_table = qed_new_l2_table(s); + } + + index = qed_l2_index(s, acb->cur_pos); + qed_update_l2_table(s, acb->request.l2_table->table, index, acb->cur_nclusters, + offset); + + if (need_alloc) { + /* Write out the whole new L2 table */ + qed_write_l2_table(s, &acb->request, 0, s->table_nelems, true, + qed_aio_write_l1_update, acb); + } else { + /* Write out only the updated part of the L2 table */ + qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters, false, + qed_aio_next_io, acb); + } + return; + +err: + qed_aio_complete(acb, ret); +} + +static void qed_aio_write_l2_update_cb(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + qed_aio_write_l2_update(acb, ret, acb->cur_cluster); +} + +/** + * Flush new data clusters before updating the L2 table + * + * This flush is necessary when a backing file is in use. A crash during an + * allocating write could result in empty clusters in the image. If the write + * only touched a subregion of the cluster, then backing image sectors have + * been lost in the untouched region. The solution is to flush after writing a + * new data cluster and before updating the L2 table. + */ +static void qed_aio_write_flush_before_l2_update(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + + if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update_cb, opaque)) { + qed_aio_complete(acb, -EIO); + } +} + +/** + * Write data to the image file + */ +static void qed_aio_write_main(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + uint64_t offset = acb->cur_cluster + + qed_offset_into_cluster(s, acb->cur_pos); + BlockCompletionFunc *next_fn; + + trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size); + + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + if (acb->find_cluster_ret == QED_CLUSTER_FOUND) { + next_fn = qed_aio_next_io; + } else { + if (s->bs->backing_hd) { + next_fn = qed_aio_write_flush_before_l2_update; + } else { + next_fn = qed_aio_write_l2_update_cb; + } + } + + BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO); + bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, + &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, + next_fn, acb); +} + +/** + * Populate back untouched region of new data cluster + */ +static void qed_aio_write_postfill(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + uint64_t start = acb->cur_pos + acb->cur_qiov.size; + uint64_t len = + qed_start_of_cluster(s, start + s->header.cluster_size - 1) - start; + uint64_t offset = acb->cur_cluster + + qed_offset_into_cluster(s, acb->cur_pos) + + acb->cur_qiov.size; + + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + trace_qed_aio_write_postfill(s, acb, start, len, offset); + qed_copy_from_backing_file(s, start, len, offset, + qed_aio_write_main, acb); +} + +/** + * Populate front untouched region of new data cluster + */ +static void qed_aio_write_prefill(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + uint64_t start = qed_start_of_cluster(s, acb->cur_pos); + uint64_t len = qed_offset_into_cluster(s, acb->cur_pos); + + trace_qed_aio_write_prefill(s, acb, start, len, acb->cur_cluster); + qed_copy_from_backing_file(s, start, len, acb->cur_cluster, + qed_aio_write_postfill, acb); +} + +/** + * Check if the QED_F_NEED_CHECK bit should be set during allocating write + */ +static bool qed_should_set_need_check(BDRVQEDState *s) +{ + /* The flush before L2 update path ensures consistency */ + if (s->bs->backing_hd) { + return false; + } + + return !(s->header.features & QED_F_NEED_CHECK); +} + +static void qed_aio_write_zero_cluster(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + qed_aio_write_l2_update(acb, 0, 1); +} + +/** + * Write new data cluster + * + * @acb: Write request + * @len: Length in bytes + * + * This path is taken when writing to previously unallocated clusters. + */ +static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len) +{ + BDRVQEDState *s = acb_to_s(acb); + BlockCompletionFunc *cb; + + /* Cancel timer when the first allocating request comes in */ + if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) { + qed_cancel_need_check_timer(s); + } + + /* Freeze this request if another allocating write is in progress */ + if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs)) { + QSIMPLEQ_INSERT_TAIL(&s->allocating_write_reqs, acb, next); + } + if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs) || + s->allocating_write_reqs_plugged) { + return; /* wait for existing request to finish */ + } + + acb->cur_nclusters = qed_bytes_to_clusters(s, + qed_offset_into_cluster(s, acb->cur_pos) + len); + qemu_iovec_concat(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); + + if (acb->flags & QED_AIOCB_ZERO) { + /* Skip ahead if the clusters are already zero */ + if (acb->find_cluster_ret == QED_CLUSTER_ZERO) { + qed_aio_next_io(acb, 0); + return; + } + + cb = qed_aio_write_zero_cluster; + } else { + cb = qed_aio_write_prefill; + acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters); + } + + if (qed_should_set_need_check(s)) { + s->header.features |= QED_F_NEED_CHECK; + qed_write_header(s, cb, acb); + } else { + cb(acb, 0); + } +} + +/** + * Write data cluster in place + * + * @acb: Write request + * @offset: Cluster offset in bytes + * @len: Length in bytes + * + * This path is taken when writing to already allocated clusters. + */ +static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len) +{ + /* Allocate buffer for zero writes */ + if (acb->flags & QED_AIOCB_ZERO) { + struct iovec *iov = acb->qiov->iov; + + if (!iov->iov_base) { + iov->iov_base = qemu_try_blockalign(acb->common.bs, iov->iov_len); + if (iov->iov_base == NULL) { + qed_aio_complete(acb, -ENOMEM); + return; + } + memset(iov->iov_base, 0, iov->iov_len); + } + } + + /* Calculate the I/O vector */ + acb->cur_cluster = offset; + qemu_iovec_concat(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); + + /* Do the actual write */ + qed_aio_write_main(acb, 0); +} + +/** + * Write data cluster + * + * @opaque: Write request + * @ret: QED_CLUSTER_FOUND, QED_CLUSTER_L2, QED_CLUSTER_L1, + * or -errno + * @offset: Cluster offset in bytes + * @len: Length in bytes + * + * Callback from qed_find_cluster(). + */ +static void qed_aio_write_data(void *opaque, int ret, + uint64_t offset, size_t len) +{ + QEDAIOCB *acb = opaque; + + trace_qed_aio_write_data(acb_to_s(acb), acb, ret, offset, len); + + acb->find_cluster_ret = ret; + + switch (ret) { + case QED_CLUSTER_FOUND: + qed_aio_write_inplace(acb, offset, len); + break; + + case QED_CLUSTER_L2: + case QED_CLUSTER_L1: + case QED_CLUSTER_ZERO: + qed_aio_write_alloc(acb, len); + break; + + default: + qed_aio_complete(acb, ret); + break; + } +} + +/** + * Read data cluster + * + * @opaque: Read request + * @ret: QED_CLUSTER_FOUND, QED_CLUSTER_L2, QED_CLUSTER_L1, + * or -errno + * @offset: Cluster offset in bytes + * @len: Length in bytes + * + * Callback from qed_find_cluster(). + */ +static void qed_aio_read_data(void *opaque, int ret, + uint64_t offset, size_t len) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + BlockDriverState *bs = acb->common.bs; + + /* Adjust offset into cluster */ + offset += qed_offset_into_cluster(s, acb->cur_pos); + + trace_qed_aio_read_data(s, acb, ret, offset, len); + + if (ret < 0) { + goto err; + } + + qemu_iovec_concat(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); + + /* Handle zero cluster and backing file reads */ + if (ret == QED_CLUSTER_ZERO) { + qemu_iovec_memset(&acb->cur_qiov, 0, 0, acb->cur_qiov.size); + qed_aio_next_io(acb, 0); + return; + } else if (ret != QED_CLUSTER_FOUND) { + qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov, + &acb->backing_qiov, qed_aio_next_io, acb); + return; + } + + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, + &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, + qed_aio_next_io, acb); + return; + +err: + qed_aio_complete(acb, ret); +} + +/** + * Begin next I/O or complete the request + */ +static void qed_aio_next_io(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + QEDFindClusterFunc *io_fn = (acb->flags & QED_AIOCB_WRITE) ? + qed_aio_write_data : qed_aio_read_data; + + trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size); + + if (acb->backing_qiov) { + qemu_iovec_destroy(acb->backing_qiov); + g_free(acb->backing_qiov); + acb->backing_qiov = NULL; + } + + /* Handle I/O error */ + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + acb->qiov_offset += acb->cur_qiov.size; + acb->cur_pos += acb->cur_qiov.size; + qemu_iovec_reset(&acb->cur_qiov); + + /* Complete request */ + if (acb->cur_pos >= acb->end_pos) { + qed_aio_complete(acb, 0); + return; + } + + /* Find next cluster and start I/O */ + qed_find_cluster(s, &acb->request, + acb->cur_pos, acb->end_pos - acb->cur_pos, + io_fn, acb); +} + +static BlockAIOCB *qed_aio_setup(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, + void *opaque, int flags) +{ + QEDAIOCB *acb = qemu_aio_get(&qed_aiocb_info, bs, cb, opaque); + + trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors, + opaque, flags); + + acb->flags = flags; + acb->qiov = qiov; + acb->qiov_offset = 0; + acb->cur_pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE; + acb->end_pos = acb->cur_pos + nb_sectors * BDRV_SECTOR_SIZE; + acb->backing_qiov = NULL; + acb->request.l2_table = NULL; + qemu_iovec_init(&acb->cur_qiov, qiov->niov); + + /* Start request */ + qed_aio_next_io(acb, 0); + return &acb->common; +} + +static BlockAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); +} + +static BlockAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, + opaque, QED_AIOCB_WRITE); +} + +typedef struct { + Coroutine *co; + int ret; + bool done; +} QEDWriteZeroesCB; + +static void coroutine_fn qed_co_write_zeroes_cb(void *opaque, int ret) +{ + QEDWriteZeroesCB *cb = opaque; + + cb->done = true; + cb->ret = ret; + if (cb->co) { + qemu_coroutine_enter(cb->co, NULL); + } +} + +static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, + BdrvRequestFlags flags) +{ + BlockAIOCB *blockacb; + BDRVQEDState *s = bs->opaque; + QEDWriteZeroesCB cb = { .done = false }; + QEMUIOVector qiov; + struct iovec iov; + + /* Refuse if there are untouched backing file sectors */ + if (bs->backing_hd) { + if (qed_offset_into_cluster(s, sector_num * BDRV_SECTOR_SIZE) != 0) { + return -ENOTSUP; + } + if (qed_offset_into_cluster(s, nb_sectors * BDRV_SECTOR_SIZE) != 0) { + return -ENOTSUP; + } + } + + /* Zero writes start without an I/O buffer. If a buffer becomes necessary + * then it will be allocated during request processing. + */ + iov.iov_base = NULL, + iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE, + + qemu_iovec_init_external(&qiov, &iov, 1); + blockacb = qed_aio_setup(bs, sector_num, &qiov, nb_sectors, + qed_co_write_zeroes_cb, &cb, + QED_AIOCB_WRITE | QED_AIOCB_ZERO); + if (!blockacb) { + return -EIO; + } + if (!cb.done) { + cb.co = qemu_coroutine_self(); + qemu_coroutine_yield(); + } + assert(cb.done); + return cb.ret; +} + +static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVQEDState *s = bs->opaque; + uint64_t old_image_size; + int ret; + + if (!qed_is_image_size_valid(offset, s->header.cluster_size, + s->header.table_size)) { + return -EINVAL; + } + + /* Shrinking is currently not supported */ + if ((uint64_t)offset < s->header.image_size) { + return -ENOTSUP; + } + + old_image_size = s->header.image_size; + s->header.image_size = offset; + ret = qed_write_header_sync(s); + if (ret < 0) { + s->header.image_size = old_image_size; + } + return ret; +} + +static int64_t bdrv_qed_getlength(BlockDriverState *bs) +{ + BDRVQEDState *s = bs->opaque; + return s->header.image_size; +} + +static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVQEDState *s = bs->opaque; + + memset(bdi, 0, sizeof(*bdi)); + bdi->cluster_size = s->header.cluster_size; + bdi->is_dirty = s->header.features & QED_F_NEED_CHECK; + bdi->unallocated_blocks_are_zero = true; + bdi->can_write_zeroes_with_unmap = true; + return 0; +} + +static int bdrv_qed_change_backing_file(BlockDriverState *bs, + const char *backing_file, + const char *backing_fmt) +{ + BDRVQEDState *s = bs->opaque; + QEDHeader new_header, le_header; + void *buffer; + size_t buffer_len, backing_file_len; + int ret; + + /* Refuse to set backing filename if unknown compat feature bits are + * active. If the image uses an unknown compat feature then we may not + * know the layout of data following the header structure and cannot safely + * add a new string. + */ + if (backing_file && (s->header.compat_features & + ~QED_COMPAT_FEATURE_MASK)) { + return -ENOTSUP; + } + + memcpy(&new_header, &s->header, sizeof(new_header)); + + new_header.features &= ~(QED_F_BACKING_FILE | + QED_F_BACKING_FORMAT_NO_PROBE); + + /* Adjust feature flags */ + if (backing_file) { + new_header.features |= QED_F_BACKING_FILE; + + if (qed_fmt_is_raw(backing_fmt)) { + new_header.features |= QED_F_BACKING_FORMAT_NO_PROBE; + } + } + + /* Calculate new header size */ + backing_file_len = 0; + + if (backing_file) { + backing_file_len = strlen(backing_file); + } + + buffer_len = sizeof(new_header); + new_header.backing_filename_offset = buffer_len; + new_header.backing_filename_size = backing_file_len; + buffer_len += backing_file_len; + + /* Make sure we can rewrite header without failing */ + if (buffer_len > new_header.header_size * new_header.cluster_size) { + return -ENOSPC; + } + + /* Prepare new header */ + buffer = g_malloc(buffer_len); + + qed_header_cpu_to_le(&new_header, &le_header); + memcpy(buffer, &le_header, sizeof(le_header)); + buffer_len = sizeof(le_header); + + if (backing_file) { + memcpy(buffer + buffer_len, backing_file, backing_file_len); + buffer_len += backing_file_len; + } + + /* Write new header */ + ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len); + g_free(buffer); + if (ret == 0) { + memcpy(&s->header, &new_header, sizeof(new_header)); + } + return ret; +} + +static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) +{ + BDRVQEDState *s = bs->opaque; + Error *local_err = NULL; + int ret; + + bdrv_qed_close(bs); + + bdrv_invalidate_cache(bs->file, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + memset(s, 0, sizeof(BDRVQEDState)); + ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err); + if (local_err) { + error_setg(errp, "Could not reopen qed layer: %s", + error_get_pretty(local_err)); + error_free(local_err); + return; + } else if (ret < 0) { + error_setg_errno(errp, -ret, "Could not reopen qed layer"); + return; + } +} + +static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) +{ + BDRVQEDState *s = bs->opaque; + + return qed_check(s, result, !!fix); +} + +static QemuOptsList qed_create_opts = { + .name = "qed-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(qed_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_BACKING_FILE, + .type = QEMU_OPT_STRING, + .help = "File name of a base image" + }, + { + .name = BLOCK_OPT_BACKING_FMT, + .type = QEMU_OPT_STRING, + .help = "Image format of the base image" + }, + { + .name = BLOCK_OPT_CLUSTER_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Cluster size (in bytes)", + .def_value_str = stringify(QED_DEFAULT_CLUSTER_SIZE) + }, + { + .name = BLOCK_OPT_TABLE_SIZE, + .type = QEMU_OPT_SIZE, + .help = "L1/L2 table size (in clusters)" + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_qed = { + .format_name = "qed", + .instance_size = sizeof(BDRVQEDState), + .create_opts = &qed_create_opts, + .supports_backing = true, + + .bdrv_probe = bdrv_qed_probe, + .bdrv_rebind = bdrv_qed_rebind, + .bdrv_open = bdrv_qed_open, + .bdrv_close = bdrv_qed_close, + .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, + .bdrv_create = bdrv_qed_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_co_get_block_status = bdrv_qed_co_get_block_status, + .bdrv_aio_readv = bdrv_qed_aio_readv, + .bdrv_aio_writev = bdrv_qed_aio_writev, + .bdrv_co_write_zeroes = bdrv_qed_co_write_zeroes, + .bdrv_truncate = bdrv_qed_truncate, + .bdrv_getlength = bdrv_qed_getlength, + .bdrv_get_info = bdrv_qed_get_info, + .bdrv_refresh_limits = bdrv_qed_refresh_limits, + .bdrv_change_backing_file = bdrv_qed_change_backing_file, + .bdrv_invalidate_cache = bdrv_qed_invalidate_cache, + .bdrv_check = bdrv_qed_check, + .bdrv_detach_aio_context = bdrv_qed_detach_aio_context, + .bdrv_attach_aio_context = bdrv_qed_attach_aio_context, +}; + +static void bdrv_qed_init(void) +{ + bdrv_register(&bdrv_qed); +} + +block_init(bdrv_qed_init); diff --git a/block/qed.h b/block/qed.h new file mode 100644 index 0000000..615e676 --- /dev/null +++ b/block/qed.h @@ -0,0 +1,343 @@ +/* + * QEMU Enhanced Disk Format + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef BLOCK_QED_H +#define BLOCK_QED_H + +#include "block/block_int.h" + +/* The layout of a QED file is as follows: + * + * +--------+----------+----------+----------+-----+ + * | header | L1 table | cluster0 | cluster1 | ... | + * +--------+----------+----------+----------+-----+ + * + * There is a 2-level pagetable for cluster allocation: + * + * +----------+ + * | L1 table | + * +----------+ + * ,------' | '------. + * +----------+ | +----------+ + * | L2 table | ... | L2 table | + * +----------+ +----------+ + * ,------' | '------. + * +----------+ | +----------+ + * | Data | ... | Data | + * +----------+ +----------+ + * + * The L1 table is fixed size and always present. L2 tables are allocated on + * demand. The L1 table size determines the maximum possible image size; it + * can be influenced using the cluster_size and table_size values. + * + * All fields are little-endian on disk. + */ +#define QED_DEFAULT_CLUSTER_SIZE 65536 +enum { + QED_MAGIC = 'Q' | 'E' << 8 | 'D' << 16 | '\0' << 24, + + /* The image supports a backing file */ + QED_F_BACKING_FILE = 0x01, + + /* The image needs a consistency check before use */ + QED_F_NEED_CHECK = 0x02, + + /* The backing file format must not be probed, treat as raw image */ + QED_F_BACKING_FORMAT_NO_PROBE = 0x04, + + /* Feature bits must be used when the on-disk format changes */ + QED_FEATURE_MASK = QED_F_BACKING_FILE | /* supported feature bits */ + QED_F_NEED_CHECK | + QED_F_BACKING_FORMAT_NO_PROBE, + QED_COMPAT_FEATURE_MASK = 0, /* supported compat feature bits */ + QED_AUTOCLEAR_FEATURE_MASK = 0, /* supported autoclear feature bits */ + + /* Data is stored in groups of sectors called clusters. Cluster size must + * be large to avoid keeping too much metadata. I/O requests that have + * sub-cluster size will require read-modify-write. + */ + QED_MIN_CLUSTER_SIZE = 4 * 1024, /* in bytes */ + QED_MAX_CLUSTER_SIZE = 64 * 1024 * 1024, + + /* Allocated clusters are tracked using a 2-level pagetable. Table size is + * a multiple of clusters so large maximum image sizes can be supported + * without jacking up the cluster size too much. + */ + QED_MIN_TABLE_SIZE = 1, /* in clusters */ + QED_MAX_TABLE_SIZE = 16, + QED_DEFAULT_TABLE_SIZE = 4, + + /* Delay to flush and clean image after last allocating write completes */ + QED_NEED_CHECK_TIMEOUT = 5, /* in seconds */ +}; + +typedef struct { + uint32_t magic; /* QED\0 */ + + uint32_t cluster_size; /* in bytes */ + uint32_t table_size; /* for L1 and L2 tables, in clusters */ + uint32_t header_size; /* in clusters */ + + uint64_t features; /* format feature bits */ + uint64_t compat_features; /* compatible feature bits */ + uint64_t autoclear_features; /* self-resetting feature bits */ + + uint64_t l1_table_offset; /* in bytes */ + uint64_t image_size; /* total logical image size, in bytes */ + + /* if (features & QED_F_BACKING_FILE) */ + uint32_t backing_filename_offset; /* in bytes from start of header */ + uint32_t backing_filename_size; /* in bytes */ +} QEMU_PACKED QEDHeader; + +typedef struct { + uint64_t offsets[0]; /* in bytes */ +} QEDTable; + +/* The L2 cache is a simple write-through cache for L2 structures */ +typedef struct CachedL2Table { + QEDTable *table; + uint64_t offset; /* offset=0 indicates an invalidate entry */ + QTAILQ_ENTRY(CachedL2Table) node; + int ref; +} CachedL2Table; + +typedef struct { + QTAILQ_HEAD(, CachedL2Table) entries; + unsigned int n_entries; +} L2TableCache; + +typedef struct QEDRequest { + CachedL2Table *l2_table; +} QEDRequest; + +enum { + QED_AIOCB_WRITE = 0x0001, /* read or write? */ + QED_AIOCB_ZERO = 0x0002, /* zero write, used with QED_AIOCB_WRITE */ +}; + +typedef struct QEDAIOCB { + BlockAIOCB common; + QEMUBH *bh; + int bh_ret; /* final return status for completion bh */ + QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */ + int flags; /* QED_AIOCB_* bits ORed together */ + uint64_t end_pos; /* request end on block device, in bytes */ + + /* User scatter-gather list */ + QEMUIOVector *qiov; + size_t qiov_offset; /* byte count already processed */ + + /* Current cluster scatter-gather list */ + QEMUIOVector cur_qiov; + QEMUIOVector *backing_qiov; + uint64_t cur_pos; /* position on block device, in bytes */ + uint64_t cur_cluster; /* cluster offset in image file */ + unsigned int cur_nclusters; /* number of clusters being accessed */ + int find_cluster_ret; /* used for L1/L2 update */ + + QEDRequest request; +} QEDAIOCB; + +typedef struct { + BlockDriverState *bs; /* device */ + uint64_t file_size; /* length of image file, in bytes */ + + QEDHeader header; /* always cpu-endian */ + QEDTable *l1_table; + L2TableCache l2_cache; /* l2 table cache */ + uint32_t table_nelems; + uint32_t l1_shift; + uint32_t l2_shift; + uint32_t l2_mask; + + /* Allocating write request queue */ + QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs; + bool allocating_write_reqs_plugged; + + /* Periodic flush and clear need check flag */ + QEMUTimer *need_check_timer; +} BDRVQEDState; + +enum { + QED_CLUSTER_FOUND, /* cluster found */ + QED_CLUSTER_ZERO, /* zero cluster found */ + QED_CLUSTER_L2, /* cluster missing in L2 */ + QED_CLUSTER_L1, /* cluster missing in L1 */ +}; + +/** + * qed_find_cluster() completion callback + * + * @opaque: User data for completion callback + * @ret: QED_CLUSTER_FOUND Success + * QED_CLUSTER_L2 Data cluster unallocated in L2 + * QED_CLUSTER_L1 L2 unallocated in L1 + * -errno POSIX error occurred + * @offset: Data cluster offset + * @len: Contiguous bytes starting from cluster offset + * + * This function is invoked when qed_find_cluster() completes. + * + * On success ret is QED_CLUSTER_FOUND and offset/len are a contiguous range + * in the image file. + * + * On failure ret is QED_CLUSTER_L2 or QED_CLUSTER_L1 for missing L2 or L1 + * table offset, respectively. len is number of contiguous unallocated bytes. + */ +typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len); + +/** + * Generic callback for chaining async callbacks + */ +typedef struct { + BlockCompletionFunc *cb; + void *opaque; +} GenericCB; + +void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque); +void gencb_complete(void *opaque, int ret); + +/** + * Header functions + */ +int qed_write_header_sync(BDRVQEDState *s); + +/** + * L2 cache functions + */ +void qed_init_l2_cache(L2TableCache *l2_cache); +void qed_free_l2_cache(L2TableCache *l2_cache); +CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache); +void qed_unref_l2_cache_entry(CachedL2Table *entry); +CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset); +void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table); + +/** + * Table I/O functions + */ +int qed_read_l1_table_sync(BDRVQEDState *s); +void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, + BlockCompletionFunc *cb, void *opaque); +int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, + unsigned int n); +int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, + uint64_t offset); +void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, + BlockCompletionFunc *cb, void *opaque); +void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush, + BlockCompletionFunc *cb, void *opaque); +int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush); + +/** + * Cluster functions + */ +void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, + size_t len, QEDFindClusterFunc *cb, void *opaque); + +/** + * Consistency check + */ +int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix); + +QEDTable *qed_alloc_table(BDRVQEDState *s); + +/** + * Round down to the start of a cluster + */ +static inline uint64_t qed_start_of_cluster(BDRVQEDState *s, uint64_t offset) +{ + return offset & ~(uint64_t)(s->header.cluster_size - 1); +} + +static inline uint64_t qed_offset_into_cluster(BDRVQEDState *s, uint64_t offset) +{ + return offset & (s->header.cluster_size - 1); +} + +static inline uint64_t qed_bytes_to_clusters(BDRVQEDState *s, uint64_t bytes) +{ + return qed_start_of_cluster(s, bytes + (s->header.cluster_size - 1)) / + (s->header.cluster_size - 1); +} + +static inline unsigned int qed_l1_index(BDRVQEDState *s, uint64_t pos) +{ + return pos >> s->l1_shift; +} + +static inline unsigned int qed_l2_index(BDRVQEDState *s, uint64_t pos) +{ + return (pos >> s->l2_shift) & s->l2_mask; +} + +/** + * Test if a cluster offset is valid + */ +static inline bool qed_check_cluster_offset(BDRVQEDState *s, uint64_t offset) +{ + uint64_t header_size = (uint64_t)s->header.header_size * + s->header.cluster_size; + + if (offset & (s->header.cluster_size - 1)) { + return false; + } + return offset >= header_size && offset < s->file_size; +} + +/** + * Test if a table offset is valid + */ +static inline bool qed_check_table_offset(BDRVQEDState *s, uint64_t offset) +{ + uint64_t end_offset = offset + (s->header.table_size - 1) * + s->header.cluster_size; + + /* Overflow check */ + if (end_offset <= offset) { + return false; + } + + return qed_check_cluster_offset(s, offset) && + qed_check_cluster_offset(s, end_offset); +} + +static inline bool qed_offset_is_cluster_aligned(BDRVQEDState *s, + uint64_t offset) +{ + if (qed_offset_into_cluster(s, offset)) { + return false; + } + return true; +} + +static inline bool qed_offset_is_unalloc_cluster(uint64_t offset) +{ + if (offset == 0) { + return true; + } + return false; +} + +static inline bool qed_offset_is_zero_cluster(uint64_t offset) +{ + if (offset == 1) { + return true; + } + return false; +} + +#endif /* BLOCK_QED_H */ diff --git a/block/quorum.c b/block/quorum.c new file mode 100644 index 0000000..f91ef75 --- /dev/null +++ b/block/quorum.c @@ -0,0 +1,1094 @@ +/* + * Quorum Block filter + * + * Copyright (C) 2012-2014 Nodalink, EURL. + * + * Author: + * Benoît Canet + * + * Based on the design and code of blkverify.c (Copyright (C) 2010 IBM, Corp) + * and blkmirror.c (Copyright (C) 2011 Red Hat, Inc). + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include "block/block_int.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qstring.h" +#include "qapi-event.h" + +#define HASH_LENGTH 32 + +#define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold" +#define QUORUM_OPT_BLKVERIFY "blkverify" +#define QUORUM_OPT_REWRITE "rewrite-corrupted" +#define QUORUM_OPT_READ_PATTERN "read-pattern" + +/* This union holds a vote hash value */ +typedef union QuorumVoteValue { + char h[HASH_LENGTH]; /* SHA-256 hash */ + int64_t l; /* simpler 64 bits hash */ +} QuorumVoteValue; + +/* A vote item */ +typedef struct QuorumVoteItem { + int index; + QLIST_ENTRY(QuorumVoteItem) next; +} QuorumVoteItem; + +/* this structure is a vote version. A version is the set of votes sharing the + * same vote value. + * The set of votes will be tracked with the items field and its cardinality is + * vote_count. + */ +typedef struct QuorumVoteVersion { + QuorumVoteValue value; + int index; + int vote_count; + QLIST_HEAD(, QuorumVoteItem) items; + QLIST_ENTRY(QuorumVoteVersion) next; +} QuorumVoteVersion; + +/* this structure holds a group of vote versions together */ +typedef struct QuorumVotes { + QLIST_HEAD(, QuorumVoteVersion) vote_list; + bool (*compare)(QuorumVoteValue *a, QuorumVoteValue *b); +} QuorumVotes; + +/* the following structure holds the state of one quorum instance */ +typedef struct BDRVQuorumState { + BlockDriverState **bs; /* children BlockDriverStates */ + int num_children; /* children count */ + int threshold; /* if less than threshold children reads gave the + * same result a quorum error occurs. + */ + bool is_blkverify; /* true if the driver is in blkverify mode + * Writes are mirrored on two children devices. + * On reads the two children devices' contents are + * compared and if a difference is spotted its + * location is printed and the code aborts. + * It is useful to debug other block drivers by + * comparing them with a reference one. + */ + bool rewrite_corrupted;/* true if the driver must rewrite-on-read corrupted + * block if Quorum is reached. + */ + + QuorumReadPattern read_pattern; +} BDRVQuorumState; + +typedef struct QuorumAIOCB QuorumAIOCB; + +/* Quorum will create one instance of the following structure per operation it + * performs on its children. + * So for each read/write operation coming from the upper layer there will be + * $children_count QuorumChildRequest. + */ +typedef struct QuorumChildRequest { + BlockAIOCB *aiocb; + QEMUIOVector qiov; + uint8_t *buf; + int ret; + QuorumAIOCB *parent; +} QuorumChildRequest; + +/* Quorum will use the following structure to track progress of each read/write + * operation received by the upper layer. + * This structure hold pointers to the QuorumChildRequest structures instances + * used to do operations on each children and track overall progress. + */ +struct QuorumAIOCB { + BlockAIOCB common; + + /* Request metadata */ + uint64_t sector_num; + int nb_sectors; + + QEMUIOVector *qiov; /* calling IOV */ + + QuorumChildRequest *qcrs; /* individual child requests */ + int count; /* number of completed AIOCB */ + int success_count; /* number of successfully completed AIOCB */ + + int rewrite_count; /* number of replica to rewrite: count down to + * zero once writes are fired + */ + + QuorumVotes votes; + + bool is_read; + int vote_ret; + int child_iter; /* which child to read in fifo pattern */ +}; + +static bool quorum_vote(QuorumAIOCB *acb); + +static void quorum_aio_cancel(BlockAIOCB *blockacb) +{ + QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common); + BDRVQuorumState *s = acb->common.bs->opaque; + int i; + + /* cancel all callbacks */ + for (i = 0; i < s->num_children; i++) { + if (acb->qcrs[i].aiocb) { + bdrv_aio_cancel_async(acb->qcrs[i].aiocb); + } + } +} + +static AIOCBInfo quorum_aiocb_info = { + .aiocb_size = sizeof(QuorumAIOCB), + .cancel_async = quorum_aio_cancel, +}; + +static void quorum_aio_finalize(QuorumAIOCB *acb) +{ + int i, ret = 0; + + if (acb->vote_ret) { + ret = acb->vote_ret; + } + + acb->common.cb(acb->common.opaque, ret); + + if (acb->is_read) { + /* on the quorum case acb->child_iter == s->num_children - 1 */ + for (i = 0; i <= acb->child_iter; i++) { + qemu_vfree(acb->qcrs[i].buf); + qemu_iovec_destroy(&acb->qcrs[i].qiov); + } + } + + g_free(acb->qcrs); + qemu_aio_unref(acb); +} + +static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b) +{ + return !memcmp(a->h, b->h, HASH_LENGTH); +} + +static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b) +{ + return a->l == b->l; +} + +static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s, + BlockDriverState *bs, + QEMUIOVector *qiov, + uint64_t sector_num, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque); + int i; + + acb->common.bs->opaque = s; + acb->sector_num = sector_num; + acb->nb_sectors = nb_sectors; + acb->qiov = qiov; + acb->qcrs = g_new0(QuorumChildRequest, s->num_children); + acb->count = 0; + acb->success_count = 0; + acb->rewrite_count = 0; + acb->votes.compare = quorum_sha256_compare; + QLIST_INIT(&acb->votes.vote_list); + acb->is_read = false; + acb->vote_ret = 0; + + for (i = 0; i < s->num_children; i++) { + acb->qcrs[i].buf = NULL; + acb->qcrs[i].ret = 0; + acb->qcrs[i].parent = acb; + } + + return acb; +} + +static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret) +{ + const char *msg = NULL; + if (ret < 0) { + msg = strerror(-ret); + } + qapi_event_send_quorum_report_bad(!!msg, msg, node_name, + acb->sector_num, acb->nb_sectors, &error_abort); +} + +static void quorum_report_failure(QuorumAIOCB *acb) +{ + const char *reference = bdrv_get_device_or_node_name(acb->common.bs); + qapi_event_send_quorum_failure(reference, acb->sector_num, + acb->nb_sectors, &error_abort); +} + +static int quorum_vote_error(QuorumAIOCB *acb); + +static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + + if (acb->success_count < s->threshold) { + acb->vote_ret = quorum_vote_error(acb); + quorum_report_failure(acb); + return true; + } + + return false; +} + +static void quorum_rewrite_aio_cb(void *opaque, int ret) +{ + QuorumAIOCB *acb = opaque; + + /* one less rewrite to do */ + acb->rewrite_count--; + + /* wait until all rewrite callbacks have completed */ + if (acb->rewrite_count) { + return; + } + + quorum_aio_finalize(acb); +} + +static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb); + +static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source) +{ + int i; + assert(dest->niov == source->niov); + assert(dest->size == source->size); + for (i = 0; i < source->niov; i++) { + assert(dest->iov[i].iov_len == source->iov[i].iov_len); + memcpy(dest->iov[i].iov_base, + source->iov[i].iov_base, + source->iov[i].iov_len); + } +} + +static void quorum_aio_cb(void *opaque, int ret) +{ + QuorumChildRequest *sacb = opaque; + QuorumAIOCB *acb = sacb->parent; + BDRVQuorumState *s = acb->common.bs->opaque; + bool rewrite = false; + + if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) { + /* We try to read next child in FIFO order if we fail to read */ + if (ret < 0 && ++acb->child_iter < s->num_children) { + read_fifo_child(acb); + return; + } + + if (ret == 0) { + quorum_copy_qiov(acb->qiov, &acb->qcrs[acb->child_iter].qiov); + } + acb->vote_ret = ret; + quorum_aio_finalize(acb); + return; + } + + sacb->ret = ret; + acb->count++; + if (ret == 0) { + acb->success_count++; + } else { + quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret); + } + assert(acb->count <= s->num_children); + assert(acb->success_count <= s->num_children); + if (acb->count < s->num_children) { + return; + } + + /* Do the vote on read */ + if (acb->is_read) { + rewrite = quorum_vote(acb); + } else { + quorum_has_too_much_io_failed(acb); + } + + /* if no rewrite is done the code will finish right away */ + if (!rewrite) { + quorum_aio_finalize(acb); + } +} + +static void quorum_report_bad_versions(BDRVQuorumState *s, + QuorumAIOCB *acb, + QuorumVoteValue *value) +{ + QuorumVoteVersion *version; + QuorumVoteItem *item; + + QLIST_FOREACH(version, &acb->votes.vote_list, next) { + if (acb->votes.compare(&version->value, value)) { + continue; + } + QLIST_FOREACH(item, &version->items, next) { + quorum_report_bad(acb, s->bs[item->index]->node_name, 0); + } + } +} + +static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb, + QuorumVoteValue *value) +{ + QuorumVoteVersion *version; + QuorumVoteItem *item; + int count = 0; + + /* first count the number of bad versions: done first to avoid concurrency + * issues. + */ + QLIST_FOREACH(version, &acb->votes.vote_list, next) { + if (acb->votes.compare(&version->value, value)) { + continue; + } + QLIST_FOREACH(item, &version->items, next) { + count++; + } + } + + /* quorum_rewrite_aio_cb will count down this to zero */ + acb->rewrite_count = count; + + /* now fire the correcting rewrites */ + QLIST_FOREACH(version, &acb->votes.vote_list, next) { + if (acb->votes.compare(&version->value, value)) { + continue; + } + QLIST_FOREACH(item, &version->items, next) { + bdrv_aio_writev(s->bs[item->index], acb->sector_num, acb->qiov, + acb->nb_sectors, quorum_rewrite_aio_cb, acb); + } + } + + /* return true if any rewrite is done else false */ + return count; +} + +static void quorum_count_vote(QuorumVotes *votes, + QuorumVoteValue *value, + int index) +{ + QuorumVoteVersion *v = NULL, *version = NULL; + QuorumVoteItem *item; + + /* look if we have something with this hash */ + QLIST_FOREACH(v, &votes->vote_list, next) { + if (votes->compare(&v->value, value)) { + version = v; + break; + } + } + + /* It's a version not yet in the list add it */ + if (!version) { + version = g_new0(QuorumVoteVersion, 1); + QLIST_INIT(&version->items); + memcpy(&version->value, value, sizeof(version->value)); + version->index = index; + version->vote_count = 0; + QLIST_INSERT_HEAD(&votes->vote_list, version, next); + } + + version->vote_count++; + + item = g_new0(QuorumVoteItem, 1); + item->index = index; + QLIST_INSERT_HEAD(&version->items, item, next); +} + +static void quorum_free_vote_list(QuorumVotes *votes) +{ + QuorumVoteVersion *version, *next_version; + QuorumVoteItem *item, *next_item; + + QLIST_FOREACH_SAFE(version, &votes->vote_list, next, next_version) { + QLIST_REMOVE(version, next); + QLIST_FOREACH_SAFE(item, &version->items, next, next_item) { + QLIST_REMOVE(item, next); + g_free(item); + } + g_free(version); + } +} + +static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash) +{ + int j, ret; + gnutls_hash_hd_t dig; + QEMUIOVector *qiov = &acb->qcrs[i].qiov; + + ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256); + + if (ret < 0) { + return ret; + } + + for (j = 0; j < qiov->niov; j++) { + ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len); + if (ret < 0) { + break; + } + } + + gnutls_hash_deinit(dig, (void *) hash); + return ret; +} + +static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes) +{ + int max = 0; + QuorumVoteVersion *candidate, *winner = NULL; + + QLIST_FOREACH(candidate, &votes->vote_list, next) { + if (candidate->vote_count > max) { + max = candidate->vote_count; + winner = candidate; + } + } + + return winner; +} + +/* qemu_iovec_compare is handy for blkverify mode because it returns the first + * differing byte location. Yet it is handcoded to compare vectors one byte + * after another so it does not benefit from the libc SIMD optimizations. + * quorum_iovec_compare is written for speed and should be used in the non + * blkverify mode of quorum. + */ +static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) +{ + int i; + int result; + + assert(a->niov == b->niov); + for (i = 0; i < a->niov; i++) { + assert(a->iov[i].iov_len == b->iov[i].iov_len); + result = memcmp(a->iov[i].iov_base, + b->iov[i].iov_base, + a->iov[i].iov_len); + if (result) { + return false; + } + } + + return true; +} + +static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "quorum: sector_num=%" PRId64 " nb_sectors=%d ", + acb->sector_num, acb->nb_sectors); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +static bool quorum_compare(QuorumAIOCB *acb, + QEMUIOVector *a, + QEMUIOVector *b) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + ssize_t offset; + + /* This driver will replace blkverify in this particular case */ + if (s->is_blkverify) { + offset = qemu_iovec_compare(a, b); + if (offset != -1) { + quorum_err(acb, "contents mismatch in sector %" PRId64, + acb->sector_num + + (uint64_t)(offset / BDRV_SECTOR_SIZE)); + } + return true; + } + + return quorum_iovec_compare(a, b); +} + +/* Do a vote to get the error code */ +static int quorum_vote_error(QuorumAIOCB *acb) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + QuorumVoteVersion *winner = NULL; + QuorumVotes error_votes; + QuorumVoteValue result_value; + int i, ret = 0; + bool error = false; + + QLIST_INIT(&error_votes.vote_list); + error_votes.compare = quorum_64bits_compare; + + for (i = 0; i < s->num_children; i++) { + ret = acb->qcrs[i].ret; + if (ret) { + error = true; + result_value.l = ret; + quorum_count_vote(&error_votes, &result_value, i); + } + } + + if (error) { + winner = quorum_get_vote_winner(&error_votes); + ret = winner->value.l; + } + + quorum_free_vote_list(&error_votes); + + return ret; +} + +static bool quorum_vote(QuorumAIOCB *acb) +{ + bool quorum = true; + bool rewrite = false; + int i, j, ret; + QuorumVoteValue hash; + BDRVQuorumState *s = acb->common.bs->opaque; + QuorumVoteVersion *winner; + + if (quorum_has_too_much_io_failed(acb)) { + return false; + } + + /* get the index of the first successful read */ + for (i = 0; i < s->num_children; i++) { + if (!acb->qcrs[i].ret) { + break; + } + } + + assert(i < s->num_children); + + /* compare this read with all other successful reads stopping at quorum + * failure + */ + for (j = i + 1; j < s->num_children; j++) { + if (acb->qcrs[j].ret) { + continue; + } + quorum = quorum_compare(acb, &acb->qcrs[i].qiov, &acb->qcrs[j].qiov); + if (!quorum) { + break; + } + } + + /* Every successful read agrees */ + if (quorum) { + quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov); + return false; + } + + /* compute hashes for each successful read, also store indexes */ + for (i = 0; i < s->num_children; i++) { + if (acb->qcrs[i].ret) { + continue; + } + ret = quorum_compute_hash(acb, i, &hash); + /* if ever the hash computation failed */ + if (ret < 0) { + acb->vote_ret = ret; + goto free_exit; + } + quorum_count_vote(&acb->votes, &hash, i); + } + + /* vote to select the most represented version */ + winner = quorum_get_vote_winner(&acb->votes); + + /* if the winner count is smaller than threshold the read fails */ + if (winner->vote_count < s->threshold) { + quorum_report_failure(acb); + acb->vote_ret = -EIO; + goto free_exit; + } + + /* we have a winner: copy it */ + quorum_copy_qiov(acb->qiov, &acb->qcrs[winner->index].qiov); + + /* some versions are bad print them */ + quorum_report_bad_versions(s, acb, &winner->value); + + /* corruption correction is enabled */ + if (s->rewrite_corrupted) { + rewrite = quorum_rewrite_bad_versions(s, acb, &winner->value); + } + +free_exit: + /* free lists */ + quorum_free_vote_list(&acb->votes); + return rewrite; +} + +static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + int i; + + for (i = 0; i < s->num_children; i++) { + acb->qcrs[i].buf = qemu_blockalign(s->bs[i], acb->qiov->size); + qemu_iovec_init(&acb->qcrs[i].qiov, acb->qiov->niov); + qemu_iovec_clone(&acb->qcrs[i].qiov, acb->qiov, acb->qcrs[i].buf); + } + + for (i = 0; i < s->num_children; i++) { + bdrv_aio_readv(s->bs[i], acb->sector_num, &acb->qcrs[i].qiov, + acb->nb_sectors, quorum_aio_cb, &acb->qcrs[i]); + } + + return &acb->common; +} + +static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + + acb->qcrs[acb->child_iter].buf = qemu_blockalign(s->bs[acb->child_iter], + acb->qiov->size); + qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov); + qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov, + acb->qcrs[acb->child_iter].buf); + bdrv_aio_readv(s->bs[acb->child_iter], acb->sector_num, + &acb->qcrs[acb->child_iter].qiov, acb->nb_sectors, + quorum_aio_cb, &acb->qcrs[acb->child_iter]); + + return &acb->common; +} + +static BlockAIOCB *quorum_aio_readv(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + BDRVQuorumState *s = bs->opaque; + QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, + nb_sectors, cb, opaque); + acb->is_read = true; + + if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { + acb->child_iter = s->num_children - 1; + return read_quorum_children(acb); + } + + acb->child_iter = 0; + return read_fifo_child(acb); +} + +static BlockAIOCB *quorum_aio_writev(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + BDRVQuorumState *s = bs->opaque; + QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors, + cb, opaque); + int i; + + for (i = 0; i < s->num_children; i++) { + acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov, + nb_sectors, &quorum_aio_cb, + &acb->qcrs[i]); + } + + return &acb->common; +} + +static int64_t quorum_getlength(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + int64_t result; + int i; + + /* check that all file have the same length */ + result = bdrv_getlength(s->bs[0]); + if (result < 0) { + return result; + } + for (i = 1; i < s->num_children; i++) { + int64_t value = bdrv_getlength(s->bs[i]); + if (value < 0) { + return value; + } + if (value != result) { + return -EIO; + } + } + + return result; +} + +static void quorum_invalidate_cache(BlockDriverState *bs, Error **errp) +{ + BDRVQuorumState *s = bs->opaque; + Error *local_err = NULL; + int i; + + for (i = 0; i < s->num_children; i++) { + bdrv_invalidate_cache(s->bs[i], &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } +} + +static coroutine_fn int quorum_co_flush(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + QuorumVoteVersion *winner = NULL; + QuorumVotes error_votes; + QuorumVoteValue result_value; + int i; + int result = 0; + + QLIST_INIT(&error_votes.vote_list); + error_votes.compare = quorum_64bits_compare; + + for (i = 0; i < s->num_children; i++) { + result = bdrv_co_flush(s->bs[i]); + result_value.l = result; + quorum_count_vote(&error_votes, &result_value, i); + } + + winner = quorum_get_vote_winner(&error_votes); + result = winner->value.l; + + quorum_free_vote_list(&error_votes); + + return result; +} + +static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs, + BlockDriverState *candidate) +{ + BDRVQuorumState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_children; i++) { + bool perm = bdrv_recurse_is_first_non_filter(s->bs[i], + candidate); + if (perm) { + return true; + } + } + + return false; +} + +static int quorum_valid_threshold(int threshold, int num_children, Error **errp) +{ + + if (threshold < 1) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + "vote-threshold", "value >= 1"); + return -ERANGE; + } + + if (threshold > num_children) { + error_setg(errp, "threshold may not exceed children count"); + return -ERANGE; + } + + return 0; +} + +static QemuOptsList quorum_runtime_opts = { + .name = "quorum", + .head = QTAILQ_HEAD_INITIALIZER(quorum_runtime_opts.head), + .desc = { + { + .name = QUORUM_OPT_VOTE_THRESHOLD, + .type = QEMU_OPT_NUMBER, + .help = "The number of vote needed for reaching quorum", + }, + { + .name = QUORUM_OPT_BLKVERIFY, + .type = QEMU_OPT_BOOL, + .help = "Trigger block verify mode if set", + }, + { + .name = QUORUM_OPT_REWRITE, + .type = QEMU_OPT_BOOL, + .help = "Rewrite corrupted block on read quorum", + }, + { + .name = QUORUM_OPT_READ_PATTERN, + .type = QEMU_OPT_STRING, + .help = "Allowed pattern: quorum, fifo. Quorum is default", + }, + { /* end of list */ } + }, +}; + +static int parse_read_pattern(const char *opt) +{ + int i; + + if (!opt) { + /* Set quorum as default */ + return QUORUM_READ_PATTERN_QUORUM; + } + + for (i = 0; i < QUORUM_READ_PATTERN_MAX; i++) { + if (!strcmp(opt, QuorumReadPattern_lookup[i])) { + return i; + } + } + + return -EINVAL; +} + +static int quorum_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVQuorumState *s = bs->opaque; + Error *local_err = NULL; + QemuOpts *opts = NULL; + bool *opened; + QDict *sub = NULL; + QList *list = NULL; + const QListEntry *lentry; + int i; + int ret = 0; + + qdict_flatten(options); + qdict_extract_subqdict(options, &sub, "children."); + qdict_array_split(sub, &list); + + if (qdict_size(sub)) { + error_setg(&local_err, "Invalid option children.%s", + qdict_first(sub)->key); + ret = -EINVAL; + goto exit; + } + + /* count how many different children are present */ + s->num_children = qlist_size(list); + if (s->num_children < 2) { + error_setg(&local_err, + "Number of provided children must be greater than 1"); + ret = -EINVAL; + goto exit; + } + + opts = qemu_opts_create(&quorum_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + ret = -EINVAL; + goto exit; + } + + s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0); + ret = parse_read_pattern(qemu_opt_get(opts, QUORUM_OPT_READ_PATTERN)); + if (ret < 0) { + error_setg(&local_err, "Please set read-pattern as fifo or quorum"); + goto exit; + } + s->read_pattern = ret; + + if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { + /* and validate it against s->num_children */ + ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err); + if (ret < 0) { + goto exit; + } + + /* is the driver in blkverify mode */ + if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) && + s->num_children == 2 && s->threshold == 2) { + s->is_blkverify = true; + } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) { + fprintf(stderr, "blkverify mode is set by setting blkverify=on " + "and using two files with vote_threshold=2\n"); + } + + s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE, + false); + if (s->rewrite_corrupted && s->is_blkverify) { + error_setg(&local_err, + "rewrite-corrupted=on cannot be used with blkverify=on"); + ret = -EINVAL; + goto exit; + } + } + + /* allocate the children BlockDriverState array */ + s->bs = g_new0(BlockDriverState *, s->num_children); + opened = g_new0(bool, s->num_children); + + for (i = 0, lentry = qlist_first(list); lentry; + lentry = qlist_next(lentry), i++) { + QDict *d; + QString *string; + + switch (qobject_type(lentry->value)) + { + /* List of options */ + case QTYPE_QDICT: + d = qobject_to_qdict(lentry->value); + QINCREF(d); + ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL, + &local_err); + break; + + /* QMP reference */ + case QTYPE_QSTRING: + string = qobject_to_qstring(lentry->value); + ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL, + flags, NULL, &local_err); + break; + + default: + error_setg(&local_err, "Specification of child block device %i " + "is invalid", i); + ret = -EINVAL; + } + + if (ret < 0) { + goto close_exit; + } + opened[i] = true; + } + + g_free(opened); + goto exit; + +close_exit: + /* cleanup on error */ + for (i = 0; i < s->num_children; i++) { + if (!opened[i]) { + continue; + } + bdrv_unref(s->bs[i]); + } + g_free(s->bs); + g_free(opened); +exit: + qemu_opts_del(opts); + /* propagate error */ + if (local_err) { + error_propagate(errp, local_err); + } + QDECREF(list); + QDECREF(sub); + return ret; +} + +static void quorum_close(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_children; i++) { + bdrv_unref(s->bs[i]); + } + + g_free(s->bs); +} + +static void quorum_detach_aio_context(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_children; i++) { + bdrv_detach_aio_context(s->bs[i]); + } +} + +static void quorum_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVQuorumState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_children; i++) { + bdrv_attach_aio_context(s->bs[i], new_context); + } +} + +static void quorum_refresh_filename(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + QDict *opts; + QList *children; + int i; + + for (i = 0; i < s->num_children; i++) { + bdrv_refresh_filename(s->bs[i]); + if (!s->bs[i]->full_open_options) { + return; + } + } + + children = qlist_new(); + for (i = 0; i < s->num_children; i++) { + QINCREF(s->bs[i]->full_open_options); + qlist_append_obj(children, QOBJECT(s->bs[i]->full_open_options)); + } + + opts = qdict_new(); + qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("quorum"))); + qdict_put_obj(opts, QUORUM_OPT_VOTE_THRESHOLD, + QOBJECT(qint_from_int(s->threshold))); + qdict_put_obj(opts, QUORUM_OPT_BLKVERIFY, + QOBJECT(qbool_from_int(s->is_blkverify))); + qdict_put_obj(opts, QUORUM_OPT_REWRITE, + QOBJECT(qbool_from_int(s->rewrite_corrupted))); + qdict_put_obj(opts, "children", QOBJECT(children)); + + bs->full_open_options = opts; +} + +static BlockDriver bdrv_quorum = { + .format_name = "quorum", + .protocol_name = "quorum", + + .instance_size = sizeof(BDRVQuorumState), + + .bdrv_file_open = quorum_open, + .bdrv_close = quorum_close, + .bdrv_refresh_filename = quorum_refresh_filename, + + .bdrv_co_flush_to_disk = quorum_co_flush, + + .bdrv_getlength = quorum_getlength, + + .bdrv_aio_readv = quorum_aio_readv, + .bdrv_aio_writev = quorum_aio_writev, + .bdrv_invalidate_cache = quorum_invalidate_cache, + + .bdrv_detach_aio_context = quorum_detach_aio_context, + .bdrv_attach_aio_context = quorum_attach_aio_context, + + .is_filter = true, + .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter, +}; + +static void bdrv_quorum_init(void) +{ + bdrv_register(&bdrv_quorum); +} + +block_init(bdrv_quorum_init); diff --git a/block/raw-aio.h b/block/raw-aio.h new file mode 100644 index 0000000..31d791f --- /dev/null +++ b/block/raw-aio.h @@ -0,0 +1,62 @@ +/* + * Declarations for AIO in the raw protocol + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ +#ifndef QEMU_RAW_AIO_H +#define QEMU_RAW_AIO_H + +/* AIO request types */ +#define QEMU_AIO_READ 0x0001 +#define QEMU_AIO_WRITE 0x0002 +#define QEMU_AIO_IOCTL 0x0004 +#define QEMU_AIO_FLUSH 0x0008 +#define QEMU_AIO_DISCARD 0x0010 +#define QEMU_AIO_WRITE_ZEROES 0x0020 +#define QEMU_AIO_TYPE_MASK \ + (QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \ + QEMU_AIO_DISCARD|QEMU_AIO_WRITE_ZEROES) + +/* AIO flags */ +#define QEMU_AIO_MISALIGNED 0x1000 +#define QEMU_AIO_BLKDEV 0x2000 + + +/* linux-aio.c - Linux native implementation */ +#ifdef CONFIG_LINUX_AIO +void *laio_init(void); +void laio_cleanup(void *s); +BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, int type); +void laio_detach_aio_context(void *s, AioContext *old_context); +void laio_attach_aio_context(void *s, AioContext *new_context); +void laio_io_plug(BlockDriverState *bs, void *aio_ctx); +void laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug); +#endif + +#ifdef _WIN32 +typedef struct QEMUWin32AIOState QEMUWin32AIOState; +QEMUWin32AIOState *win32_aio_init(void); +void win32_aio_cleanup(QEMUWin32AIOState *aio); +int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile); +BlockAIOCB *win32_aio_submit(BlockDriverState *bs, + QEMUWin32AIOState *aio, HANDLE hfile, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, int type); +void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, + AioContext *old_context); +void win32_aio_attach_aio_context(QEMUWin32AIOState *aio, + AioContext *new_context); +#endif + +#endif /* QEMU_RAW_AIO_H */ diff --git a/block/raw-posix.c b/block/raw-posix.c new file mode 100644 index 0000000..2990e95 --- /dev/null +++ b/block/raw-posix.c @@ -0,0 +1,2802 @@ +/* + * Block driver for RAW files (posix) + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "qemu/timer.h" +#include "qemu/log.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "trace.h" +#include "block/thread-pool.h" +#include "qemu/iov.h" +#include "raw-aio.h" +#include "qapi/util.h" + +#if defined(__APPLE__) && (__MACH__) +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#endif + +#ifdef __sun__ +#define _POSIX_PTHREAD_SEMANTICS 1 +#include +#endif +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __s390__ +#include +#endif +#ifndef FS_NOCOW_FL +#define FS_NOCOW_FL 0x00800000 /* Do not cow file */ +#endif +#endif +#if defined(CONFIG_FALLOCATE_PUNCH_HOLE) || defined(CONFIG_FALLOCATE_ZERO_RANGE) +#include +#endif +#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#include +#endif + +#ifdef __OpenBSD__ +#include +#include +#include +#endif + +#ifdef __NetBSD__ +#include +#include +#include +#include +#endif + +#ifdef __DragonFly__ +#include +#include +#endif + +#ifdef CONFIG_XFS +#include +#endif + +//#define DEBUG_FLOPPY + +//#define DEBUG_BLOCK +#if defined(DEBUG_BLOCK) +#define DEBUG_BLOCK_PRINT(formatCstr, ...) do { if (qemu_log_enabled()) \ + { qemu_log(formatCstr, ## __VA_ARGS__); qemu_log_flush(); } } while (0) +#else +#define DEBUG_BLOCK_PRINT(formatCstr, ...) +#endif + +/* OS X does not have O_DSYNC */ +#ifndef O_DSYNC +#ifdef O_SYNC +#define O_DSYNC O_SYNC +#elif defined(O_FSYNC) +#define O_DSYNC O_FSYNC +#endif +#endif + +/* Approximate O_DIRECT with O_DSYNC if O_DIRECT isn't available */ +#ifndef O_DIRECT +#define O_DIRECT O_DSYNC +#endif + +#define FTYPE_FILE 0 +#define FTYPE_CD 1 +#define FTYPE_FD 2 + +/* if the FD is not accessed during that time (in ns), we try to + reopen it to see if the disk has been changed */ +#define FD_OPEN_TIMEOUT (1000000000) + +#define MAX_BLOCKSIZE 4096 + +typedef struct BDRVRawState { + int fd; + int type; + int open_flags; + size_t buf_align; + +#if defined(__linux__) + /* linux floppy specific */ + int64_t fd_open_time; + int64_t fd_error_time; + int fd_got_error; + int fd_media_changed; +#endif +#ifdef CONFIG_LINUX_AIO + int use_aio; + void *aio_ctx; +#endif +#ifdef CONFIG_XFS + bool is_xfs:1; +#endif + bool has_discard:1; + bool has_write_zeroes:1; + bool discard_zeroes:1; + bool has_fallocate; + bool needs_alignment; +} BDRVRawState; + +typedef struct BDRVRawReopenState { + int fd; + int open_flags; +#ifdef CONFIG_LINUX_AIO + int use_aio; +#endif +} BDRVRawReopenState; + +static int fd_open(BlockDriverState *bs); +static int64_t raw_getlength(BlockDriverState *bs); + +typedef struct RawPosixAIOData { + BlockDriverState *bs; + int aio_fildes; + union { + struct iovec *aio_iov; + void *aio_ioctl_buf; + }; + int aio_niov; + uint64_t aio_nbytes; +#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */ + off_t aio_offset; + int aio_type; +} RawPosixAIOData; + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +static int cdrom_reopen(BlockDriverState *bs); +#endif + +#if defined(__NetBSD__) +static int raw_normalize_devicepath(const char **filename) +{ + static char namebuf[PATH_MAX]; + const char *dp, *fname; + struct stat sb; + + fname = *filename; + dp = strrchr(fname, '/'); + if (lstat(fname, &sb) < 0) { + fprintf(stderr, "%s: stat failed: %s\n", + fname, strerror(errno)); + return -errno; + } + + if (!S_ISBLK(sb.st_mode)) { + return 0; + } + + if (dp == NULL) { + snprintf(namebuf, PATH_MAX, "r%s", fname); + } else { + snprintf(namebuf, PATH_MAX, "%.*s/r%s", + (int)(dp - fname), fname, dp + 1); + } + fprintf(stderr, "%s is a block device", fname); + *filename = namebuf; + fprintf(stderr, ", using %s\n", *filename); + + return 0; +} +#else +static int raw_normalize_devicepath(const char **filename) +{ + return 0; +} +#endif + +/* + * Get logical block size via ioctl. On success store it in @sector_size_p. + */ +static int probe_logical_blocksize(int fd, unsigned int *sector_size_p) +{ + unsigned int sector_size; + bool success = false; + + errno = ENOTSUP; + + /* Try a few ioctls to get the right size */ +#ifdef BLKSSZGET + if (ioctl(fd, BLKSSZGET, §or_size) >= 0) { + *sector_size_p = sector_size; + success = true; + } +#endif +#ifdef DKIOCGETBLOCKSIZE + if (ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) { + *sector_size_p = sector_size; + success = true; + } +#endif +#ifdef DIOCGSECTORSIZE + if (ioctl(fd, DIOCGSECTORSIZE, §or_size) >= 0) { + *sector_size_p = sector_size; + success = true; + } +#endif + + return success ? 0 : -errno; +} + +/** + * Get physical block size of @fd. + * On success, store it in @blk_size and return 0. + * On failure, return -errno. + */ +static int probe_physical_blocksize(int fd, unsigned int *blk_size) +{ +#ifdef BLKPBSZGET + if (ioctl(fd, BLKPBSZGET, blk_size) < 0) { + return -errno; + } + return 0; +#else + return -ENOTSUP; +#endif +} + +/* Check if read is allowed with given memory buffer and length. + * + * This function is used to check O_DIRECT memory buffer and request alignment. + */ +static bool raw_is_io_aligned(int fd, void *buf, size_t len) +{ + ssize_t ret = pread(fd, buf, len, 0); + + if (ret >= 0) { + return true; + } + +#ifdef __linux__ + /* The Linux kernel returns EINVAL for misaligned O_DIRECT reads. Ignore + * other errors (e.g. real I/O error), which could happen on a failed + * drive, since we only care about probing alignment. + */ + if (errno != EINVAL) { + return true; + } +#endif + + return false; +} + +static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) +{ + BDRVRawState *s = bs->opaque; + char *buf; + size_t max_align = MAX(MAX_BLOCKSIZE, getpagesize()); + + /* For /dev/sg devices the alignment is not really used. + With buffered I/O, we don't have any restrictions. */ + if (bs->sg || !s->needs_alignment) { + bs->request_alignment = 1; + s->buf_align = 1; + return; + } + + bs->request_alignment = 0; + s->buf_align = 0; + /* Let's try to use the logical blocksize for the alignment. */ + if (probe_logical_blocksize(fd, &bs->request_alignment) < 0) { + bs->request_alignment = 0; + } +#ifdef CONFIG_XFS + if (s->is_xfs) { + struct dioattr da; + if (xfsctl(NULL, fd, XFS_IOC_DIOINFO, &da) >= 0) { + bs->request_alignment = da.d_miniosz; + /* The kernel returns wrong information for d_mem */ + /* s->buf_align = da.d_mem; */ + } + } +#endif + + /* If we could not get the sizes so far, we can only guess them */ + if (!s->buf_align) { + size_t align; + buf = qemu_memalign(max_align, 2 * max_align); + for (align = 512; align <= max_align; align <<= 1) { + if (raw_is_io_aligned(fd, buf + align, max_align)) { + s->buf_align = align; + break; + } + } + qemu_vfree(buf); + } + + if (!bs->request_alignment) { + size_t align; + buf = qemu_memalign(s->buf_align, max_align); + for (align = 512; align <= max_align; align <<= 1) { + if (raw_is_io_aligned(fd, buf, align)) { + bs->request_alignment = align; + break; + } + } + qemu_vfree(buf); + } + + if (!s->buf_align || !bs->request_alignment) { + error_setg(errp, "Could not find working O_DIRECT alignment. " + "Try cache.direct=off."); + } +} + +static void raw_parse_flags(int bdrv_flags, int *open_flags) +{ + assert(open_flags != NULL); + + *open_flags |= O_BINARY; + *open_flags &= ~O_ACCMODE; + if (bdrv_flags & BDRV_O_RDWR) { + *open_flags |= O_RDWR; + } else { + *open_flags |= O_RDONLY; + } + + /* Use O_DSYNC for write-through caching, no flags for write-back caching, + * and O_DIRECT for no caching. */ + if ((bdrv_flags & BDRV_O_NOCACHE)) { + *open_flags |= O_DIRECT; + } +} + +static void raw_detach_aio_context(BlockDriverState *bs) +{ +#ifdef CONFIG_LINUX_AIO + BDRVRawState *s = bs->opaque; + + if (s->use_aio) { + laio_detach_aio_context(s->aio_ctx, bdrv_get_aio_context(bs)); + } +#endif +} + +static void raw_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ +#ifdef CONFIG_LINUX_AIO + BDRVRawState *s = bs->opaque; + + if (s->use_aio) { + laio_attach_aio_context(s->aio_ctx, new_context); + } +#endif +} + +#ifdef CONFIG_LINUX_AIO +static int raw_set_aio(void **aio_ctx, int *use_aio, int bdrv_flags) +{ + int ret = -1; + assert(aio_ctx != NULL); + assert(use_aio != NULL); + /* + * Currently Linux do AIO only for files opened with O_DIRECT + * specified so check NOCACHE flag too + */ + if ((bdrv_flags & (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) == + (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) { + + /* if non-NULL, laio_init() has already been run */ + if (*aio_ctx == NULL) { + *aio_ctx = laio_init(); + if (!*aio_ctx) { + goto error; + } + } + *use_aio = 1; + } else { + *use_aio = 0; + } + + ret = 0; + +error: + return ret; +} +#endif + +static void raw_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + /* The filename does not have to be prefixed by the protocol name, since + * "file" is the default protocol; therefore, the return value of this + * function call can be ignored. */ + strstart(filename, "file:", &filename); + + qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); +} + +static QemuOptsList raw_runtime_opts = { + .name = "raw", + .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "File name of the image", + }, + { /* end of list */ } + }, +}; + +static int raw_open_common(BlockDriverState *bs, QDict *options, + int bdrv_flags, int open_flags, Error **errp) +{ + BDRVRawState *s = bs->opaque; + QemuOpts *opts; + Error *local_err = NULL; + const char *filename = NULL; + int fd, ret; + struct stat st; + + opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + filename = qemu_opt_get(opts, "filename"); + + ret = raw_normalize_devicepath(&filename); + if (ret != 0) { + error_setg_errno(errp, -ret, "Could not normalize device path"); + goto fail; + } + + s->open_flags = open_flags; + raw_parse_flags(bdrv_flags, &s->open_flags); + + s->fd = -1; + fd = qemu_open(filename, s->open_flags, 0644); + if (fd < 0) { + ret = -errno; + if (ret == -EROFS) { + ret = -EACCES; + } + goto fail; + } + s->fd = fd; + +#ifdef CONFIG_LINUX_AIO + if (raw_set_aio(&s->aio_ctx, &s->use_aio, bdrv_flags)) { + qemu_close(fd); + ret = -errno; + error_setg_errno(errp, -ret, "Could not set AIO state"); + goto fail; + } + if (!s->use_aio && (bdrv_flags & BDRV_O_NATIVE_AIO)) { + error_printf("WARNING: aio=native was specified for '%s', but " + "it requires cache.direct=on, which was not " + "specified. Falling back to aio=threads.\n" + " This will become an error condition in " + "future QEMU versions.\n", + bs->filename); + } +#endif + + s->has_discard = true; + s->has_write_zeroes = true; + if ((bs->open_flags & BDRV_O_NOCACHE) != 0) { + s->needs_alignment = true; + } + + if (fstat(s->fd, &st) < 0) { + ret = -errno; + error_setg_errno(errp, errno, "Could not stat file"); + goto fail; + } + if (S_ISREG(st.st_mode)) { + s->discard_zeroes = true; + s->has_fallocate = true; + } + if (S_ISBLK(st.st_mode)) { +#ifdef BLKDISCARDZEROES + unsigned int arg; + if (ioctl(s->fd, BLKDISCARDZEROES, &arg) == 0 && arg) { + s->discard_zeroes = true; + } +#endif +#ifdef __linux__ + /* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do + * not rely on the contents of discarded blocks unless using O_DIRECT. + * Same for BLKZEROOUT. + */ + if (!(bs->open_flags & BDRV_O_NOCACHE)) { + s->discard_zeroes = false; + s->has_write_zeroes = false; + } +#endif + } +#ifdef __FreeBSD__ + if (S_ISCHR(st.st_mode)) { + /* + * The file is a char device (disk), which on FreeBSD isn't behind + * a pager, so force all requests to be aligned. This is needed + * so QEMU makes sure all IO operations on the device are aligned + * to sector size, or else FreeBSD will reject them with EINVAL. + */ + s->needs_alignment = true; + } +#endif + +#ifdef CONFIG_XFS + if (platform_test_xfs_fd(s->fd)) { + s->is_xfs = true; + } +#endif + + raw_attach_aio_context(bs, bdrv_get_aio_context(bs)); + + ret = 0; +fail: + if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { + unlink(filename); + } + qemu_opts_del(opts); + return ret; +} + +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + Error *local_err = NULL; + int ret; + + s->type = FTYPE_FILE; + ret = raw_open_common(bs, options, flags, 0, &local_err); + if (local_err) { + error_propagate(errp, local_err); + } + return ret; +} + +static int raw_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + BDRVRawState *s; + BDRVRawReopenState *raw_s; + int ret = 0; + Error *local_err = NULL; + + assert(state != NULL); + assert(state->bs != NULL); + + s = state->bs->opaque; + + state->opaque = g_new0(BDRVRawReopenState, 1); + raw_s = state->opaque; + +#ifdef CONFIG_LINUX_AIO + raw_s->use_aio = s->use_aio; + + /* we can use s->aio_ctx instead of a copy, because the use_aio flag is + * valid in the 'false' condition even if aio_ctx is set, and raw_set_aio() + * won't override aio_ctx if aio_ctx is non-NULL */ + if (raw_set_aio(&s->aio_ctx, &raw_s->use_aio, state->flags)) { + error_setg(errp, "Could not set AIO state"); + return -1; + } +#endif + + if (s->type == FTYPE_FD || s->type == FTYPE_CD) { + raw_s->open_flags |= O_NONBLOCK; + } + + raw_parse_flags(state->flags, &raw_s->open_flags); + + raw_s->fd = -1; + + int fcntl_flags = O_APPEND | O_NONBLOCK; +#ifdef O_NOATIME + fcntl_flags |= O_NOATIME; +#endif + +#ifdef O_ASYNC + /* Not all operating systems have O_ASYNC, and those that don't + * will not let us track the state into raw_s->open_flags (typically + * you achieve the same effect with an ioctl, for example I_SETSIG + * on Solaris). But we do not use O_ASYNC, so that's fine. + */ + assert((s->open_flags & O_ASYNC) == 0); +#endif + + if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { + /* dup the original fd */ + /* TODO: use qemu fcntl wrapper */ +#ifdef F_DUPFD_CLOEXEC + raw_s->fd = fcntl(s->fd, F_DUPFD_CLOEXEC, 0); +#else + raw_s->fd = dup(s->fd); + if (raw_s->fd != -1) { + qemu_set_cloexec(raw_s->fd); + } +#endif + if (raw_s->fd >= 0) { + ret = fcntl_setfl(raw_s->fd, raw_s->open_flags); + if (ret) { + qemu_close(raw_s->fd); + raw_s->fd = -1; + } + } + } + + /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ + if (raw_s->fd == -1) { + assert(!(raw_s->open_flags & O_CREAT)); + raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags); + if (raw_s->fd == -1) { + error_setg_errno(errp, errno, "Could not reopen file"); + ret = -1; + } + } + + /* Fail already reopen_prepare() if we can't get a working O_DIRECT + * alignment with the new fd. */ + if (raw_s->fd != -1) { + raw_probe_alignment(state->bs, raw_s->fd, &local_err); + if (local_err) { + qemu_close(raw_s->fd); + raw_s->fd = -1; + error_propagate(errp, local_err); + ret = -EINVAL; + } + } + + return ret; +} + +static void raw_reopen_commit(BDRVReopenState *state) +{ + BDRVRawReopenState *raw_s = state->opaque; + BDRVRawState *s = state->bs->opaque; + + s->open_flags = raw_s->open_flags; + + qemu_close(s->fd); + s->fd = raw_s->fd; +#ifdef CONFIG_LINUX_AIO + s->use_aio = raw_s->use_aio; +#endif + + g_free(state->opaque); + state->opaque = NULL; +} + + +static void raw_reopen_abort(BDRVReopenState *state) +{ + BDRVRawReopenState *raw_s = state->opaque; + + /* nothing to do if NULL, we didn't get far enough */ + if (raw_s == NULL) { + return; + } + + if (raw_s->fd >= 0) { + qemu_close(raw_s->fd); + raw_s->fd = -1; + } + g_free(state->opaque); + state->opaque = NULL; +} + +static void raw_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BDRVRawState *s = bs->opaque; + + raw_probe_alignment(bs, s->fd, errp); + bs->bl.min_mem_alignment = s->buf_align; + bs->bl.opt_mem_alignment = MAX(s->buf_align, getpagesize()); +} + +static int check_for_dasd(int fd) +{ +#ifdef BIODASDINFO2 + struct dasd_information2_t info = {0}; + + return ioctl(fd, BIODASDINFO2, &info); +#else + return -1; +#endif +} + +/** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz and return zero. + * On failure, return negative errno. + */ +static int hdev_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + BDRVRawState *s = bs->opaque; + int ret; + + /* If DASD, get blocksizes */ + if (check_for_dasd(s->fd) < 0) { + return -ENOTSUP; + } + ret = probe_logical_blocksize(s->fd, &bsz->log); + if (ret < 0) { + return ret; + } + return probe_physical_blocksize(s->fd, &bsz->phys); +} + +/** + * Try to get @bs's geometry: cyls, heads, sectors. + * On success, store them in @geo and return 0. + * On failure return -errno. + * (Allows block driver to assign default geometry values that guest sees) + */ +#ifdef __linux__ +static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + BDRVRawState *s = bs->opaque; + struct hd_geometry ioctl_geo = {0}; + uint32_t blksize; + + /* If DASD, get its geometry */ + if (check_for_dasd(s->fd) < 0) { + return -ENOTSUP; + } + if (ioctl(s->fd, HDIO_GETGEO, &ioctl_geo) < 0) { + return -errno; + } + /* HDIO_GETGEO may return success even though geo contains zeros + (e.g. certain multipath setups) */ + if (!ioctl_geo.heads || !ioctl_geo.sectors || !ioctl_geo.cylinders) { + return -ENOTSUP; + } + /* Do not return a geometry for partition */ + if (ioctl_geo.start != 0) { + return -ENOTSUP; + } + geo->heads = ioctl_geo.heads; + geo->sectors = ioctl_geo.sectors; + if (!probe_physical_blocksize(s->fd, &blksize)) { + /* overwrite cyls: HDIO_GETGEO result is incorrect for big drives */ + geo->cylinders = bdrv_nb_sectors(bs) / (blksize / BDRV_SECTOR_SIZE) + / (geo->heads * geo->sectors); + return 0; + } + geo->cylinders = ioctl_geo.cylinders; + + return 0; +} +#else /* __linux__ */ +static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + return -ENOTSUP; +} +#endif + +static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb) +{ + int ret; + + ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf); + if (ret == -1) { + return -errno; + } + + return 0; +} + +static ssize_t handle_aiocb_flush(RawPosixAIOData *aiocb) +{ + int ret; + + ret = qemu_fdatasync(aiocb->aio_fildes); + if (ret == -1) { + return -errno; + } + return 0; +} + +#ifdef CONFIG_PREADV + +static bool preadv_present = true; + +static ssize_t +qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset) +{ + return preadv(fd, iov, nr_iov, offset); +} + +static ssize_t +qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset) +{ + return pwritev(fd, iov, nr_iov, offset); +} + +#else + +static bool preadv_present = false; + +static ssize_t +qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset) +{ + return -ENOSYS; +} + +static ssize_t +qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset) +{ + return -ENOSYS; +} + +#endif + +static ssize_t handle_aiocb_rw_vector(RawPosixAIOData *aiocb) +{ + ssize_t len; + + do { + if (aiocb->aio_type & QEMU_AIO_WRITE) + len = qemu_pwritev(aiocb->aio_fildes, + aiocb->aio_iov, + aiocb->aio_niov, + aiocb->aio_offset); + else + len = qemu_preadv(aiocb->aio_fildes, + aiocb->aio_iov, + aiocb->aio_niov, + aiocb->aio_offset); + } while (len == -1 && errno == EINTR); + + if (len == -1) { + return -errno; + } + return len; +} + +/* + * Read/writes the data to/from a given linear buffer. + * + * Returns the number of bytes handles or -errno in case of an error. Short + * reads are only returned if the end of the file is reached. + */ +static ssize_t handle_aiocb_rw_linear(RawPosixAIOData *aiocb, char *buf) +{ + ssize_t offset = 0; + ssize_t len; + + while (offset < aiocb->aio_nbytes) { + if (aiocb->aio_type & QEMU_AIO_WRITE) { + len = pwrite(aiocb->aio_fildes, + (const char *)buf + offset, + aiocb->aio_nbytes - offset, + aiocb->aio_offset + offset); + } else { + len = pread(aiocb->aio_fildes, + buf + offset, + aiocb->aio_nbytes - offset, + aiocb->aio_offset + offset); + } + if (len == -1 && errno == EINTR) { + continue; + } else if (len == -1 && errno == EINVAL && + (aiocb->bs->open_flags & BDRV_O_NOCACHE) && + !(aiocb->aio_type & QEMU_AIO_WRITE) && + offset > 0) { + /* O_DIRECT pread() may fail with EINVAL when offset is unaligned + * after a short read. Assume that O_DIRECT short reads only occur + * at EOF. Therefore this is a short read, not an I/O error. + */ + break; + } else if (len == -1) { + offset = -errno; + break; + } else if (len == 0) { + break; + } + offset += len; + } + + return offset; +} + +static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) +{ + ssize_t nbytes; + char *buf; + + if (!(aiocb->aio_type & QEMU_AIO_MISALIGNED)) { + /* + * If there is just a single buffer, and it is properly aligned + * we can just use plain pread/pwrite without any problems. + */ + if (aiocb->aio_niov == 1) { + return handle_aiocb_rw_linear(aiocb, aiocb->aio_iov->iov_base); + } + /* + * We have more than one iovec, and all are properly aligned. + * + * Try preadv/pwritev first and fall back to linearizing the + * buffer if it's not supported. + */ + if (preadv_present) { + nbytes = handle_aiocb_rw_vector(aiocb); + if (nbytes == aiocb->aio_nbytes || + (nbytes < 0 && nbytes != -ENOSYS)) { + return nbytes; + } + preadv_present = false; + } + + /* + * XXX(hch): short read/write. no easy way to handle the reminder + * using these interfaces. For now retry using plain + * pread/pwrite? + */ + } + + /* + * Ok, we have to do it the hard way, copy all segments into + * a single aligned buffer. + */ + buf = qemu_try_blockalign(aiocb->bs, aiocb->aio_nbytes); + if (buf == NULL) { + return -ENOMEM; + } + + if (aiocb->aio_type & QEMU_AIO_WRITE) { + char *p = buf; + int i; + + for (i = 0; i < aiocb->aio_niov; ++i) { + memcpy(p, aiocb->aio_iov[i].iov_base, aiocb->aio_iov[i].iov_len); + p += aiocb->aio_iov[i].iov_len; + } + assert(p - buf == aiocb->aio_nbytes); + } + + nbytes = handle_aiocb_rw_linear(aiocb, buf); + if (!(aiocb->aio_type & QEMU_AIO_WRITE)) { + char *p = buf; + size_t count = aiocb->aio_nbytes, copy; + int i; + + for (i = 0; i < aiocb->aio_niov && count; ++i) { + copy = count; + if (copy > aiocb->aio_iov[i].iov_len) { + copy = aiocb->aio_iov[i].iov_len; + } + memcpy(aiocb->aio_iov[i].iov_base, p, copy); + assert(count >= copy); + p += copy; + count -= copy; + } + assert(count == 0); + } + qemu_vfree(buf); + + return nbytes; +} + +#ifdef CONFIG_XFS +static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes) +{ + struct xfs_flock64 fl; + + memset(&fl, 0, sizeof(fl)); + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = bytes; + + if (xfsctl(NULL, s->fd, XFS_IOC_ZERO_RANGE, &fl) < 0) { + DEBUG_BLOCK_PRINT("cannot write zero range (%s)\n", strerror(errno)); + return -errno; + } + + return 0; +} + +static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) +{ + struct xfs_flock64 fl; + + memset(&fl, 0, sizeof(fl)); + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = bytes; + + if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { + DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno)); + return -errno; + } + + return 0; +} +#endif + +static int translate_err(int err) +{ + if (err == -ENODEV || err == -ENOSYS || err == -EOPNOTSUPP || + err == -ENOTTY) { + err = -ENOTSUP; + } + return err; +} + +#ifdef CONFIG_FALLOCATE +static int do_fallocate(int fd, int mode, off_t offset, off_t len) +{ + do { + if (fallocate(fd, mode, offset, len) == 0) { + return 0; + } + } while (errno == EINTR); + return translate_err(-errno); +} +#endif + +static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) +{ + int ret = -ENOTSUP; + BDRVRawState *s = aiocb->bs->opaque; + + if (!s->has_write_zeroes) { + return -ENOTSUP; + } + +#ifdef BLKZEROOUT + do { + uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; + if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) { + return 0; + } + } while (errno == EINTR); + + ret = translate_err(-errno); +#endif + + if (ret == -ENOTSUP) { + s->has_write_zeroes = false; + } + return ret; +} + +static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) +{ +#if defined(CONFIG_FALLOCATE) || defined(CONFIG_XFS) + BDRVRawState *s = aiocb->bs->opaque; +#endif + + if (aiocb->aio_type & QEMU_AIO_BLKDEV) { + return handle_aiocb_write_zeroes_block(aiocb); + } + +#ifdef CONFIG_XFS + if (s->is_xfs) { + return xfs_write_zeroes(s, aiocb->aio_offset, aiocb->aio_nbytes); + } +#endif + +#ifdef CONFIG_FALLOCATE_ZERO_RANGE + if (s->has_write_zeroes) { + int ret = do_fallocate(s->fd, FALLOC_FL_ZERO_RANGE, + aiocb->aio_offset, aiocb->aio_nbytes); + if (ret == 0 || ret != -ENOTSUP) { + return ret; + } + s->has_write_zeroes = false; + } +#endif + +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE + if (s->has_discard && s->has_fallocate) { + int ret = do_fallocate(s->fd, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + aiocb->aio_offset, aiocb->aio_nbytes); + if (ret == 0) { + ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); + if (ret == 0 || ret != -ENOTSUP) { + return ret; + } + s->has_fallocate = false; + } else if (ret != -ENOTSUP) { + return ret; + } else { + s->has_discard = false; + } + } +#endif + +#ifdef CONFIG_FALLOCATE + if (s->has_fallocate && aiocb->aio_offset >= bdrv_getlength(aiocb->bs)) { + int ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); + if (ret == 0 || ret != -ENOTSUP) { + return ret; + } + s->has_fallocate = false; + } +#endif + + return -ENOTSUP; +} + +static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) +{ + int ret = -EOPNOTSUPP; + BDRVRawState *s = aiocb->bs->opaque; + + if (!s->has_discard) { + return -ENOTSUP; + } + + if (aiocb->aio_type & QEMU_AIO_BLKDEV) { +#ifdef BLKDISCARD + do { + uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; + if (ioctl(aiocb->aio_fildes, BLKDISCARD, range) == 0) { + return 0; + } + } while (errno == EINTR); + + ret = -errno; +#endif + } else { +#ifdef CONFIG_XFS + if (s->is_xfs) { + return xfs_discard(s, aiocb->aio_offset, aiocb->aio_nbytes); + } +#endif + +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE + ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + aiocb->aio_offset, aiocb->aio_nbytes); +#endif + } + + ret = translate_err(ret); + if (ret == -ENOTSUP) { + s->has_discard = false; + } + return ret; +} + +static int aio_worker(void *arg) +{ + RawPosixAIOData *aiocb = arg; + ssize_t ret = 0; + + switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { + case QEMU_AIO_READ: + ret = handle_aiocb_rw(aiocb); + if (ret >= 0 && ret < aiocb->aio_nbytes) { + iov_memset(aiocb->aio_iov, aiocb->aio_niov, ret, + 0, aiocb->aio_nbytes - ret); + + ret = aiocb->aio_nbytes; + } + if (ret == aiocb->aio_nbytes) { + ret = 0; + } else if (ret >= 0 && ret < aiocb->aio_nbytes) { + ret = -EINVAL; + } + break; + case QEMU_AIO_WRITE: + ret = handle_aiocb_rw(aiocb); + if (ret == aiocb->aio_nbytes) { + ret = 0; + } else if (ret >= 0 && ret < aiocb->aio_nbytes) { + ret = -EINVAL; + } + break; + case QEMU_AIO_FLUSH: + ret = handle_aiocb_flush(aiocb); + break; + case QEMU_AIO_IOCTL: + ret = handle_aiocb_ioctl(aiocb); + break; + case QEMU_AIO_DISCARD: + ret = handle_aiocb_discard(aiocb); + break; + case QEMU_AIO_WRITE_ZEROES: + ret = handle_aiocb_write_zeroes(aiocb); + break; + default: + fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); + ret = -EINVAL; + break; + } + + g_slice_free(RawPosixAIOData, aiocb); + return ret; +} + +static int paio_submit_co(BlockDriverState *bs, int fd, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + int type) +{ + RawPosixAIOData *acb = g_slice_new(RawPosixAIOData); + ThreadPool *pool; + + acb->bs = bs; + acb->aio_type = type; + acb->aio_fildes = fd; + + acb->aio_nbytes = nb_sectors * BDRV_SECTOR_SIZE; + acb->aio_offset = sector_num * BDRV_SECTOR_SIZE; + + if (qiov) { + acb->aio_iov = qiov->iov; + acb->aio_niov = qiov->niov; + assert(qiov->size == acb->aio_nbytes); + } + + trace_paio_submit_co(sector_num, nb_sectors, type); + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_co(pool, aio_worker, acb); +} + +static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, int type) +{ + RawPosixAIOData *acb = g_slice_new(RawPosixAIOData); + ThreadPool *pool; + + acb->bs = bs; + acb->aio_type = type; + acb->aio_fildes = fd; + + acb->aio_nbytes = nb_sectors * BDRV_SECTOR_SIZE; + acb->aio_offset = sector_num * BDRV_SECTOR_SIZE; + + if (qiov) { + acb->aio_iov = qiov->iov; + acb->aio_niov = qiov->niov; + assert(qiov->size == acb->aio_nbytes); + } + + trace_paio_submit(acb, opaque, sector_num, nb_sectors, type); + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); +} + +static BlockAIOCB *raw_aio_submit(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, int type) +{ + BDRVRawState *s = bs->opaque; + + if (fd_open(bs) < 0) + return NULL; + + /* + * Check if the underlying device requires requests to be aligned, + * and if the request we are trying to submit is aligned or not. + * If this is the case tell the low-level driver that it needs + * to copy the buffer. + */ + if (s->needs_alignment) { + if (!bdrv_qiov_is_aligned(bs, qiov)) { + type |= QEMU_AIO_MISALIGNED; +#ifdef CONFIG_LINUX_AIO + } else if (s->use_aio) { + return laio_submit(bs, s->aio_ctx, s->fd, sector_num, qiov, + nb_sectors, cb, opaque, type); +#endif + } + } + + return paio_submit(bs, s->fd, sector_num, qiov, nb_sectors, + cb, opaque, type); +} + +static void raw_aio_plug(BlockDriverState *bs) +{ +#ifdef CONFIG_LINUX_AIO + BDRVRawState *s = bs->opaque; + if (s->use_aio) { + laio_io_plug(bs, s->aio_ctx); + } +#endif +} + +static void raw_aio_unplug(BlockDriverState *bs) +{ +#ifdef CONFIG_LINUX_AIO + BDRVRawState *s = bs->opaque; + if (s->use_aio) { + laio_io_unplug(bs, s->aio_ctx, true); + } +#endif +} + +static void raw_aio_flush_io_queue(BlockDriverState *bs) +{ +#ifdef CONFIG_LINUX_AIO + BDRVRawState *s = bs->opaque; + if (s->use_aio) { + laio_io_unplug(bs, s->aio_ctx, false); + } +#endif +} + +static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return raw_aio_submit(bs, sector_num, qiov, nb_sectors, + cb, opaque, QEMU_AIO_READ); +} + +static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return raw_aio_submit(bs, sector_num, qiov, nb_sectors, + cb, opaque, QEMU_AIO_WRITE); +} + +static BlockAIOCB *raw_aio_flush(BlockDriverState *bs, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + + if (fd_open(bs) < 0) + return NULL; + + return paio_submit(bs, s->fd, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH); +} + +static void raw_close(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + + raw_detach_aio_context(bs); + +#ifdef CONFIG_LINUX_AIO + if (s->use_aio) { + laio_cleanup(s->aio_ctx); + } +#endif + if (s->fd >= 0) { + qemu_close(s->fd); + s->fd = -1; + } +} + +static int raw_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVRawState *s = bs->opaque; + struct stat st; + + if (fstat(s->fd, &st)) { + return -errno; + } + + if (S_ISREG(st.st_mode)) { + if (ftruncate(s->fd, offset) < 0) { + return -errno; + } + } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { + if (offset > raw_getlength(bs)) { + return -EINVAL; + } + } else { + return -ENOTSUP; + } + + return 0; +} + +#ifdef __OpenBSD__ +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int fd = s->fd; + struct stat st; + + if (fstat(fd, &st)) + return -errno; + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { + struct disklabel dl; + + if (ioctl(fd, DIOCGDINFO, &dl)) + return -errno; + return (uint64_t)dl.d_secsize * + dl.d_partitions[DISKPART(st.st_rdev)].p_size; + } else + return st.st_size; +} +#elif defined(__NetBSD__) +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int fd = s->fd; + struct stat st; + + if (fstat(fd, &st)) + return -errno; + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { + struct dkwedge_info dkw; + + if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1) { + return dkw.dkw_size * 512; + } else { + struct disklabel dl; + + if (ioctl(fd, DIOCGDINFO, &dl)) + return -errno; + return (uint64_t)dl.d_secsize * + dl.d_partitions[DISKPART(st.st_rdev)].p_size; + } + } else + return st.st_size; +} +#elif defined(__sun__) +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + struct dk_minfo minfo; + int ret; + int64_t size; + + ret = fd_open(bs); + if (ret < 0) { + return ret; + } + + /* + * Use the DKIOCGMEDIAINFO ioctl to read the size. + */ + ret = ioctl(s->fd, DKIOCGMEDIAINFO, &minfo); + if (ret != -1) { + return minfo.dki_lbsize * minfo.dki_capacity; + } + + /* + * There are reports that lseek on some devices fails, but + * irc discussion said that contingency on contingency was overkill. + */ + size = lseek(s->fd, 0, SEEK_END); + if (size < 0) { + return -errno; + } + return size; +} +#elif defined(CONFIG_BSD) +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int fd = s->fd; + int64_t size; + struct stat sb; +#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) + int reopened = 0; +#endif + int ret; + + ret = fd_open(bs); + if (ret < 0) + return ret; + +#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) +again: +#endif + if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { +#ifdef DIOCGMEDIASIZE + if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) +#elif defined(DIOCGPART) + { + struct partinfo pi; + if (ioctl(fd, DIOCGPART, &pi) == 0) + size = pi.media_size; + else + size = 0; + } + if (size == 0) +#endif +#if defined(__APPLE__) && defined(__MACH__) + { + uint64_t sectors = 0; + uint32_t sector_size = 0; + + if (ioctl(fd, DKIOCGETBLOCKCOUNT, §ors) == 0 + && ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) == 0) { + size = sectors * sector_size; + } else { + size = lseek(fd, 0LL, SEEK_END); + if (size < 0) { + return -errno; + } + } + } +#else + size = lseek(fd, 0LL, SEEK_END); + if (size < 0) { + return -errno; + } +#endif +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + switch(s->type) { + case FTYPE_CD: + /* XXX FreeBSD acd returns UINT_MAX sectors for an empty drive */ + if (size == 2048LL * (unsigned)-1) + size = 0; + /* XXX no disc? maybe we need to reopen... */ + if (size <= 0 && !reopened && cdrom_reopen(bs) >= 0) { + reopened = 1; + goto again; + } + } +#endif + } else { + size = lseek(fd, 0, SEEK_END); + if (size < 0) { + return -errno; + } + } + return size; +} +#else +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int ret; + int64_t size; + + ret = fd_open(bs); + if (ret < 0) { + return ret; + } + + size = lseek(s->fd, 0, SEEK_END); + if (size < 0) { + return -errno; + } + return size; +} +#endif + +static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +{ + struct stat st; + BDRVRawState *s = bs->opaque; + + if (fstat(s->fd, &st) < 0) { + return -errno; + } + return (int64_t)st.st_blocks * 512; +} + +static int raw_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int fd; + int result = 0; + int64_t total_size = 0; + bool nocow = false; + PreallocMode prealloc; + char *buf = NULL; + Error *local_err = NULL; + + strstart(filename, "file:", &filename); + + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false); + buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); + prealloc = qapi_enum_parse(PreallocMode_lookup, buf, + PREALLOC_MODE_MAX, PREALLOC_MODE_OFF, + &local_err); + g_free(buf); + if (local_err) { + error_propagate(errp, local_err); + result = -EINVAL; + goto out; + } + + fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + 0644); + if (fd < 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not create file"); + goto out; + } + + if (nocow) { +#ifdef __linux__ + /* Set NOCOW flag to solve performance issue on fs like btrfs. + * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value + * will be ignored since any failure of this operation should not + * block the left work. + */ + int attr; + if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) { + attr |= FS_NOCOW_FL; + ioctl(fd, FS_IOC_SETFLAGS, &attr); + } +#endif + } + + if (ftruncate(fd, total_size) != 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not resize file"); + goto out_close; + } + + switch (prealloc) { +#ifdef CONFIG_POSIX_FALLOCATE + case PREALLOC_MODE_FALLOC: + /* posix_fallocate() doesn't set errno. */ + result = -posix_fallocate(fd, 0, total_size); + if (result != 0) { + error_setg_errno(errp, -result, + "Could not preallocate data for the new file"); + } + break; +#endif + case PREALLOC_MODE_FULL: + { + int64_t num = 0, left = total_size; + buf = g_malloc0(65536); + + while (left > 0) { + num = MIN(left, 65536); + result = write(fd, buf, num); + if (result < 0) { + result = -errno; + error_setg_errno(errp, -result, + "Could not write to the new file"); + break; + } + left -= result; + } + if (result >= 0) { + result = fsync(fd); + if (result < 0) { + result = -errno; + error_setg_errno(errp, -result, + "Could not flush new file to disk"); + } + } + g_free(buf); + break; + } + case PREALLOC_MODE_OFF: + break; + default: + result = -EINVAL; + error_setg(errp, "Unsupported preallocation mode: %s", + PreallocMode_lookup[prealloc]); + break; + } + +out_close: + if (qemu_close(fd) != 0 && result == 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not close the new file"); + } +out: + return result; +} + +/* + * Find allocation range in @bs around offset @start. + * May change underlying file descriptor's file offset. + * If @start is not in a hole, store @start in @data, and the + * beginning of the next hole in @hole, and return 0. + * If @start is in a non-trailing hole, store @start in @hole and the + * beginning of the next non-hole in @data, and return 0. + * If @start is in a trailing hole or beyond EOF, return -ENXIO. + * If we can't find out, return a negative errno other than -ENXIO. + */ +static int find_allocation(BlockDriverState *bs, off_t start, + off_t *data, off_t *hole) +{ +#if defined SEEK_HOLE && defined SEEK_DATA + BDRVRawState *s = bs->opaque; + off_t offs; + + /* + * SEEK_DATA cases: + * D1. offs == start: start is in data + * D2. offs > start: start is in a hole, next data at offs + * D3. offs < 0, errno = ENXIO: either start is in a trailing hole + * or start is beyond EOF + * If the latter happens, the file has been truncated behind + * our back since we opened it. All bets are off then. + * Treating like a trailing hole is simplest. + * D4. offs < 0, errno != ENXIO: we learned nothing + */ + offs = lseek(s->fd, start, SEEK_DATA); + if (offs < 0) { + return -errno; /* D3 or D4 */ + } + assert(offs >= start); + + if (offs > start) { + /* D2: in hole, next data at offs */ + *hole = start; + *data = offs; + return 0; + } + + /* D1: in data, end not yet known */ + + /* + * SEEK_HOLE cases: + * H1. offs == start: start is in a hole + * If this happens here, a hole has been dug behind our back + * since the previous lseek(). + * H2. offs > start: either start is in data, next hole at offs, + * or start is in trailing hole, EOF at offs + * Linux treats trailing holes like any other hole: offs == + * start. Solaris seeks to EOF instead: offs > start (blech). + * If that happens here, a hole has been dug behind our back + * since the previous lseek(). + * H3. offs < 0, errno = ENXIO: start is beyond EOF + * If this happens, the file has been truncated behind our + * back since we opened it. Treat it like a trailing hole. + * H4. offs < 0, errno != ENXIO: we learned nothing + * Pretend we know nothing at all, i.e. "forget" about D1. + */ + offs = lseek(s->fd, start, SEEK_HOLE); + if (offs < 0) { + return -errno; /* D1 and (H3 or H4) */ + } + assert(offs >= start); + + if (offs > start) { + /* + * D1 and H2: either in data, next hole at offs, or it was in + * data but is now in a trailing hole. In the latter case, + * all bets are off. Treating it as if it there was data all + * the way to EOF is safe, so simply do that. + */ + *data = start; + *hole = offs; + return 0; + } + + /* D1 and H1 */ + return -EBUSY; +#else + return -ENOTSUP; +#endif +} + +/* + * Returns the allocation status of the specified sectors. + * + * If 'sector_num' is beyond the end of the disk image the return value is 0 + * and 'pnum' is set to 0. + * + * 'pnum' is set to the number of sectors (including and immediately following + * the specified sector) that are known to be in the same + * allocated/unallocated state. + * + * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes + * beyond the end of the disk image it will be clamped. + */ +static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + off_t start, data = 0, hole = 0; + int64_t total_size; + int ret; + + ret = fd_open(bs); + if (ret < 0) { + return ret; + } + + start = sector_num * BDRV_SECTOR_SIZE; + total_size = bdrv_getlength(bs); + if (total_size < 0) { + return total_size; + } else if (start >= total_size) { + *pnum = 0; + return 0; + } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) { + nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE); + } + + ret = find_allocation(bs, start, &data, &hole); + if (ret == -ENXIO) { + /* Trailing hole */ + *pnum = nb_sectors; + ret = BDRV_BLOCK_ZERO; + } else if (ret < 0) { + /* No info available, so pretend there are no holes */ + *pnum = nb_sectors; + ret = BDRV_BLOCK_DATA; + } else if (data == start) { + /* On a data extent, compute sectors to the end of the extent. */ + *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE); + ret = BDRV_BLOCK_DATA; + } else { + /* On a hole, compute sectors to the beginning of the next extent. */ + assert(hole == start); + *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE); + ret = BDRV_BLOCK_ZERO; + } + return ret | BDRV_BLOCK_OFFSET_VALID | start; +} + +static coroutine_fn BlockAIOCB *raw_aio_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + + return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors, + cb, opaque, QEMU_AIO_DISCARD); +} + +static int coroutine_fn raw_co_write_zeroes( + BlockDriverState *bs, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags) +{ + BDRVRawState *s = bs->opaque; + + if (!(flags & BDRV_REQ_MAY_UNMAP)) { + return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors, + QEMU_AIO_WRITE_ZEROES); + } else if (s->discard_zeroes) { + return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors, + QEMU_AIO_DISCARD); + } + return -ENOTSUP; +} + +static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVRawState *s = bs->opaque; + + bdi->unallocated_blocks_are_zero = s->discard_zeroes; + bdi->can_write_zeroes_with_unmap = s->discard_zeroes; + return 0; +} + +static QemuOptsList raw_create_opts = { + .name = "raw-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_NOCOW, + .type = QEMU_OPT_BOOL, + .help = "Turn off copy-on-write (valid only on btrfs)" + }, + { + .name = BLOCK_OPT_PREALLOC, + .type = QEMU_OPT_STRING, + .help = "Preallocation mode (allowed values: off, falloc, full)" + }, + { /* end of list */ } + } +}; + +BlockDriver bdrv_file = { + .format_name = "file", + .protocol_name = "file", + .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, + .bdrv_probe = NULL, /* no probe for protocols */ + .bdrv_parse_filename = raw_parse_filename, + .bdrv_file_open = raw_open, + .bdrv_reopen_prepare = raw_reopen_prepare, + .bdrv_reopen_commit = raw_reopen_commit, + .bdrv_reopen_abort = raw_reopen_abort, + .bdrv_close = raw_close, + .bdrv_create = raw_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_co_get_block_status = raw_co_get_block_status, + .bdrv_co_write_zeroes = raw_co_write_zeroes, + + .bdrv_aio_readv = raw_aio_readv, + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + .bdrv_aio_discard = raw_aio_discard, + .bdrv_refresh_limits = raw_refresh_limits, + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + .bdrv_flush_io_queue = raw_aio_flush_io_queue, + + .bdrv_truncate = raw_truncate, + .bdrv_getlength = raw_getlength, + .bdrv_get_info = raw_get_info, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, + + .bdrv_detach_aio_context = raw_detach_aio_context, + .bdrv_attach_aio_context = raw_attach_aio_context, + + .create_opts = &raw_create_opts, +}; + +/***********************************************/ +/* host device */ + +#if defined(__APPLE__) && defined(__MACH__) +static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ); +static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ); + +kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) +{ + kern_return_t kernResult; + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + + kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); + if ( KERN_SUCCESS != kernResult ) { + printf( "IOMasterPort returned %d\n", kernResult ); + } + + classesToMatch = IOServiceMatching( kIOCDMediaClass ); + if ( classesToMatch == NULL ) { + printf( "IOServiceMatching returned a NULL dictionary.\n" ); + } else { + CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); + } + kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator ); + if ( KERN_SUCCESS != kernResult ) + { + printf( "IOServiceGetMatchingServices returned %d\n", kernResult ); + } + + return kernResult; +} + +kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ) +{ + io_object_t nextMedia; + kern_return_t kernResult = KERN_FAILURE; + *bsdPath = '\0'; + nextMedia = IOIteratorNext( mediaIterator ); + if ( nextMedia ) + { + CFTypeRef bsdPathAsCFString; + bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); + if ( bsdPathAsCFString ) { + size_t devPathLength; + strcpy( bsdPath, _PATH_DEV ); + strcat( bsdPath, "r" ); + devPathLength = strlen( bsdPath ); + if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { + kernResult = KERN_SUCCESS; + } + CFRelease( bsdPathAsCFString ); + } + IOObjectRelease( nextMedia ); + } + + return kernResult; +} + +#endif + +static int hdev_probe_device(const char *filename) +{ + struct stat st; + + /* allow a dedicated CD-ROM driver to match with a higher priority */ + if (strstart(filename, "/dev/cdrom", NULL)) + return 50; + + if (stat(filename, &st) >= 0 && + (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { + return 100; + } + + return 0; +} + +static int check_hdev_writable(BDRVRawState *s) +{ +#if defined(BLKROGET) + /* Linux block devices can be configured "read-only" using blockdev(8). + * This is independent of device node permissions and therefore open(2) + * with O_RDWR succeeds. Actual writes fail with EPERM. + * + * bdrv_open() is supposed to fail if the disk is read-only. Explicitly + * check for read-only block devices so that Linux block devices behave + * properly. + */ + struct stat st; + int readonly = 0; + + if (fstat(s->fd, &st)) { + return -errno; + } + + if (!S_ISBLK(st.st_mode)) { + return 0; + } + + if (ioctl(s->fd, BLKROGET, &readonly) < 0) { + return -errno; + } + + if (readonly) { + return -EACCES; + } +#endif /* defined(BLKROGET) */ + return 0; +} + +static void hdev_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + /* The prefix is optional, just as for "file". */ + strstart(filename, "host_device:", &filename); + + qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); +} + +static int hdev_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + Error *local_err = NULL; + int ret; + const char *filename = qdict_get_str(options, "filename"); + +#if defined(__APPLE__) && defined(__MACH__) + if (strstart(filename, "/dev/cdrom", NULL)) { + kern_return_t kernResult; + io_iterator_t mediaIterator; + char bsdPath[ MAXPATHLEN ]; + int fd; + + kernResult = FindEjectableCDMedia( &mediaIterator ); + kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) ); + + if ( bsdPath[ 0 ] != '\0' ) { + strcat(bsdPath,"s0"); + /* some CDs don't have a partition 0 */ + fd = qemu_open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) { + bsdPath[strlen(bsdPath)-1] = '1'; + } else { + qemu_close(fd); + } + filename = bsdPath; + qdict_put(options, "filename", qstring_from_str(filename)); + } + + if ( mediaIterator ) + IOObjectRelease( mediaIterator ); + } +#endif + + s->type = FTYPE_FILE; +#if defined(__linux__) + { + char resolved_path[ MAXPATHLEN ], *temp; + + temp = realpath(filename, resolved_path); + if (temp && strstart(temp, "/dev/sg", NULL)) { + bs->sg = 1; + } + } +#endif + + ret = raw_open_common(bs, options, flags, 0, &local_err); + if (ret < 0) { + if (local_err) { + error_propagate(errp, local_err); + } + return ret; + } + + if (flags & BDRV_O_RDWR) { + ret = check_hdev_writable(s); + if (ret < 0) { + raw_close(bs); + error_setg_errno(errp, -ret, "The device is not writable"); + return ret; + } + } + + return ret; +} + +#if defined(__linux__) +/* Note: we do not have a reliable method to detect if the floppy is + present. The current method is to try to open the floppy at every + I/O and to keep it opened during a few hundreds of ms. */ +static int fd_open(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int last_media_present; + + if (s->type != FTYPE_FD) + return 0; + last_media_present = (s->fd >= 0); + if (s->fd >= 0 && + (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { + qemu_close(s->fd); + s->fd = -1; +#ifdef DEBUG_FLOPPY + printf("Floppy closed\n"); +#endif + } + if (s->fd < 0) { + if (s->fd_got_error && + (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_error_time) < FD_OPEN_TIMEOUT) { +#ifdef DEBUG_FLOPPY + printf("No floppy (open delayed)\n"); +#endif + return -EIO; + } + s->fd = qemu_open(bs->filename, s->open_flags & ~O_NONBLOCK); + if (s->fd < 0) { + s->fd_error_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + s->fd_got_error = 1; + if (last_media_present) + s->fd_media_changed = 1; +#ifdef DEBUG_FLOPPY + printf("No floppy\n"); +#endif + return -EIO; + } +#ifdef DEBUG_FLOPPY + printf("Floppy opened\n"); +#endif + } + if (!last_media_present) + s->fd_media_changed = 1; + s->fd_open_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + s->fd_got_error = 0; + return 0; +} + +static int hdev_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +{ + BDRVRawState *s = bs->opaque; + + return ioctl(s->fd, req, buf); +} + +static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, + unsigned long int req, void *buf, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + RawPosixAIOData *acb; + ThreadPool *pool; + + if (fd_open(bs) < 0) + return NULL; + + acb = g_slice_new(RawPosixAIOData); + acb->bs = bs; + acb->aio_type = QEMU_AIO_IOCTL; + acb->aio_fildes = s->fd; + acb->aio_offset = 0; + acb->aio_ioctl_buf = buf; + acb->aio_ioctl_cmd = req; + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); +} + +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +static int fd_open(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + + /* this is just to ensure s->fd is sane (its called by io ops) */ + if (s->fd >= 0) + return 0; + return -EIO; +} +#else /* !linux && !FreeBSD */ + +static int fd_open(BlockDriverState *bs) +{ + return 0; +} + +#endif /* !linux && !FreeBSD */ + +static coroutine_fn BlockAIOCB *hdev_aio_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + + if (fd_open(bs) < 0) { + return NULL; + } + return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors, + cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); +} + +static coroutine_fn int hdev_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) +{ + BDRVRawState *s = bs->opaque; + int rc; + + rc = fd_open(bs); + if (rc < 0) { + return rc; + } + if (!(flags & BDRV_REQ_MAY_UNMAP)) { + return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors, + QEMU_AIO_WRITE_ZEROES|QEMU_AIO_BLKDEV); + } else if (s->discard_zeroes) { + return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors, + QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); + } + return -ENOTSUP; +} + +static int hdev_create(const char *filename, QemuOpts *opts, + Error **errp) +{ + int fd; + int ret = 0; + struct stat stat_buf; + int64_t total_size = 0; + bool has_prefix; + + /* This function is used by all three protocol block drivers and therefore + * any of these three prefixes may be given. + * The return value has to be stored somewhere, otherwise this is an error + * due to -Werror=unused-value. */ + has_prefix = + strstart(filename, "host_device:", &filename) || + strstart(filename, "host_cdrom:" , &filename) || + strstart(filename, "host_floppy:", &filename); + + (void)has_prefix; + + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + + fd = qemu_open(filename, O_WRONLY | O_BINARY); + if (fd < 0) { + ret = -errno; + error_setg_errno(errp, -ret, "Could not open device"); + return ret; + } + + if (fstat(fd, &stat_buf) < 0) { + ret = -errno; + error_setg_errno(errp, -ret, "Could not stat device"); + } else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) { + error_setg(errp, + "The given file is neither a block nor a character device"); + ret = -ENODEV; + } else if (lseek(fd, 0, SEEK_END) < total_size) { + error_setg(errp, "Device is too small"); + ret = -ENOSPC; + } + + qemu_close(fd); + return ret; +} + +static BlockDriver bdrv_host_device = { + .format_name = "host_device", + .protocol_name = "host_device", + .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, + .bdrv_probe_device = hdev_probe_device, + .bdrv_parse_filename = hdev_parse_filename, + .bdrv_file_open = hdev_open, + .bdrv_close = raw_close, + .bdrv_reopen_prepare = raw_reopen_prepare, + .bdrv_reopen_commit = raw_reopen_commit, + .bdrv_reopen_abort = raw_reopen_abort, + .bdrv_create = hdev_create, + .create_opts = &raw_create_opts, + .bdrv_co_write_zeroes = hdev_co_write_zeroes, + + .bdrv_aio_readv = raw_aio_readv, + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + .bdrv_aio_discard = hdev_aio_discard, + .bdrv_refresh_limits = raw_refresh_limits, + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + .bdrv_flush_io_queue = raw_aio_flush_io_queue, + + .bdrv_truncate = raw_truncate, + .bdrv_getlength = raw_getlength, + .bdrv_get_info = raw_get_info, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, + .bdrv_probe_blocksizes = hdev_probe_blocksizes, + .bdrv_probe_geometry = hdev_probe_geometry, + + .bdrv_detach_aio_context = raw_detach_aio_context, + .bdrv_attach_aio_context = raw_attach_aio_context, + + /* generic scsi device */ +#ifdef __linux__ + .bdrv_ioctl = hdev_ioctl, + .bdrv_aio_ioctl = hdev_aio_ioctl, +#endif +}; + +#ifdef __linux__ +static void floppy_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + /* The prefix is optional, just as for "file". */ + strstart(filename, "host_floppy:", &filename); + + qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); +} + +static int floppy_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + Error *local_err = NULL; + int ret; + + s->type = FTYPE_FD; + + /* open will not fail even if no floppy is inserted, so add O_NONBLOCK */ + ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err); + if (ret) { + if (local_err) { + error_propagate(errp, local_err); + } + return ret; + } + + /* close fd so that we can reopen it as needed */ + qemu_close(s->fd); + s->fd = -1; + s->fd_media_changed = 1; + + error_report("Host floppy pass-through is deprecated"); + error_printf("Support for it will be removed in a future release.\n"); + return 0; +} + +static int floppy_probe_device(const char *filename) +{ + int fd, ret; + int prio = 0; + struct floppy_struct fdparam; + struct stat st; + + if (strstart(filename, "/dev/fd", NULL) && + !strstart(filename, "/dev/fdset/", NULL)) { + prio = 50; + } + + fd = qemu_open(filename, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + goto out; + } + ret = fstat(fd, &st); + if (ret == -1 || !S_ISBLK(st.st_mode)) { + goto outc; + } + + /* Attempt to detect via a floppy specific ioctl */ + ret = ioctl(fd, FDGETPRM, &fdparam); + if (ret >= 0) + prio = 100; + +outc: + qemu_close(fd); +out: + return prio; +} + + +static int floppy_is_inserted(BlockDriverState *bs) +{ + return fd_open(bs) >= 0; +} + +static int floppy_media_changed(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int ret; + + /* + * XXX: we do not have a true media changed indication. + * It does not work if the floppy is changed without trying to read it. + */ + fd_open(bs); + ret = s->fd_media_changed; + s->fd_media_changed = 0; +#ifdef DEBUG_FLOPPY + printf("Floppy changed=%d\n", ret); +#endif + return ret; +} + +static void floppy_eject(BlockDriverState *bs, bool eject_flag) +{ + BDRVRawState *s = bs->opaque; + int fd; + + if (s->fd >= 0) { + qemu_close(s->fd); + s->fd = -1; + } + fd = qemu_open(bs->filename, s->open_flags | O_NONBLOCK); + if (fd >= 0) { + if (ioctl(fd, FDEJECT, 0) < 0) + perror("FDEJECT"); + qemu_close(fd); + } +} + +static BlockDriver bdrv_host_floppy = { + .format_name = "host_floppy", + .protocol_name = "host_floppy", + .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, + .bdrv_probe_device = floppy_probe_device, + .bdrv_parse_filename = floppy_parse_filename, + .bdrv_file_open = floppy_open, + .bdrv_close = raw_close, + .bdrv_reopen_prepare = raw_reopen_prepare, + .bdrv_reopen_commit = raw_reopen_commit, + .bdrv_reopen_abort = raw_reopen_abort, + .bdrv_create = hdev_create, + .create_opts = &raw_create_opts, + + .bdrv_aio_readv = raw_aio_readv, + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + .bdrv_refresh_limits = raw_refresh_limits, + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + .bdrv_flush_io_queue = raw_aio_flush_io_queue, + + .bdrv_truncate = raw_truncate, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, + + .bdrv_detach_aio_context = raw_detach_aio_context, + .bdrv_attach_aio_context = raw_attach_aio_context, + + /* removable device support */ + .bdrv_is_inserted = floppy_is_inserted, + .bdrv_media_changed = floppy_media_changed, + .bdrv_eject = floppy_eject, +}; +#endif + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +static void cdrom_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + /* The prefix is optional, just as for "file". */ + strstart(filename, "host_cdrom:", &filename); + + qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); +} +#endif + +#ifdef __linux__ +static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + Error *local_err = NULL; + int ret; + + s->type = FTYPE_CD; + + /* open will not fail even if no CD is inserted, so add O_NONBLOCK */ + ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err); + if (local_err) { + error_propagate(errp, local_err); + } + return ret; +} + +static int cdrom_probe_device(const char *filename) +{ + int fd, ret; + int prio = 0; + struct stat st; + + fd = qemu_open(filename, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + goto out; + } + ret = fstat(fd, &st); + if (ret == -1 || !S_ISBLK(st.st_mode)) { + goto outc; + } + + /* Attempt to detect via a CDROM specific ioctl */ + ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); + if (ret >= 0) + prio = 100; + +outc: + qemu_close(fd); +out: + return prio; +} + +static int cdrom_is_inserted(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int ret; + + ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); + if (ret == CDS_DISC_OK) + return 1; + return 0; +} + +static void cdrom_eject(BlockDriverState *bs, bool eject_flag) +{ + BDRVRawState *s = bs->opaque; + + if (eject_flag) { + if (ioctl(s->fd, CDROMEJECT, NULL) < 0) + perror("CDROMEJECT"); + } else { + if (ioctl(s->fd, CDROMCLOSETRAY, NULL) < 0) + perror("CDROMEJECT"); + } +} + +static void cdrom_lock_medium(BlockDriverState *bs, bool locked) +{ + BDRVRawState *s = bs->opaque; + + if (ioctl(s->fd, CDROM_LOCKDOOR, locked) < 0) { + /* + * Note: an error can happen if the distribution automatically + * mounts the CD-ROM + */ + /* perror("CDROM_LOCKDOOR"); */ + } +} + +static BlockDriver bdrv_host_cdrom = { + .format_name = "host_cdrom", + .protocol_name = "host_cdrom", + .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, + .bdrv_probe_device = cdrom_probe_device, + .bdrv_parse_filename = cdrom_parse_filename, + .bdrv_file_open = cdrom_open, + .bdrv_close = raw_close, + .bdrv_reopen_prepare = raw_reopen_prepare, + .bdrv_reopen_commit = raw_reopen_commit, + .bdrv_reopen_abort = raw_reopen_abort, + .bdrv_create = hdev_create, + .create_opts = &raw_create_opts, + + .bdrv_aio_readv = raw_aio_readv, + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + .bdrv_refresh_limits = raw_refresh_limits, + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + .bdrv_flush_io_queue = raw_aio_flush_io_queue, + + .bdrv_truncate = raw_truncate, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, + + .bdrv_detach_aio_context = raw_detach_aio_context, + .bdrv_attach_aio_context = raw_attach_aio_context, + + /* removable device support */ + .bdrv_is_inserted = cdrom_is_inserted, + .bdrv_eject = cdrom_eject, + .bdrv_lock_medium = cdrom_lock_medium, + + /* generic scsi device */ + .bdrv_ioctl = hdev_ioctl, + .bdrv_aio_ioctl = hdev_aio_ioctl, +}; +#endif /* __linux__ */ + +#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) +static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + Error *local_err = NULL; + int ret; + + s->type = FTYPE_CD; + + ret = raw_open_common(bs, options, flags, 0, &local_err); + if (ret) { + if (local_err) { + error_propagate(errp, local_err); + } + return ret; + } + + /* make sure the door isn't locked at this time */ + ioctl(s->fd, CDIOCALLOW); + return 0; +} + +static int cdrom_probe_device(const char *filename) +{ + if (strstart(filename, "/dev/cd", NULL) || + strstart(filename, "/dev/acd", NULL)) + return 100; + return 0; +} + +static int cdrom_reopen(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int fd; + + /* + * Force reread of possibly changed/newly loaded disc, + * FreeBSD seems to not notice sometimes... + */ + if (s->fd >= 0) + qemu_close(s->fd); + fd = qemu_open(bs->filename, s->open_flags, 0644); + if (fd < 0) { + s->fd = -1; + return -EIO; + } + s->fd = fd; + + /* make sure the door isn't locked at this time */ + ioctl(s->fd, CDIOCALLOW); + return 0; +} + +static int cdrom_is_inserted(BlockDriverState *bs) +{ + return raw_getlength(bs) > 0; +} + +static void cdrom_eject(BlockDriverState *bs, bool eject_flag) +{ + BDRVRawState *s = bs->opaque; + + if (s->fd < 0) + return; + + (void) ioctl(s->fd, CDIOCALLOW); + + if (eject_flag) { + if (ioctl(s->fd, CDIOCEJECT) < 0) + perror("CDIOCEJECT"); + } else { + if (ioctl(s->fd, CDIOCCLOSE) < 0) + perror("CDIOCCLOSE"); + } + + cdrom_reopen(bs); +} + +static void cdrom_lock_medium(BlockDriverState *bs, bool locked) +{ + BDRVRawState *s = bs->opaque; + + if (s->fd < 0) + return; + if (ioctl(s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) { + /* + * Note: an error can happen if the distribution automatically + * mounts the CD-ROM + */ + /* perror("CDROM_LOCKDOOR"); */ + } +} + +static BlockDriver bdrv_host_cdrom = { + .format_name = "host_cdrom", + .protocol_name = "host_cdrom", + .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, + .bdrv_probe_device = cdrom_probe_device, + .bdrv_parse_filename = cdrom_parse_filename, + .bdrv_file_open = cdrom_open, + .bdrv_close = raw_close, + .bdrv_reopen_prepare = raw_reopen_prepare, + .bdrv_reopen_commit = raw_reopen_commit, + .bdrv_reopen_abort = raw_reopen_abort, + .bdrv_create = hdev_create, + .create_opts = &raw_create_opts, + + .bdrv_aio_readv = raw_aio_readv, + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + .bdrv_refresh_limits = raw_refresh_limits, + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + .bdrv_flush_io_queue = raw_aio_flush_io_queue, + + .bdrv_truncate = raw_truncate, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, + + .bdrv_detach_aio_context = raw_detach_aio_context, + .bdrv_attach_aio_context = raw_attach_aio_context, + + /* removable device support */ + .bdrv_is_inserted = cdrom_is_inserted, + .bdrv_eject = cdrom_eject, + .bdrv_lock_medium = cdrom_lock_medium, +}; +#endif /* __FreeBSD__ */ + +static void bdrv_file_init(void) +{ + /* + * Register all the drivers. Note that order is important, the driver + * registered last will get probed first. + */ + bdrv_register(&bdrv_file); + bdrv_register(&bdrv_host_device); +#ifdef __linux__ + bdrv_register(&bdrv_host_floppy); + bdrv_register(&bdrv_host_cdrom); +#endif +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + bdrv_register(&bdrv_host_cdrom); +#endif +} + +block_init(bdrv_file_init); diff --git a/block/raw-win32.c b/block/raw-win32.c new file mode 100644 index 0000000..dae5d2f --- /dev/null +++ b/block/raw-win32.c @@ -0,0 +1,728 @@ +/* + * Block driver for RAW files (win32) + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "qemu/timer.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "raw-aio.h" +#include "trace.h" +#include "block/thread-pool.h" +#include "qemu/iov.h" +#include +#include + +#define FTYPE_FILE 0 +#define FTYPE_CD 1 +#define FTYPE_HARDDISK 2 + +typedef struct RawWin32AIOData { + BlockDriverState *bs; + HANDLE hfile; + struct iovec *aio_iov; + int aio_niov; + size_t aio_nbytes; + off64_t aio_offset; + int aio_type; +} RawWin32AIOData; + +typedef struct BDRVRawState { + HANDLE hfile; + int type; + char drive_path[16]; /* format: "d:\" */ + QEMUWin32AIOState *aio; +} BDRVRawState; + +/* + * Read/writes the data to/from a given linear buffer. + * + * Returns the number of bytes handles or -errno in case of an error. Short + * reads are only returned if the end of the file is reached. + */ +static size_t handle_aiocb_rw(RawWin32AIOData *aiocb) +{ + size_t offset = 0; + int i; + + for (i = 0; i < aiocb->aio_niov; i++) { + OVERLAPPED ov; + DWORD ret, ret_count, len; + + memset(&ov, 0, sizeof(ov)); + ov.Offset = (aiocb->aio_offset + offset); + ov.OffsetHigh = (aiocb->aio_offset + offset) >> 32; + len = aiocb->aio_iov[i].iov_len; + if (aiocb->aio_type & QEMU_AIO_WRITE) { + ret = WriteFile(aiocb->hfile, aiocb->aio_iov[i].iov_base, + len, &ret_count, &ov); + } else { + ret = ReadFile(aiocb->hfile, aiocb->aio_iov[i].iov_base, + len, &ret_count, &ov); + } + if (!ret) { + ret_count = 0; + } + if (ret_count != len) { + offset += ret_count; + break; + } + offset += len; + } + + return offset; +} + +static int aio_worker(void *arg) +{ + RawWin32AIOData *aiocb = arg; + ssize_t ret = 0; + size_t count; + + switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { + case QEMU_AIO_READ: + count = handle_aiocb_rw(aiocb); + if (count < aiocb->aio_nbytes) { + /* A short read means that we have reached EOF. Pad the buffer + * with zeros for bytes after EOF. */ + iov_memset(aiocb->aio_iov, aiocb->aio_niov, count, + 0, aiocb->aio_nbytes - count); + + count = aiocb->aio_nbytes; + } + if (count == aiocb->aio_nbytes) { + ret = 0; + } else { + ret = -EINVAL; + } + break; + case QEMU_AIO_WRITE: + count = handle_aiocb_rw(aiocb); + if (count == aiocb->aio_nbytes) { + count = 0; + } else { + count = -EINVAL; + } + break; + case QEMU_AIO_FLUSH: + if (!FlushFileBuffers(aiocb->hfile)) { + return -EIO; + } + break; + default: + fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); + ret = -EINVAL; + break; + } + + g_slice_free(RawWin32AIOData, aiocb); + return ret; +} + +static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, int type) +{ + RawWin32AIOData *acb = g_slice_new(RawWin32AIOData); + ThreadPool *pool; + + acb->bs = bs; + acb->hfile = hfile; + acb->aio_type = type; + + if (qiov) { + acb->aio_iov = qiov->iov; + acb->aio_niov = qiov->niov; + } + acb->aio_nbytes = nb_sectors * 512; + acb->aio_offset = sector_num * 512; + + trace_paio_submit(acb, opaque, sector_num, nb_sectors, type); + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); +} + +int qemu_ftruncate64(int fd, int64_t length) +{ + LARGE_INTEGER li; + DWORD dw; + LONG high; + HANDLE h; + BOOL res; + + if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0) + return -1; + + h = (HANDLE)_get_osfhandle(fd); + + /* get current position, ftruncate do not change position */ + li.HighPart = 0; + li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT); + if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + return -1; + } + + high = length >> 32; + dw = SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN); + if (dw == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + return -1; + } + res = SetEndOfFile(h); + + /* back to old position */ + SetFilePointer(h, li.LowPart, &li.HighPart, FILE_BEGIN); + return res ? 0 : -1; +} + +static int set_sparse(int fd) +{ + DWORD returned; + return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE, + NULL, 0, NULL, 0, &returned, NULL); +} + +static void raw_detach_aio_context(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + + if (s->aio) { + win32_aio_detach_aio_context(s->aio, bdrv_get_aio_context(bs)); + } +} + +static void raw_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVRawState *s = bs->opaque; + + if (s->aio) { + win32_aio_attach_aio_context(s->aio, new_context); + } +} + +static void raw_probe_alignment(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + DWORD sectorsPerCluster, freeClusters, totalClusters, count; + DISK_GEOMETRY_EX dg; + BOOL status; + + if (s->type == FTYPE_CD) { + bs->request_alignment = 2048; + return; + } + if (s->type == FTYPE_HARDDISK) { + status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, + NULL, 0, &dg, sizeof(dg), &count, NULL); + if (status != 0) { + bs->request_alignment = dg.Geometry.BytesPerSector; + return; + } + /* try GetDiskFreeSpace too */ + } + + if (s->drive_path[0]) { + GetDiskFreeSpace(s->drive_path, §orsPerCluster, + &dg.Geometry.BytesPerSector, + &freeClusters, &totalClusters); + bs->request_alignment = dg.Geometry.BytesPerSector; + } +} + +static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped) +{ + assert(access_flags != NULL); + assert(overlapped != NULL); + + if (flags & BDRV_O_RDWR) { + *access_flags = GENERIC_READ | GENERIC_WRITE; + } else { + *access_flags = GENERIC_READ; + } + + *overlapped = FILE_ATTRIBUTE_NORMAL; + if (flags & BDRV_O_NATIVE_AIO) { + *overlapped |= FILE_FLAG_OVERLAPPED; + } + if (flags & BDRV_O_NOCACHE) { + *overlapped |= FILE_FLAG_NO_BUFFERING; + } +} + +static void raw_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + /* The filename does not have to be prefixed by the protocol name, since + * "file" is the default protocol; therefore, the return value of this + * function call can be ignored. */ + strstart(filename, "file:", &filename); + + qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); +} + +static QemuOptsList raw_runtime_opts = { + .name = "raw", + .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "File name of the image", + }, + { /* end of list */ } + }, +}; + +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + int access_flags; + DWORD overlapped; + QemuOpts *opts; + Error *local_err = NULL; + const char *filename; + int ret; + + s->type = FTYPE_FILE; + + opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + filename = qemu_opt_get(opts, "filename"); + + raw_parse_flags(flags, &access_flags, &overlapped); + + if (filename[0] && filename[1] == ':') { + snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", filename[0]); + } else if (filename[0] == '\\' && filename[1] == '\\') { + s->drive_path[0] = 0; + } else { + /* Relative path. */ + char buf[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, buf); + snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", buf[0]); + } + + s->hfile = CreateFile(filename, access_flags, + FILE_SHARE_READ, NULL, + OPEN_EXISTING, overlapped, NULL); + if (s->hfile == INVALID_HANDLE_VALUE) { + int err = GetLastError(); + + if (err == ERROR_ACCESS_DENIED) { + ret = -EACCES; + } else { + ret = -EINVAL; + } + goto fail; + } + + if (flags & BDRV_O_NATIVE_AIO) { + s->aio = win32_aio_init(); + if (s->aio == NULL) { + CloseHandle(s->hfile); + error_setg(errp, "Could not initialize AIO"); + ret = -EINVAL; + goto fail; + } + + ret = win32_aio_attach(s->aio, s->hfile); + if (ret < 0) { + win32_aio_cleanup(s->aio); + CloseHandle(s->hfile); + error_setg_errno(errp, -ret, "Could not enable AIO"); + goto fail; + } + + win32_aio_attach_aio_context(s->aio, bdrv_get_aio_context(bs)); + } + + raw_probe_alignment(bs); + ret = 0; +fail: + qemu_opts_del(opts); + return ret; +} + +static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + if (s->aio) { + return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, + nb_sectors, cb, opaque, QEMU_AIO_READ); + } else { + return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors, + cb, opaque, QEMU_AIO_READ); + } +} + +static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + if (s->aio) { + return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, + nb_sectors, cb, opaque, QEMU_AIO_WRITE); + } else { + return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors, + cb, opaque, QEMU_AIO_WRITE); + } +} + +static BlockAIOCB *raw_aio_flush(BlockDriverState *bs, + BlockCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + return paio_submit(bs, s->hfile, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH); +} + +static void raw_close(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + + if (s->aio) { + win32_aio_detach_aio_context(s->aio, bdrv_get_aio_context(bs)); + win32_aio_cleanup(s->aio); + s->aio = NULL; + } + + CloseHandle(s->hfile); + if (bs->open_flags & BDRV_O_TEMPORARY) { + unlink(bs->filename); + } +} + +static int raw_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVRawState *s = bs->opaque; + LONG low, high; + DWORD dwPtrLow; + + low = offset; + high = offset >> 32; + + /* + * An error has occurred if the return value is INVALID_SET_FILE_POINTER + * and GetLastError doesn't return NO_ERROR. + */ + dwPtrLow = SetFilePointer(s->hfile, low, &high, FILE_BEGIN); + if (dwPtrLow == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + fprintf(stderr, "SetFilePointer error: %lu\n", GetLastError()); + return -EIO; + } + if (SetEndOfFile(s->hfile) == 0) { + fprintf(stderr, "SetEndOfFile error: %lu\n", GetLastError()); + return -EIO; + } + return 0; +} + +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + LARGE_INTEGER l; + ULARGE_INTEGER available, total, total_free; + DISK_GEOMETRY_EX dg; + DWORD count; + BOOL status; + + switch(s->type) { + case FTYPE_FILE: + l.LowPart = GetFileSize(s->hfile, (PDWORD)&l.HighPart); + if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR) + return -EIO; + break; + case FTYPE_CD: + if (!GetDiskFreeSpaceEx(s->drive_path, &available, &total, &total_free)) + return -EIO; + l.QuadPart = total.QuadPart; + break; + case FTYPE_HARDDISK: + status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, + NULL, 0, &dg, sizeof(dg), &count, NULL); + if (status != 0) { + l = dg.DiskSize; + } + break; + default: + return -EIO; + } + return l.QuadPart; +} + +static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +{ + typedef DWORD (WINAPI * get_compressed_t)(const char *filename, + DWORD * high); + get_compressed_t get_compressed; + struct _stati64 st; + const char *filename = bs->filename; + /* WinNT support GetCompressedFileSize to determine allocate size */ + get_compressed = + (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), + "GetCompressedFileSizeA"); + if (get_compressed) { + DWORD high, low; + low = get_compressed(filename, &high); + if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) { + return (((int64_t) high) << 32) + low; + } + } + + if (_stati64(filename, &st) < 0) { + return -1; + } + return st.st_size; +} + +static int raw_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int fd; + int64_t total_size = 0; + + strstart(filename, "file:", &filename); + + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + + fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + 0644); + if (fd < 0) { + error_setg_errno(errp, errno, "Could not create file"); + return -EIO; + } + set_sparse(fd); + ftruncate(fd, total_size); + qemu_close(fd); + return 0; +} + + +static QemuOptsList raw_create_opts = { + .name = "raw-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { /* end of list */ } + } +}; + +BlockDriver bdrv_file = { + .format_name = "file", + .protocol_name = "file", + .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, + .bdrv_parse_filename = raw_parse_filename, + .bdrv_file_open = raw_open, + .bdrv_close = raw_close, + .bdrv_create = raw_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + + .bdrv_aio_readv = raw_aio_readv, + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + + .bdrv_truncate = raw_truncate, + .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, + + .create_opts = &raw_create_opts, +}; + +/***********************************************/ +/* host device */ + +static int find_cdrom(char *cdrom_name, int cdrom_name_size) +{ + char drives[256], *pdrv = drives; + UINT type; + + memset(drives, 0, sizeof(drives)); + GetLogicalDriveStrings(sizeof(drives), drives); + while(pdrv[0] != '\0') { + type = GetDriveType(pdrv); + switch(type) { + case DRIVE_CDROM: + snprintf(cdrom_name, cdrom_name_size, "\\\\.\\%c:", pdrv[0]); + return 0; + break; + } + pdrv += lstrlen(pdrv) + 1; + } + return -1; +} + +static int find_device_type(BlockDriverState *bs, const char *filename) +{ + BDRVRawState *s = bs->opaque; + UINT type; + const char *p; + + if (strstart(filename, "\\\\.\\", &p) || + strstart(filename, "//./", &p)) { + if (stristart(p, "PhysicalDrive", NULL)) + return FTYPE_HARDDISK; + snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", p[0]); + type = GetDriveType(s->drive_path); + switch (type) { + case DRIVE_REMOVABLE: + case DRIVE_FIXED: + return FTYPE_HARDDISK; + case DRIVE_CDROM: + return FTYPE_CD; + default: + return FTYPE_FILE; + } + } else { + return FTYPE_FILE; + } +} + +static int hdev_probe_device(const char *filename) +{ + if (strstart(filename, "/dev/cdrom", NULL)) + return 100; + if (is_windows_drive(filename)) + return 100; + return 0; +} + +static void hdev_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + /* The prefix is optional, just as for "file". */ + strstart(filename, "host_device:", &filename); + + qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); +} + +static int hdev_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + int access_flags, create_flags; + int ret = 0; + DWORD overlapped; + char device_name[64]; + + Error *local_err = NULL; + const char *filename; + + QemuOpts *opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, + &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto done; + } + + filename = qemu_opt_get(opts, "filename"); + + if (strstart(filename, "/dev/cdrom", NULL)) { + if (find_cdrom(device_name, sizeof(device_name)) < 0) { + error_setg(errp, "Could not open CD-ROM drive"); + ret = -ENOENT; + goto done; + } + filename = device_name; + } else { + /* transform drive letters into device name */ + if (((filename[0] >= 'a' && filename[0] <= 'z') || + (filename[0] >= 'A' && filename[0] <= 'Z')) && + filename[1] == ':' && filename[2] == '\0') { + snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]); + filename = device_name; + } + } + s->type = find_device_type(bs, filename); + + raw_parse_flags(flags, &access_flags, &overlapped); + + create_flags = OPEN_EXISTING; + + s->hfile = CreateFile(filename, access_flags, + FILE_SHARE_READ, NULL, + create_flags, overlapped, NULL); + if (s->hfile == INVALID_HANDLE_VALUE) { + int err = GetLastError(); + + if (err == ERROR_ACCESS_DENIED) { + ret = -EACCES; + } else { + ret = -EINVAL; + } + error_setg_errno(errp, -ret, "Could not open device"); + goto done; + } + +done: + qemu_opts_del(opts); + return ret; +} + +static BlockDriver bdrv_host_device = { + .format_name = "host_device", + .protocol_name = "host_device", + .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, + .bdrv_parse_filename = hdev_parse_filename, + .bdrv_probe_device = hdev_probe_device, + .bdrv_file_open = hdev_open, + .bdrv_close = raw_close, + + .bdrv_aio_readv = raw_aio_readv, + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + + .bdrv_detach_aio_context = raw_detach_aio_context, + .bdrv_attach_aio_context = raw_attach_aio_context, + + .bdrv_getlength = raw_getlength, + .has_variable_length = true, + + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, +}; + +static void bdrv_file_init(void) +{ + bdrv_register(&bdrv_file); + bdrv_register(&bdrv_host_device); +} + +block_init(bdrv_file_init); diff --git a/block/raw_bsd.c b/block/raw_bsd.c new file mode 100644 index 0000000..e3d2d04 --- /dev/null +++ b/block/raw_bsd.c @@ -0,0 +1,282 @@ +/* BlockDriver implementation for "raw" + * + * Copyright (C) 2010, 2013, Red Hat, Inc. + * Copyright (C) 2010, Blue Swirl + * Copyright (C) 2009, Anthony Liguori + * + * Author: + * Laszlo Ersek + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "block/block_int.h" +#include "qemu/option.h" + +static QemuOptsList raw_create_opts = { + .name = "raw-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { /* end of list */ } + } +}; + +static int raw_reopen_prepare(BDRVReopenState *reopen_state, + BlockReopenQueue *queue, Error **errp) +{ + return 0; +} + +static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov); +} + +static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + void *buf = NULL; + BlockDriver *drv; + QEMUIOVector local_qiov; + int ret; + + if (bs->probed && sector_num == 0) { + /* As long as these conditions are true, we can't get partial writes to + * the probe buffer and can just directly check the request. */ + QEMU_BUILD_BUG_ON(BLOCK_PROBE_BUF_SIZE != 512); + QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != 512); + + if (nb_sectors == 0) { + /* qemu_iovec_to_buf() would fail, but we want to return success + * instead of -EINVAL in this case. */ + return 0; + } + + buf = qemu_try_blockalign(bs->file, 512); + if (!buf) { + ret = -ENOMEM; + goto fail; + } + + ret = qemu_iovec_to_buf(qiov, 0, buf, 512); + if (ret != 512) { + ret = -EINVAL; + goto fail; + } + + drv = bdrv_probe_all(buf, 512, NULL); + if (drv != bs->drv) { + ret = -EPERM; + goto fail; + } + + /* Use the checked buffer, a malicious guest might be overwriting its + * original buffer in the background. */ + qemu_iovec_init(&local_qiov, qiov->niov + 1); + qemu_iovec_add(&local_qiov, buf, 512); + qemu_iovec_concat(&local_qiov, qiov, 512, qiov->size - 512); + qiov = &local_qiov; + } + + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + ret = bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); + +fail: + if (qiov == &local_qiov) { + qemu_iovec_destroy(&local_qiov); + } + qemu_vfree(buf); + return ret; +} + +static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + *pnum = nb_sectors; + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | + (sector_num << BDRV_SECTOR_BITS); +} + +static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + BdrvRequestFlags flags) +{ + return bdrv_co_write_zeroes(bs->file, sector_num, nb_sectors, flags); +} + +static int coroutine_fn raw_co_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors) +{ + return bdrv_co_discard(bs->file, sector_num, nb_sectors); +} + +static int64_t raw_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file); +} + +static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + return bdrv_get_info(bs->file, bdi); +} + +static void raw_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl = bs->file->bl; +} + +static int raw_truncate(BlockDriverState *bs, int64_t offset) +{ + return bdrv_truncate(bs->file, offset); +} + +static int raw_is_inserted(BlockDriverState *bs) +{ + return bdrv_is_inserted(bs->file); +} + +static int raw_media_changed(BlockDriverState *bs) +{ + return bdrv_media_changed(bs->file); +} + +static void raw_eject(BlockDriverState *bs, bool eject_flag) +{ + bdrv_eject(bs->file, eject_flag); +} + +static void raw_lock_medium(BlockDriverState *bs, bool locked) +{ + bdrv_lock_medium(bs->file, locked); +} + +static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +{ + return bdrv_ioctl(bs->file, req, buf); +} + +static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs, + unsigned long int req, void *buf, + BlockCompletionFunc *cb, + void *opaque) +{ + return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque); +} + +static int raw_has_zero_init(BlockDriverState *bs) +{ + return bdrv_has_zero_init(bs->file); +} + +static int raw_create(const char *filename, QemuOpts *opts, Error **errp) +{ + Error *local_err = NULL; + int ret; + + ret = bdrv_create_file(filename, opts, &local_err); + if (local_err) { + error_propagate(errp, local_err); + } + return ret; +} + +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + bs->sg = bs->file->sg; + + if (bs->probed && !bdrv_is_read_only(bs)) { + fprintf(stderr, + "WARNING: Image format was not specified for '%s' and probing " + "guessed raw.\n" + " Automatically detecting the format is dangerous for " + "raw images, write operations on block 0 will be restricted.\n" + " Specify the 'raw' format explicitly to remove the " + "restrictions.\n", + bs->file->filename); + } + + return 0; +} + +static void raw_close(BlockDriverState *bs) +{ +} + +static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + /* smallest possible positive score so that raw is used if and only if no + * other block driver works + */ + return 1; +} + +static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + return bdrv_probe_blocksizes(bs->file, bsz); +} + +static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + return bdrv_probe_geometry(bs->file, geo); +} + +BlockDriver bdrv_raw = { + .format_name = "raw", + .bdrv_probe = &raw_probe, + .bdrv_reopen_prepare = &raw_reopen_prepare, + .bdrv_open = &raw_open, + .bdrv_close = &raw_close, + .bdrv_create = &raw_create, + .bdrv_co_readv = &raw_co_readv, + .bdrv_co_writev = &raw_co_writev, + .bdrv_co_write_zeroes = &raw_co_write_zeroes, + .bdrv_co_discard = &raw_co_discard, + .bdrv_co_get_block_status = &raw_co_get_block_status, + .bdrv_truncate = &raw_truncate, + .bdrv_getlength = &raw_getlength, + .has_variable_length = true, + .bdrv_get_info = &raw_get_info, + .bdrv_refresh_limits = &raw_refresh_limits, + .bdrv_probe_blocksizes = &raw_probe_blocksizes, + .bdrv_probe_geometry = &raw_probe_geometry, + .bdrv_is_inserted = &raw_is_inserted, + .bdrv_media_changed = &raw_media_changed, + .bdrv_eject = &raw_eject, + .bdrv_lock_medium = &raw_lock_medium, + .bdrv_ioctl = &raw_ioctl, + .bdrv_aio_ioctl = &raw_aio_ioctl, + .create_opts = &raw_create_opts, + .bdrv_has_zero_init = &raw_has_zero_init +}; + +static void bdrv_raw_init(void) +{ + bdrv_register(&bdrv_raw); +} + +block_init(bdrv_raw_init); diff --git a/block/rbd.c b/block/rbd.c new file mode 100644 index 0000000..fbe87e0 --- /dev/null +++ b/block/rbd.c @@ -0,0 +1,961 @@ +/* + * QEMU Block driver for RADOS (Ceph) + * + * Copyright (C) 2010-2011 Christian Brunner , + * Josh Durgin + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include + +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "block/block_int.h" + +#include + +/* + * When specifying the image filename use: + * + * rbd:poolname/devicename[@snapshotname][:option1=value1[:option2=value2...]] + * + * poolname must be the name of an existing rados pool. + * + * devicename is the name of the rbd image. + * + * Each option given is used to configure rados, and may be any valid + * Ceph option, "id", or "conf". + * + * The "id" option indicates what user we should authenticate as to + * the Ceph cluster. If it is excluded we will use the Ceph default + * (normally 'admin'). + * + * The "conf" option specifies a Ceph configuration file to read. If + * it is not specified, we will read from the default Ceph locations + * (e.g., /etc/ceph/ceph.conf). To avoid reading _any_ configuration + * file, specify conf=/dev/null. + * + * Configuration values containing :, @, or = can be escaped with a + * leading "\". + */ + +/* rbd_aio_discard added in 0.1.2 */ +#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2) +#define LIBRBD_SUPPORTS_DISCARD +#else +#undef LIBRBD_SUPPORTS_DISCARD +#endif + +#define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER) + +#define RBD_MAX_CONF_NAME_SIZE 128 +#define RBD_MAX_CONF_VAL_SIZE 512 +#define RBD_MAX_CONF_SIZE 1024 +#define RBD_MAX_POOL_NAME_SIZE 128 +#define RBD_MAX_SNAP_NAME_SIZE 128 +#define RBD_MAX_SNAPS 100 + +typedef enum { + RBD_AIO_READ, + RBD_AIO_WRITE, + RBD_AIO_DISCARD, + RBD_AIO_FLUSH +} RBDAIOCmd; + +typedef struct RBDAIOCB { + BlockAIOCB common; + QEMUBH *bh; + int64_t ret; + QEMUIOVector *qiov; + char *bounce; + RBDAIOCmd cmd; + int64_t sector_num; + int error; + struct BDRVRBDState *s; + int status; +} RBDAIOCB; + +typedef struct RADOSCB { + int rcbid; + RBDAIOCB *acb; + struct BDRVRBDState *s; + int done; + int64_t size; + char *buf; + int64_t ret; +} RADOSCB; + +#define RBD_FD_READ 0 +#define RBD_FD_WRITE 1 + +typedef struct BDRVRBDState { + rados_t cluster; + rados_ioctx_t io_ctx; + rbd_image_t image; + char name[RBD_MAX_IMAGE_NAME_SIZE]; + char *snap; +} BDRVRBDState; + +static int qemu_rbd_next_tok(char *dst, int dst_len, + char *src, char delim, + const char *name, + char **p, Error **errp) +{ + int l; + char *end; + + *p = NULL; + + if (delim != '\0') { + for (end = src; *end; ++end) { + if (*end == delim) { + break; + } + if (*end == '\\' && end[1] != '\0') { + end++; + } + } + if (*end == delim) { + *p = end + 1; + *end = '\0'; + } + } + l = strlen(src); + if (l >= dst_len) { + error_setg(errp, "%s too long", name); + return -EINVAL; + } else if (l == 0) { + error_setg(errp, "%s too short", name); + return -EINVAL; + } + + pstrcpy(dst, dst_len, src); + + return 0; +} + +static void qemu_rbd_unescape(char *src) +{ + char *p; + + for (p = src; *src; ++src, ++p) { + if (*src == '\\' && src[1] != '\0') { + src++; + } + *p = *src; + } + *p = '\0'; +} + +static int qemu_rbd_parsename(const char *filename, + char *pool, int pool_len, + char *snap, int snap_len, + char *name, int name_len, + char *conf, int conf_len, + Error **errp) +{ + const char *start; + char *p, *buf; + int ret; + + if (!strstart(filename, "rbd:", &start)) { + error_setg(errp, "File name must start with 'rbd:'"); + return -EINVAL; + } + + buf = g_strdup(start); + p = buf; + *snap = '\0'; + *conf = '\0'; + + ret = qemu_rbd_next_tok(pool, pool_len, p, + '/', "pool name", &p, errp); + if (ret < 0 || !p) { + ret = -EINVAL; + goto done; + } + qemu_rbd_unescape(pool); + + if (strchr(p, '@')) { + ret = qemu_rbd_next_tok(name, name_len, p, + '@', "object name", &p, errp); + if (ret < 0) { + goto done; + } + ret = qemu_rbd_next_tok(snap, snap_len, p, + ':', "snap name", &p, errp); + qemu_rbd_unescape(snap); + } else { + ret = qemu_rbd_next_tok(name, name_len, p, + ':', "object name", &p, errp); + } + qemu_rbd_unescape(name); + if (ret < 0 || !p) { + goto done; + } + + ret = qemu_rbd_next_tok(conf, conf_len, p, + '\0', "configuration", &p, errp); + +done: + g_free(buf); + return ret; +} + +static char *qemu_rbd_parse_clientname(const char *conf, char *clientname) +{ + const char *p = conf; + + while (*p) { + int len; + const char *end = strchr(p, ':'); + + if (end) { + len = end - p; + } else { + len = strlen(p); + } + + if (strncmp(p, "id=", 3) == 0) { + len -= 3; + strncpy(clientname, p + 3, len); + clientname[len] = '\0'; + return clientname; + } + if (end == NULL) { + break; + } + p = end + 1; + } + return NULL; +} + +static int qemu_rbd_set_conf(rados_t cluster, const char *conf, Error **errp) +{ + char *p, *buf; + char name[RBD_MAX_CONF_NAME_SIZE]; + char value[RBD_MAX_CONF_VAL_SIZE]; + int ret = 0; + + buf = g_strdup(conf); + p = buf; + + while (p) { + ret = qemu_rbd_next_tok(name, sizeof(name), p, + '=', "conf option name", &p, errp); + if (ret < 0) { + break; + } + qemu_rbd_unescape(name); + + if (!p) { + error_setg(errp, "conf option %s has no value", name); + ret = -EINVAL; + break; + } + + ret = qemu_rbd_next_tok(value, sizeof(value), p, + ':', "conf option value", &p, errp); + if (ret < 0) { + break; + } + qemu_rbd_unescape(value); + + if (strcmp(name, "conf") == 0) { + ret = rados_conf_read_file(cluster, value); + if (ret < 0) { + error_setg(errp, "error reading conf file %s", value); + break; + } + } else if (strcmp(name, "id") == 0) { + /* ignore, this is parsed by qemu_rbd_parse_clientname() */ + } else { + ret = rados_conf_set(cluster, name, value); + if (ret < 0) { + error_setg(errp, "invalid conf option %s", name); + ret = -EINVAL; + break; + } + } + } + + g_free(buf); + return ret; +} + +static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) +{ + Error *local_err = NULL; + int64_t bytes = 0; + int64_t objsize; + int obj_order = 0; + char pool[RBD_MAX_POOL_NAME_SIZE]; + char name[RBD_MAX_IMAGE_NAME_SIZE]; + char snap_buf[RBD_MAX_SNAP_NAME_SIZE]; + char conf[RBD_MAX_CONF_SIZE]; + char clientname_buf[RBD_MAX_CONF_SIZE]; + char *clientname; + rados_t cluster; + rados_ioctx_t io_ctx; + int ret; + + if (qemu_rbd_parsename(filename, pool, sizeof(pool), + snap_buf, sizeof(snap_buf), + name, sizeof(name), + conf, sizeof(conf), &local_err) < 0) { + error_propagate(errp, local_err); + return -EINVAL; + } + + /* Read out options */ + bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0); + if (objsize) { + if ((objsize - 1) & objsize) { /* not a power of 2? */ + error_setg(errp, "obj size needs to be power of 2"); + return -EINVAL; + } + if (objsize < 4096) { + error_setg(errp, "obj size too small"); + return -EINVAL; + } + obj_order = ctz32(objsize); + } + + clientname = qemu_rbd_parse_clientname(conf, clientname_buf); + if (rados_create(&cluster, clientname) < 0) { + error_setg(errp, "error initializing"); + return -EIO; + } + + if (strstr(conf, "conf=") == NULL) { + /* try default location, but ignore failure */ + rados_conf_read_file(cluster, NULL); + } + + if (conf[0] != '\0' && + qemu_rbd_set_conf(cluster, conf, &local_err) < 0) { + rados_shutdown(cluster); + error_propagate(errp, local_err); + return -EIO; + } + + if (rados_connect(cluster) < 0) { + error_setg(errp, "error connecting"); + rados_shutdown(cluster); + return -EIO; + } + + if (rados_ioctx_create(cluster, pool, &io_ctx) < 0) { + error_setg(errp, "error opening pool %s", pool); + rados_shutdown(cluster); + return -EIO; + } + + ret = rbd_create(io_ctx, name, bytes, &obj_order); + rados_ioctx_destroy(io_ctx); + rados_shutdown(cluster); + + return ret; +} + +/* + * This aio completion is being called from rbd_finish_bh() and runs in qemu + * BH context. + */ +static void qemu_rbd_complete_aio(RADOSCB *rcb) +{ + RBDAIOCB *acb = rcb->acb; + int64_t r; + + r = rcb->ret; + + if (acb->cmd != RBD_AIO_READ) { + if (r < 0) { + acb->ret = r; + acb->error = 1; + } else if (!acb->error) { + acb->ret = rcb->size; + } + } else { + if (r < 0) { + memset(rcb->buf, 0, rcb->size); + acb->ret = r; + acb->error = 1; + } else if (r < rcb->size) { + memset(rcb->buf + r, 0, rcb->size - r); + if (!acb->error) { + acb->ret = rcb->size; + } + } else if (!acb->error) { + acb->ret = r; + } + } + + g_free(rcb); + + if (acb->cmd == RBD_AIO_READ) { + qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size); + } + qemu_vfree(acb->bounce); + acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); + acb->status = 0; + + qemu_aio_unref(acb); +} + +/* TODO Convert to fine grained options */ +static QemuOptsList runtime_opts = { + .name = "rbd", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "Specification of the rbd image", + }, + { /* end of list */ } + }, +}; + +static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRBDState *s = bs->opaque; + char pool[RBD_MAX_POOL_NAME_SIZE]; + char snap_buf[RBD_MAX_SNAP_NAME_SIZE]; + char conf[RBD_MAX_CONF_SIZE]; + char clientname_buf[RBD_MAX_CONF_SIZE]; + char *clientname; + QemuOpts *opts; + Error *local_err = NULL; + const char *filename; + int r; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + qemu_opts_del(opts); + return -EINVAL; + } + + filename = qemu_opt_get(opts, "filename"); + + if (qemu_rbd_parsename(filename, pool, sizeof(pool), + snap_buf, sizeof(snap_buf), + s->name, sizeof(s->name), + conf, sizeof(conf), errp) < 0) { + r = -EINVAL; + goto failed_opts; + } + + clientname = qemu_rbd_parse_clientname(conf, clientname_buf); + r = rados_create(&s->cluster, clientname); + if (r < 0) { + error_setg(errp, "error initializing"); + goto failed_opts; + } + + s->snap = NULL; + if (snap_buf[0] != '\0') { + s->snap = g_strdup(snap_buf); + } + + /* + * Fallback to more conservative semantics if setting cache + * options fails. Ignore errors from setting rbd_cache because the + * only possible error is that the option does not exist, and + * librbd defaults to no caching. If write through caching cannot + * be set up, fall back to no caching. + */ + if (flags & BDRV_O_NOCACHE) { + rados_conf_set(s->cluster, "rbd_cache", "false"); + } else { + rados_conf_set(s->cluster, "rbd_cache", "true"); + } + + if (strstr(conf, "conf=") == NULL) { + /* try default location, but ignore failure */ + rados_conf_read_file(s->cluster, NULL); + } + + if (conf[0] != '\0') { + r = qemu_rbd_set_conf(s->cluster, conf, errp); + if (r < 0) { + goto failed_shutdown; + } + } + + r = rados_connect(s->cluster); + if (r < 0) { + error_setg(errp, "error connecting"); + goto failed_shutdown; + } + + r = rados_ioctx_create(s->cluster, pool, &s->io_ctx); + if (r < 0) { + error_setg(errp, "error opening pool %s", pool); + goto failed_shutdown; + } + + r = rbd_open(s->io_ctx, s->name, &s->image, s->snap); + if (r < 0) { + error_setg(errp, "error reading header from %s", s->name); + goto failed_open; + } + + bs->read_only = (s->snap != NULL); + + qemu_opts_del(opts); + return 0; + +failed_open: + rados_ioctx_destroy(s->io_ctx); +failed_shutdown: + rados_shutdown(s->cluster); + g_free(s->snap); +failed_opts: + qemu_opts_del(opts); + return r; +} + +static void qemu_rbd_close(BlockDriverState *bs) +{ + BDRVRBDState *s = bs->opaque; + + rbd_close(s->image); + rados_ioctx_destroy(s->io_ctx); + g_free(s->snap); + rados_shutdown(s->cluster); +} + +static const AIOCBInfo rbd_aiocb_info = { + .aiocb_size = sizeof(RBDAIOCB), +}; + +static void rbd_finish_bh(void *opaque) +{ + RADOSCB *rcb = opaque; + qemu_bh_delete(rcb->acb->bh); + qemu_rbd_complete_aio(rcb); +} + +/* + * This is the callback function for rbd_aio_read and _write + * + * Note: this function is being called from a non qemu thread so + * we need to be careful about what we do here. Generally we only + * schedule a BH, and do the rest of the io completion handling + * from rbd_finish_bh() which runs in a qemu context. + */ +static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb) +{ + RBDAIOCB *acb = rcb->acb; + + rcb->ret = rbd_aio_get_return_value(c); + rbd_aio_release(c); + + acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs), + rbd_finish_bh, rcb); + qemu_bh_schedule(acb->bh); +} + +static int rbd_aio_discard_wrapper(rbd_image_t image, + uint64_t off, + uint64_t len, + rbd_completion_t comp) +{ +#ifdef LIBRBD_SUPPORTS_DISCARD + return rbd_aio_discard(image, off, len, comp); +#else + return -ENOTSUP; +#endif +} + +static int rbd_aio_flush_wrapper(rbd_image_t image, + rbd_completion_t comp) +{ +#ifdef LIBRBD_SUPPORTS_AIO_FLUSH + return rbd_aio_flush(image, comp); +#else + return -ENOTSUP; +#endif +} + +static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque, + RBDAIOCmd cmd) +{ + RBDAIOCB *acb; + RADOSCB *rcb = NULL; + rbd_completion_t c; + int64_t off, size; + char *buf; + int r; + + BDRVRBDState *s = bs->opaque; + + acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque); + acb->cmd = cmd; + acb->qiov = qiov; + if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) { + acb->bounce = NULL; + } else { + acb->bounce = qemu_try_blockalign(bs, qiov->size); + if (acb->bounce == NULL) { + goto failed; + } + } + acb->ret = 0; + acb->error = 0; + acb->s = s; + acb->bh = NULL; + acb->status = -EINPROGRESS; + + if (cmd == RBD_AIO_WRITE) { + qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size); + } + + buf = acb->bounce; + + off = sector_num * BDRV_SECTOR_SIZE; + size = nb_sectors * BDRV_SECTOR_SIZE; + + rcb = g_new(RADOSCB, 1); + rcb->done = 0; + rcb->acb = acb; + rcb->buf = buf; + rcb->s = acb->s; + rcb->size = size; + r = rbd_aio_create_completion(rcb, (rbd_callback_t) rbd_finish_aiocb, &c); + if (r < 0) { + goto failed; + } + + switch (cmd) { + case RBD_AIO_WRITE: + r = rbd_aio_write(s->image, off, size, buf, c); + break; + case RBD_AIO_READ: + r = rbd_aio_read(s->image, off, size, buf, c); + break; + case RBD_AIO_DISCARD: + r = rbd_aio_discard_wrapper(s->image, off, size, c); + break; + case RBD_AIO_FLUSH: + r = rbd_aio_flush_wrapper(s->image, c); + break; + default: + r = -EINVAL; + } + + if (r < 0) { + goto failed_completion; + } + + return &acb->common; + +failed_completion: + rbd_aio_release(c); +failed: + g_free(rcb); + qemu_vfree(acb->bounce); + qemu_aio_unref(acb); + return NULL; +} + +static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque, + RBD_AIO_READ); +} + +static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque, + RBD_AIO_WRITE); +} + +#ifdef LIBRBD_SUPPORTS_AIO_FLUSH +static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs, + BlockCompletionFunc *cb, + void *opaque) +{ + return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH); +} + +#else + +static int qemu_rbd_co_flush(BlockDriverState *bs) +{ +#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 1) + /* rbd_flush added in 0.1.1 */ + BDRVRBDState *s = bs->opaque; + return rbd_flush(s->image); +#else + return 0; +#endif +} +#endif + +static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVRBDState *s = bs->opaque; + rbd_image_info_t info; + int r; + + r = rbd_stat(s->image, &info, sizeof(info)); + if (r < 0) { + return r; + } + + bdi->cluster_size = info.obj_size; + return 0; +} + +static int64_t qemu_rbd_getlength(BlockDriverState *bs) +{ + BDRVRBDState *s = bs->opaque; + rbd_image_info_t info; + int r; + + r = rbd_stat(s->image, &info, sizeof(info)); + if (r < 0) { + return r; + } + + return info.size; +} + +static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVRBDState *s = bs->opaque; + int r; + + r = rbd_resize(s->image, offset); + if (r < 0) { + return r; + } + + return 0; +} + +static int qemu_rbd_snap_create(BlockDriverState *bs, + QEMUSnapshotInfo *sn_info) +{ + BDRVRBDState *s = bs->opaque; + int r; + + if (sn_info->name[0] == '\0') { + return -EINVAL; /* we need a name for rbd snapshots */ + } + + /* + * rbd snapshots are using the name as the user controlled unique identifier + * we can't use the rbd snapid for that purpose, as it can't be set + */ + if (sn_info->id_str[0] != '\0' && + strcmp(sn_info->id_str, sn_info->name) != 0) { + return -EINVAL; + } + + if (strlen(sn_info->name) >= sizeof(sn_info->id_str)) { + return -ERANGE; + } + + r = rbd_snap_create(s->image, sn_info->name); + if (r < 0) { + error_report("failed to create snap: %s", strerror(-r)); + return r; + } + + return 0; +} + +static int qemu_rbd_snap_remove(BlockDriverState *bs, + const char *snapshot_id, + const char *snapshot_name, + Error **errp) +{ + BDRVRBDState *s = bs->opaque; + int r; + + if (!snapshot_name) { + error_setg(errp, "rbd need a valid snapshot name"); + return -EINVAL; + } + + /* If snapshot_id is specified, it must be equal to name, see + qemu_rbd_snap_list() */ + if (snapshot_id && strcmp(snapshot_id, snapshot_name)) { + error_setg(errp, + "rbd do not support snapshot id, it should be NULL or " + "equal to snapshot name"); + return -EINVAL; + } + + r = rbd_snap_remove(s->image, snapshot_name); + if (r < 0) { + error_setg_errno(errp, -r, "Failed to remove the snapshot"); + } + return r; +} + +static int qemu_rbd_snap_rollback(BlockDriverState *bs, + const char *snapshot_name) +{ + BDRVRBDState *s = bs->opaque; + int r; + + r = rbd_snap_rollback(s->image, snapshot_name); + return r; +} + +static int qemu_rbd_snap_list(BlockDriverState *bs, + QEMUSnapshotInfo **psn_tab) +{ + BDRVRBDState *s = bs->opaque; + QEMUSnapshotInfo *sn_info, *sn_tab = NULL; + int i, snap_count; + rbd_snap_info_t *snaps; + int max_snaps = RBD_MAX_SNAPS; + + do { + snaps = g_new(rbd_snap_info_t, max_snaps); + snap_count = rbd_snap_list(s->image, snaps, &max_snaps); + if (snap_count <= 0) { + g_free(snaps); + } + } while (snap_count == -ERANGE); + + if (snap_count <= 0) { + goto done; + } + + sn_tab = g_new0(QEMUSnapshotInfo, snap_count); + + for (i = 0; i < snap_count; i++) { + const char *snap_name = snaps[i].name; + + sn_info = sn_tab + i; + pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), snap_name); + pstrcpy(sn_info->name, sizeof(sn_info->name), snap_name); + + sn_info->vm_state_size = snaps[i].size; + sn_info->date_sec = 0; + sn_info->date_nsec = 0; + sn_info->vm_clock_nsec = 0; + } + rbd_snap_list_end(snaps); + g_free(snaps); + + done: + *psn_tab = sn_tab; + return snap_count; +} + +#ifdef LIBRBD_SUPPORTS_DISCARD +static BlockAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, + BlockCompletionFunc *cb, + void *opaque) +{ + return rbd_start_aio(bs, sector_num, NULL, nb_sectors, cb, opaque, + RBD_AIO_DISCARD); +} +#endif + +#ifdef LIBRBD_SUPPORTS_INVALIDATE +static void qemu_rbd_invalidate_cache(BlockDriverState *bs, + Error **errp) +{ + BDRVRBDState *s = bs->opaque; + int r = rbd_invalidate_cache(s->image); + if (r < 0) { + error_setg_errno(errp, -r, "Failed to invalidate the cache"); + } +} +#endif + +static QemuOptsList qemu_rbd_create_opts = { + .name = "rbd-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(qemu_rbd_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_CLUSTER_SIZE, + .type = QEMU_OPT_SIZE, + .help = "RBD object size" + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_rbd = { + .format_name = "rbd", + .instance_size = sizeof(BDRVRBDState), + .bdrv_needs_filename = true, + .bdrv_file_open = qemu_rbd_open, + .bdrv_close = qemu_rbd_close, + .bdrv_create = qemu_rbd_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_get_info = qemu_rbd_getinfo, + .create_opts = &qemu_rbd_create_opts, + .bdrv_getlength = qemu_rbd_getlength, + .bdrv_truncate = qemu_rbd_truncate, + .protocol_name = "rbd", + + .bdrv_aio_readv = qemu_rbd_aio_readv, + .bdrv_aio_writev = qemu_rbd_aio_writev, + +#ifdef LIBRBD_SUPPORTS_AIO_FLUSH + .bdrv_aio_flush = qemu_rbd_aio_flush, +#else + .bdrv_co_flush_to_disk = qemu_rbd_co_flush, +#endif + +#ifdef LIBRBD_SUPPORTS_DISCARD + .bdrv_aio_discard = qemu_rbd_aio_discard, +#endif + + .bdrv_snapshot_create = qemu_rbd_snap_create, + .bdrv_snapshot_delete = qemu_rbd_snap_remove, + .bdrv_snapshot_list = qemu_rbd_snap_list, + .bdrv_snapshot_goto = qemu_rbd_snap_rollback, +#ifdef LIBRBD_SUPPORTS_INVALIDATE + .bdrv_invalidate_cache = qemu_rbd_invalidate_cache, +#endif +}; + +static void bdrv_rbd_init(void) +{ + bdrv_register(&bdrv_rbd); +} + +block_init(bdrv_rbd_init); diff --git a/block/sheepdog.c b/block/sheepdog.c new file mode 100644 index 0000000..bd7cbed --- /dev/null +++ b/block/sheepdog.c @@ -0,0 +1,2815 @@ +/* + * Copyright (C) 2009-2010 Nippon Telegraph and Telephone Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu-common.h" +#include "qemu/uri.h" +#include "qemu/error-report.h" +#include "qemu/sockets.h" +#include "block/block_int.h" +#include "qemu/bitops.h" + +#define SD_PROTO_VER 0x01 + +#define SD_DEFAULT_ADDR "localhost" +#define SD_DEFAULT_PORT 7000 + +#define SD_OP_CREATE_AND_WRITE_OBJ 0x01 +#define SD_OP_READ_OBJ 0x02 +#define SD_OP_WRITE_OBJ 0x03 +/* 0x04 is used internally by Sheepdog */ +#define SD_OP_DISCARD_OBJ 0x05 + +#define SD_OP_NEW_VDI 0x11 +#define SD_OP_LOCK_VDI 0x12 +#define SD_OP_RELEASE_VDI 0x13 +#define SD_OP_GET_VDI_INFO 0x14 +#define SD_OP_READ_VDIS 0x15 +#define SD_OP_FLUSH_VDI 0x16 +#define SD_OP_DEL_VDI 0x17 +#define SD_OP_GET_CLUSTER_DEFAULT 0x18 + +#define SD_FLAG_CMD_WRITE 0x01 +#define SD_FLAG_CMD_COW 0x02 +#define SD_FLAG_CMD_CACHE 0x04 /* Writeback mode for cache */ +#define SD_FLAG_CMD_DIRECT 0x08 /* Don't use cache */ + +#define SD_RES_SUCCESS 0x00 /* Success */ +#define SD_RES_UNKNOWN 0x01 /* Unknown error */ +#define SD_RES_NO_OBJ 0x02 /* No object found */ +#define SD_RES_EIO 0x03 /* I/O error */ +#define SD_RES_VDI_EXIST 0x04 /* Vdi exists already */ +#define SD_RES_INVALID_PARMS 0x05 /* Invalid parameters */ +#define SD_RES_SYSTEM_ERROR 0x06 /* System error */ +#define SD_RES_VDI_LOCKED 0x07 /* Vdi is locked */ +#define SD_RES_NO_VDI 0x08 /* No vdi found */ +#define SD_RES_NO_BASE_VDI 0x09 /* No base vdi found */ +#define SD_RES_VDI_READ 0x0A /* Cannot read requested vdi */ +#define SD_RES_VDI_WRITE 0x0B /* Cannot write requested vdi */ +#define SD_RES_BASE_VDI_READ 0x0C /* Cannot read base vdi */ +#define SD_RES_BASE_VDI_WRITE 0x0D /* Cannot write base vdi */ +#define SD_RES_NO_TAG 0x0E /* Requested tag is not found */ +#define SD_RES_STARTUP 0x0F /* Sheepdog is on starting up */ +#define SD_RES_VDI_NOT_LOCKED 0x10 /* Vdi is not locked */ +#define SD_RES_SHUTDOWN 0x11 /* Sheepdog is shutting down */ +#define SD_RES_NO_MEM 0x12 /* Cannot allocate memory */ +#define SD_RES_FULL_VDI 0x13 /* we already have the maximum vdis */ +#define SD_RES_VER_MISMATCH 0x14 /* Protocol version mismatch */ +#define SD_RES_NO_SPACE 0x15 /* Server has no room for new objects */ +#define SD_RES_WAIT_FOR_FORMAT 0x16 /* Waiting for a format operation */ +#define SD_RES_WAIT_FOR_JOIN 0x17 /* Waiting for other nodes joining */ +#define SD_RES_JOIN_FAILED 0x18 /* Target node had failed to join sheepdog */ +#define SD_RES_HALT 0x19 /* Sheepdog is stopped serving IO request */ +#define SD_RES_READONLY 0x1A /* Object is read-only */ + +/* + * Object ID rules + * + * 0 - 19 (20 bits): data object space + * 20 - 31 (12 bits): reserved data object space + * 32 - 55 (24 bits): vdi object space + * 56 - 59 ( 4 bits): reserved vdi object space + * 60 - 63 ( 4 bits): object type identifier space + */ + +#define VDI_SPACE_SHIFT 32 +#define VDI_BIT (UINT64_C(1) << 63) +#define VMSTATE_BIT (UINT64_C(1) << 62) +#define MAX_DATA_OBJS (UINT64_C(1) << 20) +#define MAX_CHILDREN 1024 +#define SD_MAX_VDI_LEN 256 +#define SD_MAX_VDI_TAG_LEN 256 +#define SD_NR_VDIS (1U << 24) +#define SD_DATA_OBJ_SIZE (UINT64_C(1) << 22) +#define SD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * MAX_DATA_OBJS) +#define SD_DEFAULT_BLOCK_SIZE_SHIFT 22 +/* + * For erasure coding, we use at most SD_EC_MAX_STRIP for data strips and + * (SD_EC_MAX_STRIP - 1) for parity strips + * + * SD_MAX_COPIES is sum of number of data strips and parity strips. + */ +#define SD_EC_MAX_STRIP 16 +#define SD_MAX_COPIES (SD_EC_MAX_STRIP * 2 - 1) + +#define SD_INODE_SIZE (sizeof(SheepdogInode)) +#define CURRENT_VDI_ID 0 + +#define LOCK_TYPE_NORMAL 0 +#define LOCK_TYPE_SHARED 1 /* for iSCSI multipath */ + +typedef struct SheepdogReq { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t opcode_specific[8]; +} SheepdogReq; + +typedef struct SheepdogRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint32_t opcode_specific[7]; +} SheepdogRsp; + +typedef struct SheepdogObjReq { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint64_t oid; + uint64_t cow_oid; + uint8_t copies; + uint8_t copy_policy; + uint8_t reserved[6]; + uint64_t offset; +} SheepdogObjReq; + +typedef struct SheepdogObjRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint8_t copies; + uint8_t copy_policy; + uint8_t reserved[2]; + uint32_t pad[6]; +} SheepdogObjRsp; + +typedef struct SheepdogVdiReq { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint64_t vdi_size; + uint32_t base_vdi_id; + uint8_t copies; + uint8_t copy_policy; + uint8_t store_policy; + uint8_t block_size_shift; + uint32_t snapid; + uint32_t type; + uint32_t pad[2]; +} SheepdogVdiReq; + +typedef struct SheepdogVdiRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint32_t rsvd; + uint32_t vdi_id; + uint32_t pad[5]; +} SheepdogVdiRsp; + +typedef struct SheepdogClusterRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint8_t nr_copies; + uint8_t copy_policy; + uint8_t block_size_shift; + uint8_t __pad1; + uint32_t __pad2[6]; +} SheepdogClusterRsp; + +typedef struct SheepdogInode { + char name[SD_MAX_VDI_LEN]; + char tag[SD_MAX_VDI_TAG_LEN]; + uint64_t ctime; + uint64_t snap_ctime; + uint64_t vm_clock_nsec; + uint64_t vdi_size; + uint64_t vm_state_size; + uint16_t copy_policy; + uint8_t nr_copies; + uint8_t block_size_shift; + uint32_t snap_id; + uint32_t vdi_id; + uint32_t parent_vdi_id; + uint32_t child_vdi_id[MAX_CHILDREN]; + uint32_t data_vdi_id[MAX_DATA_OBJS]; +} SheepdogInode; + +#define SD_INODE_HEADER_SIZE offsetof(SheepdogInode, data_vdi_id) + +/* + * 64 bit FNV-1a non-zero initial basis + */ +#define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL) + +/* + * 64 bit Fowler/Noll/Vo FNV-1a hash code + */ +static inline uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval) +{ + unsigned char *bp = buf; + unsigned char *be = bp + len; + while (bp < be) { + hval ^= (uint64_t) *bp++; + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); + } + return hval; +} + +static inline bool is_data_obj_writable(SheepdogInode *inode, unsigned int idx) +{ + return inode->vdi_id == inode->data_vdi_id[idx]; +} + +static inline bool is_data_obj(uint64_t oid) +{ + return !(VDI_BIT & oid); +} + +static inline uint64_t data_oid_to_idx(uint64_t oid) +{ + return oid & (MAX_DATA_OBJS - 1); +} + +static inline uint32_t oid_to_vid(uint64_t oid) +{ + return (oid & ~VDI_BIT) >> VDI_SPACE_SHIFT; +} + +static inline uint64_t vid_to_vdi_oid(uint32_t vid) +{ + return VDI_BIT | ((uint64_t)vid << VDI_SPACE_SHIFT); +} + +static inline uint64_t vid_to_vmstate_oid(uint32_t vid, uint32_t idx) +{ + return VMSTATE_BIT | ((uint64_t)vid << VDI_SPACE_SHIFT) | idx; +} + +static inline uint64_t vid_to_data_oid(uint32_t vid, uint32_t idx) +{ + return ((uint64_t)vid << VDI_SPACE_SHIFT) | idx; +} + +static inline bool is_snapshot(struct SheepdogInode *inode) +{ + return !!inode->snap_ctime; +} + +#undef DPRINTF +#ifdef DEBUG_SDOG +#define DPRINTF(fmt, args...) \ + do { \ + fprintf(stdout, "%s %d: " fmt, __func__, __LINE__, ##args); \ + } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +typedef struct SheepdogAIOCB SheepdogAIOCB; + +typedef struct AIOReq { + SheepdogAIOCB *aiocb; + unsigned int iov_offset; + + uint64_t oid; + uint64_t base_oid; + uint64_t offset; + unsigned int data_len; + uint8_t flags; + uint32_t id; + bool create; + + QLIST_ENTRY(AIOReq) aio_siblings; +} AIOReq; + +enum AIOCBState { + AIOCB_WRITE_UDATA, + AIOCB_READ_UDATA, + AIOCB_FLUSH_CACHE, + AIOCB_DISCARD_OBJ, +}; + +struct SheepdogAIOCB { + BlockAIOCB common; + + QEMUIOVector *qiov; + + int64_t sector_num; + int nb_sectors; + + int ret; + enum AIOCBState aiocb_type; + + Coroutine *coroutine; + void (*aio_done_func)(SheepdogAIOCB *); + + bool cancelable; + int nr_pending; +}; + +typedef struct BDRVSheepdogState { + BlockDriverState *bs; + AioContext *aio_context; + + SheepdogInode inode; + + uint32_t min_dirty_data_idx; + uint32_t max_dirty_data_idx; + + char name[SD_MAX_VDI_LEN]; + bool is_snapshot; + uint32_t cache_flags; + bool discard_supported; + + char *host_spec; + bool is_unix; + int fd; + + CoMutex lock; + Coroutine *co_send; + Coroutine *co_recv; + + uint32_t aioreq_seq_num; + + /* Every aio request must be linked to either of these queues. */ + QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head; + QLIST_HEAD(pending_aio_head, AIOReq) pending_aio_head; + QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head; +} BDRVSheepdogState; + +static const char * sd_strerror(int err) +{ + int i; + + static const struct { + int err; + const char *desc; + } errors[] = { + {SD_RES_SUCCESS, "Success"}, + {SD_RES_UNKNOWN, "Unknown error"}, + {SD_RES_NO_OBJ, "No object found"}, + {SD_RES_EIO, "I/O error"}, + {SD_RES_VDI_EXIST, "VDI exists already"}, + {SD_RES_INVALID_PARMS, "Invalid parameters"}, + {SD_RES_SYSTEM_ERROR, "System error"}, + {SD_RES_VDI_LOCKED, "VDI is already locked"}, + {SD_RES_NO_VDI, "No vdi found"}, + {SD_RES_NO_BASE_VDI, "No base VDI found"}, + {SD_RES_VDI_READ, "Failed read the requested VDI"}, + {SD_RES_VDI_WRITE, "Failed to write the requested VDI"}, + {SD_RES_BASE_VDI_READ, "Failed to read the base VDI"}, + {SD_RES_BASE_VDI_WRITE, "Failed to write the base VDI"}, + {SD_RES_NO_TAG, "Failed to find the requested tag"}, + {SD_RES_STARTUP, "The system is still booting"}, + {SD_RES_VDI_NOT_LOCKED, "VDI isn't locked"}, + {SD_RES_SHUTDOWN, "The system is shutting down"}, + {SD_RES_NO_MEM, "Out of memory on the server"}, + {SD_RES_FULL_VDI, "We already have the maximum vdis"}, + {SD_RES_VER_MISMATCH, "Protocol version mismatch"}, + {SD_RES_NO_SPACE, "Server has no space for new objects"}, + {SD_RES_WAIT_FOR_FORMAT, "Sheepdog is waiting for a format operation"}, + {SD_RES_WAIT_FOR_JOIN, "Sheepdog is waiting for other nodes joining"}, + {SD_RES_JOIN_FAILED, "Target node had failed to join sheepdog"}, + {SD_RES_HALT, "Sheepdog is stopped serving IO request"}, + {SD_RES_READONLY, "Object is read-only"}, + }; + + for (i = 0; i < ARRAY_SIZE(errors); ++i) { + if (errors[i].err == err) { + return errors[i].desc; + } + } + + return "Invalid error code"; +} + +/* + * Sheepdog I/O handling: + * + * 1. In sd_co_rw_vector, we send the I/O requests to the server and + * link the requests to the inflight_list in the + * BDRVSheepdogState. The function exits without waiting for + * receiving the response. + * + * 2. We receive the response in aio_read_response, the fd handler to + * the sheepdog connection. If metadata update is needed, we send + * the write request to the vdi object in sd_write_done, the write + * completion function. We switch back to sd_co_readv/writev after + * all the requests belonging to the AIOCB are finished. + */ + +static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb, + uint64_t oid, unsigned int data_len, + uint64_t offset, uint8_t flags, bool create, + uint64_t base_oid, unsigned int iov_offset) +{ + AIOReq *aio_req; + + aio_req = g_malloc(sizeof(*aio_req)); + aio_req->aiocb = acb; + aio_req->iov_offset = iov_offset; + aio_req->oid = oid; + aio_req->base_oid = base_oid; + aio_req->offset = offset; + aio_req->data_len = data_len; + aio_req->flags = flags; + aio_req->id = s->aioreq_seq_num++; + aio_req->create = create; + + acb->nr_pending++; + return aio_req; +} + +static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req) +{ + SheepdogAIOCB *acb = aio_req->aiocb; + + acb->cancelable = false; + QLIST_REMOVE(aio_req, aio_siblings); + g_free(aio_req); + + acb->nr_pending--; +} + +static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb) +{ + qemu_coroutine_enter(acb->coroutine, NULL); + qemu_aio_unref(acb); +} + +/* + * Check whether the specified acb can be canceled + * + * We can cancel aio when any request belonging to the acb is: + * - Not processed by the sheepdog server. + * - Not linked to the inflight queue. + */ +static bool sd_acb_cancelable(const SheepdogAIOCB *acb) +{ + BDRVSheepdogState *s = acb->common.bs->opaque; + AIOReq *aioreq; + + if (!acb->cancelable) { + return false; + } + + QLIST_FOREACH(aioreq, &s->inflight_aio_head, aio_siblings) { + if (aioreq->aiocb == acb) { + return false; + } + } + + return true; +} + +static void sd_aio_cancel(BlockAIOCB *blockacb) +{ + SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb; + BDRVSheepdogState *s = acb->common.bs->opaque; + AIOReq *aioreq, *next; + + if (sd_acb_cancelable(acb)) { + /* Remove outstanding requests from pending and failed queues. */ + QLIST_FOREACH_SAFE(aioreq, &s->pending_aio_head, aio_siblings, + next) { + if (aioreq->aiocb == acb) { + free_aio_req(s, aioreq); + } + } + QLIST_FOREACH_SAFE(aioreq, &s->failed_aio_head, aio_siblings, + next) { + if (aioreq->aiocb == acb) { + free_aio_req(s, aioreq); + } + } + + assert(acb->nr_pending == 0); + if (acb->common.cb) { + acb->common.cb(acb->common.opaque, -ECANCELED); + } + sd_finish_aiocb(acb); + } +} + +static const AIOCBInfo sd_aiocb_info = { + .aiocb_size = sizeof(SheepdogAIOCB), + .cancel_async = sd_aio_cancel, +}; + +static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t sector_num, int nb_sectors) +{ + SheepdogAIOCB *acb; + + acb = qemu_aio_get(&sd_aiocb_info, bs, NULL, NULL); + + acb->qiov = qiov; + + acb->sector_num = sector_num; + acb->nb_sectors = nb_sectors; + + acb->aio_done_func = NULL; + acb->cancelable = true; + acb->coroutine = qemu_coroutine_self(); + acb->ret = 0; + acb->nr_pending = 0; + return acb; +} + +/* Return -EIO in case of error, file descriptor on success */ +static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) +{ + int fd; + + if (s->is_unix) { + fd = unix_connect(s->host_spec, errp); + } else { + fd = inet_connect(s->host_spec, errp); + + if (fd >= 0) { + int ret = socket_set_nodelay(fd); + if (ret < 0) { + error_report("%s", strerror(errno)); + } + } + } + + if (fd >= 0) { + qemu_set_nonblock(fd); + } else { + fd = -EIO; + } + + return fd; +} + +/* Return 0 on success and -errno in case of error */ +static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, + unsigned int *wlen) +{ + int ret; + + ret = qemu_co_send(sockfd, hdr, sizeof(*hdr)); + if (ret != sizeof(*hdr)) { + error_report("failed to send a req, %s", strerror(errno)); + ret = -socket_error(); + return ret; + } + + ret = qemu_co_send(sockfd, data, *wlen); + if (ret != *wlen) { + ret = -socket_error(); + error_report("failed to send a req, %s", strerror(errno)); + } + + return ret; +} + +static void restart_co_req(void *opaque) +{ + Coroutine *co = opaque; + + qemu_coroutine_enter(co, NULL); +} + +typedef struct SheepdogReqCo { + int sockfd; + AioContext *aio_context; + SheepdogReq *hdr; + void *data; + unsigned int *wlen; + unsigned int *rlen; + int ret; + bool finished; +} SheepdogReqCo; + +static coroutine_fn void do_co_req(void *opaque) +{ + int ret; + Coroutine *co; + SheepdogReqCo *srco = opaque; + int sockfd = srco->sockfd; + SheepdogReq *hdr = srco->hdr; + void *data = srco->data; + unsigned int *wlen = srco->wlen; + unsigned int *rlen = srco->rlen; + + co = qemu_coroutine_self(); + aio_set_fd_handler(srco->aio_context, sockfd, NULL, restart_co_req, co); + + ret = send_co_req(sockfd, hdr, data, wlen); + if (ret < 0) { + goto out; + } + + aio_set_fd_handler(srco->aio_context, sockfd, restart_co_req, NULL, co); + + ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr)); + if (ret != sizeof(*hdr)) { + error_report("failed to get a rsp, %s", strerror(errno)); + ret = -errno; + goto out; + } + + if (*rlen > hdr->data_length) { + *rlen = hdr->data_length; + } + + if (*rlen) { + ret = qemu_co_recv(sockfd, data, *rlen); + if (ret != *rlen) { + error_report("failed to get the data, %s", strerror(errno)); + ret = -errno; + goto out; + } + } + ret = 0; +out: + /* there is at most one request for this sockfd, so it is safe to + * set each handler to NULL. */ + aio_set_fd_handler(srco->aio_context, sockfd, NULL, NULL, NULL); + + srco->ret = ret; + srco->finished = true; +} + +/* + * Send the request to the sheep in a synchronous manner. + * + * Return 0 on success, -errno in case of error. + */ +static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr, + void *data, unsigned int *wlen, unsigned int *rlen) +{ + Coroutine *co; + SheepdogReqCo srco = { + .sockfd = sockfd, + .aio_context = aio_context, + .hdr = hdr, + .data = data, + .wlen = wlen, + .rlen = rlen, + .ret = 0, + .finished = false, + }; + + if (qemu_in_coroutine()) { + do_co_req(&srco); + } else { + co = qemu_coroutine_create(do_co_req); + qemu_coroutine_enter(co, &srco); + while (!srco.finished) { + aio_poll(aio_context, true); + } + } + + return srco.ret; +} + +static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, + struct iovec *iov, int niov, + enum AIOCBState aiocb_type); +static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req); +static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag); +static int get_sheep_fd(BDRVSheepdogState *s, Error **errp); +static void co_write_request(void *opaque); + +static AIOReq *find_pending_req(BDRVSheepdogState *s, uint64_t oid) +{ + AIOReq *aio_req; + + QLIST_FOREACH(aio_req, &s->pending_aio_head, aio_siblings) { + if (aio_req->oid == oid) { + return aio_req; + } + } + + return NULL; +} + +/* + * This function searchs pending requests to the object `oid', and + * sends them. + */ +static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid) +{ + AIOReq *aio_req; + SheepdogAIOCB *acb; + + while ((aio_req = find_pending_req(s, oid)) != NULL) { + acb = aio_req->aiocb; + /* move aio_req from pending list to inflight one */ + QLIST_REMOVE(aio_req, aio_siblings); + QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); + add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, + acb->aiocb_type); + } +} + +static coroutine_fn void reconnect_to_sdog(void *opaque) +{ + BDRVSheepdogState *s = opaque; + AIOReq *aio_req, *next; + + aio_set_fd_handler(s->aio_context, s->fd, NULL, NULL, NULL); + close(s->fd); + s->fd = -1; + + /* Wait for outstanding write requests to be completed. */ + while (s->co_send != NULL) { + co_write_request(opaque); + } + + /* Try to reconnect the sheepdog server every one second. */ + while (s->fd < 0) { + Error *local_err = NULL; + s->fd = get_sheep_fd(s, &local_err); + if (s->fd < 0) { + DPRINTF("Wait for connection to be established\n"); + error_report_err(local_err); + co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME, + 1000000000ULL); + } + }; + + /* + * Now we have to resend all the request in the inflight queue. However, + * resend_aioreq() can yield and newly created requests can be added to the + * inflight queue before the coroutine is resumed. To avoid mixing them, we + * have to move all the inflight requests to the failed queue before + * resend_aioreq() is called. + */ + QLIST_FOREACH_SAFE(aio_req, &s->inflight_aio_head, aio_siblings, next) { + QLIST_REMOVE(aio_req, aio_siblings); + QLIST_INSERT_HEAD(&s->failed_aio_head, aio_req, aio_siblings); + } + + /* Resend all the failed aio requests. */ + while (!QLIST_EMPTY(&s->failed_aio_head)) { + aio_req = QLIST_FIRST(&s->failed_aio_head); + QLIST_REMOVE(aio_req, aio_siblings); + QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); + resend_aioreq(s, aio_req); + } +} + +/* + * Receive responses of the I/O requests. + * + * This function is registered as a fd handler, and called from the + * main loop when s->fd is ready for reading responses. + */ +static void coroutine_fn aio_read_response(void *opaque) +{ + SheepdogObjRsp rsp; + BDRVSheepdogState *s = opaque; + int fd = s->fd; + int ret; + AIOReq *aio_req = NULL; + SheepdogAIOCB *acb; + uint64_t idx; + + /* read a header */ + ret = qemu_co_recv(fd, &rsp, sizeof(rsp)); + if (ret != sizeof(rsp)) { + error_report("failed to get the header, %s", strerror(errno)); + goto err; + } + + /* find the right aio_req from the inflight aio list */ + QLIST_FOREACH(aio_req, &s->inflight_aio_head, aio_siblings) { + if (aio_req->id == rsp.id) { + break; + } + } + if (!aio_req) { + error_report("cannot find aio_req %x", rsp.id); + goto err; + } + + acb = aio_req->aiocb; + + switch (acb->aiocb_type) { + case AIOCB_WRITE_UDATA: + /* this coroutine context is no longer suitable for co_recv + * because we may send data to update vdi objects */ + s->co_recv = NULL; + if (!is_data_obj(aio_req->oid)) { + break; + } + idx = data_oid_to_idx(aio_req->oid); + + if (aio_req->create) { + /* + * If the object is newly created one, we need to update + * the vdi object (metadata object). min_dirty_data_idx + * and max_dirty_data_idx are changed to include updated + * index between them. + */ + if (rsp.result == SD_RES_SUCCESS) { + s->inode.data_vdi_id[idx] = s->inode.vdi_id; + s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx); + s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx); + } + /* + * Some requests may be blocked because simultaneous + * create requests are not allowed, so we search the + * pending requests here. + */ + send_pending_req(s, aio_req->oid); + } + break; + case AIOCB_READ_UDATA: + ret = qemu_co_recvv(fd, acb->qiov->iov, acb->qiov->niov, + aio_req->iov_offset, rsp.data_length); + if (ret != rsp.data_length) { + error_report("failed to get the data, %s", strerror(errno)); + goto err; + } + break; + case AIOCB_FLUSH_CACHE: + if (rsp.result == SD_RES_INVALID_PARMS) { + DPRINTF("disable cache since the server doesn't support it\n"); + s->cache_flags = SD_FLAG_CMD_DIRECT; + rsp.result = SD_RES_SUCCESS; + } + break; + case AIOCB_DISCARD_OBJ: + switch (rsp.result) { + case SD_RES_INVALID_PARMS: + error_report("sheep(%s) doesn't support discard command", + s->host_spec); + rsp.result = SD_RES_SUCCESS; + s->discard_supported = false; + break; + case SD_RES_SUCCESS: + idx = data_oid_to_idx(aio_req->oid); + s->inode.data_vdi_id[idx] = 0; + break; + default: + break; + } + } + + switch (rsp.result) { + case SD_RES_SUCCESS: + break; + case SD_RES_READONLY: + if (s->inode.vdi_id == oid_to_vid(aio_req->oid)) { + ret = reload_inode(s, 0, ""); + if (ret < 0) { + goto err; + } + } + if (is_data_obj(aio_req->oid)) { + aio_req->oid = vid_to_data_oid(s->inode.vdi_id, + data_oid_to_idx(aio_req->oid)); + } else { + aio_req->oid = vid_to_vdi_oid(s->inode.vdi_id); + } + resend_aioreq(s, aio_req); + goto out; + default: + acb->ret = -EIO; + error_report("%s", sd_strerror(rsp.result)); + break; + } + + free_aio_req(s, aio_req); + if (!acb->nr_pending) { + /* + * We've finished all requests which belong to the AIOCB, so + * we can switch back to sd_co_readv/writev now. + */ + acb->aio_done_func(acb); + } +out: + s->co_recv = NULL; + return; +err: + s->co_recv = NULL; + reconnect_to_sdog(opaque); +} + +static void co_read_response(void *opaque) +{ + BDRVSheepdogState *s = opaque; + + if (!s->co_recv) { + s->co_recv = qemu_coroutine_create(aio_read_response); + } + + qemu_coroutine_enter(s->co_recv, opaque); +} + +static void co_write_request(void *opaque) +{ + BDRVSheepdogState *s = opaque; + + qemu_coroutine_enter(s->co_send, NULL); +} + +/* + * Return a socket descriptor to read/write objects. + * + * We cannot use this descriptor for other operations because + * the block driver may be on waiting response from the server. + */ +static int get_sheep_fd(BDRVSheepdogState *s, Error **errp) +{ + int fd; + + fd = connect_to_sdog(s, errp); + if (fd < 0) { + return fd; + } + + aio_set_fd_handler(s->aio_context, fd, co_read_response, NULL, s); + return fd; +} + +static int sd_parse_uri(BDRVSheepdogState *s, const char *filename, + char *vdi, uint32_t *snapid, char *tag) +{ + URI *uri; + QueryParams *qp = NULL; + int ret = 0; + + uri = uri_parse(filename); + if (!uri) { + return -EINVAL; + } + + /* transport */ + if (!strcmp(uri->scheme, "sheepdog")) { + s->is_unix = false; + } else if (!strcmp(uri->scheme, "sheepdog+tcp")) { + s->is_unix = false; + } else if (!strcmp(uri->scheme, "sheepdog+unix")) { + s->is_unix = true; + } else { + ret = -EINVAL; + goto out; + } + + if (uri->path == NULL || !strcmp(uri->path, "/")) { + ret = -EINVAL; + goto out; + } + pstrcpy(vdi, SD_MAX_VDI_LEN, uri->path + 1); + + qp = query_params_parse(uri->query); + if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) { + ret = -EINVAL; + goto out; + } + + if (s->is_unix) { + /* sheepdog+unix:///vdiname?socket=path */ + if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { + ret = -EINVAL; + goto out; + } + s->host_spec = g_strdup(qp->p[0].value); + } else { + /* sheepdog[+tcp]://[host:port]/vdiname */ + s->host_spec = g_strdup_printf("%s:%d", uri->server ?: SD_DEFAULT_ADDR, + uri->port ?: SD_DEFAULT_PORT); + } + + /* snapshot tag */ + if (uri->fragment) { + *snapid = strtoul(uri->fragment, NULL, 10); + if (*snapid == 0) { + pstrcpy(tag, SD_MAX_VDI_TAG_LEN, uri->fragment); + } + } else { + *snapid = CURRENT_VDI_ID; /* search current vdi */ + } + +out: + if (qp) { + query_params_free(qp); + } + uri_free(uri); + return ret; +} + +/* + * Parse a filename (old syntax) + * + * filename must be one of the following formats: + * 1. [vdiname] + * 2. [vdiname]:[snapid] + * 3. [vdiname]:[tag] + * 4. [hostname]:[port]:[vdiname] + * 5. [hostname]:[port]:[vdiname]:[snapid] + * 6. [hostname]:[port]:[vdiname]:[tag] + * + * You can boot from the snapshot images by specifying `snapid` or + * `tag'. + * + * You can run VMs outside the Sheepdog cluster by specifying + * `hostname' and `port' (experimental). + */ +static int parse_vdiname(BDRVSheepdogState *s, const char *filename, + char *vdi, uint32_t *snapid, char *tag) +{ + char *p, *q, *uri; + const char *host_spec, *vdi_spec; + int nr_sep, ret; + + strstart(filename, "sheepdog:", (const char **)&filename); + p = q = g_strdup(filename); + + /* count the number of separators */ + nr_sep = 0; + while (*p) { + if (*p == ':') { + nr_sep++; + } + p++; + } + p = q; + + /* use the first two tokens as host_spec. */ + if (nr_sep >= 2) { + host_spec = p; + p = strchr(p, ':'); + p++; + p = strchr(p, ':'); + *p++ = '\0'; + } else { + host_spec = ""; + } + + vdi_spec = p; + + p = strchr(vdi_spec, ':'); + if (p) { + *p++ = '#'; + } + + uri = g_strdup_printf("sheepdog://%s/%s", host_spec, vdi_spec); + + ret = sd_parse_uri(s, uri, vdi, snapid, tag); + + g_free(q); + g_free(uri); + + return ret; +} + +static int find_vdi_name(BDRVSheepdogState *s, const char *filename, + uint32_t snapid, const char *tag, uint32_t *vid, + bool lock, Error **errp) +{ + int ret, fd; + SheepdogVdiReq hdr; + SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; + unsigned int wlen, rlen = 0; + char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; + + fd = connect_to_sdog(s, errp); + if (fd < 0) { + return fd; + } + + /* This pair of strncpy calls ensures that the buffer is zero-filled, + * which is desirable since we'll soon be sending those bytes, and + * don't want the send_req to read uninitialized data. + */ + strncpy(buf, filename, SD_MAX_VDI_LEN); + strncpy(buf + SD_MAX_VDI_LEN, tag, SD_MAX_VDI_TAG_LEN); + + memset(&hdr, 0, sizeof(hdr)); + if (lock) { + hdr.opcode = SD_OP_LOCK_VDI; + hdr.type = LOCK_TYPE_NORMAL; + } else { + hdr.opcode = SD_OP_GET_VDI_INFO; + } + wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN; + hdr.proto_ver = SD_PROTO_VER; + hdr.data_length = wlen; + hdr.snapid = snapid; + hdr.flags = SD_FLAG_CMD_WRITE; + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + if (ret) { + error_setg_errno(errp, -ret, "cannot get vdi info"); + goto out; + } + + if (rsp->result != SD_RES_SUCCESS) { + error_setg(errp, "cannot get vdi info, %s, %s %" PRIu32 " %s", + sd_strerror(rsp->result), filename, snapid, tag); + if (rsp->result == SD_RES_NO_VDI) { + ret = -ENOENT; + } else if (rsp->result == SD_RES_VDI_LOCKED) { + ret = -EBUSY; + } else { + ret = -EIO; + } + goto out; + } + *vid = rsp->vdi_id; + + ret = 0; +out: + closesocket(fd); + return ret; +} + +static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, + struct iovec *iov, int niov, + enum AIOCBState aiocb_type) +{ + int nr_copies = s->inode.nr_copies; + SheepdogObjReq hdr; + unsigned int wlen = 0; + int ret; + uint64_t oid = aio_req->oid; + unsigned int datalen = aio_req->data_len; + uint64_t offset = aio_req->offset; + uint8_t flags = aio_req->flags; + uint64_t old_oid = aio_req->base_oid; + bool create = aio_req->create; + + if (!nr_copies) { + error_report("bug"); + } + + memset(&hdr, 0, sizeof(hdr)); + + switch (aiocb_type) { + case AIOCB_FLUSH_CACHE: + hdr.opcode = SD_OP_FLUSH_VDI; + break; + case AIOCB_READ_UDATA: + hdr.opcode = SD_OP_READ_OBJ; + hdr.flags = flags; + break; + case AIOCB_WRITE_UDATA: + if (create) { + hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ; + } else { + hdr.opcode = SD_OP_WRITE_OBJ; + } + wlen = datalen; + hdr.flags = SD_FLAG_CMD_WRITE | flags; + break; + case AIOCB_DISCARD_OBJ: + hdr.opcode = SD_OP_DISCARD_OBJ; + break; + } + + if (s->cache_flags) { + hdr.flags |= s->cache_flags; + } + + hdr.oid = oid; + hdr.cow_oid = old_oid; + hdr.copies = s->inode.nr_copies; + + hdr.data_length = datalen; + hdr.offset = offset; + + hdr.id = aio_req->id; + + qemu_co_mutex_lock(&s->lock); + s->co_send = qemu_coroutine_self(); + aio_set_fd_handler(s->aio_context, s->fd, + co_read_response, co_write_request, s); + socket_set_cork(s->fd, 1); + + /* send a header */ + ret = qemu_co_send(s->fd, &hdr, sizeof(hdr)); + if (ret != sizeof(hdr)) { + error_report("failed to send a req, %s", strerror(errno)); + goto out; + } + + if (wlen) { + ret = qemu_co_sendv(s->fd, iov, niov, aio_req->iov_offset, wlen); + if (ret != wlen) { + error_report("failed to send a data, %s", strerror(errno)); + } + } +out: + socket_set_cork(s->fd, 0); + aio_set_fd_handler(s->aio_context, s->fd, co_read_response, NULL, s); + s->co_send = NULL; + qemu_co_mutex_unlock(&s->lock); +} + +static int read_write_object(int fd, AioContext *aio_context, char *buf, + uint64_t oid, uint8_t copies, + unsigned int datalen, uint64_t offset, + bool write, bool create, uint32_t cache_flags) +{ + SheepdogObjReq hdr; + SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr; + unsigned int wlen, rlen; + int ret; + + memset(&hdr, 0, sizeof(hdr)); + + if (write) { + wlen = datalen; + rlen = 0; + hdr.flags = SD_FLAG_CMD_WRITE; + if (create) { + hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ; + } else { + hdr.opcode = SD_OP_WRITE_OBJ; + } + } else { + wlen = 0; + rlen = datalen; + hdr.opcode = SD_OP_READ_OBJ; + } + + hdr.flags |= cache_flags; + + hdr.oid = oid; + hdr.data_length = datalen; + hdr.offset = offset; + hdr.copies = copies; + + ret = do_req(fd, aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + if (ret) { + error_report("failed to send a request to the sheep"); + return ret; + } + + switch (rsp->result) { + case SD_RES_SUCCESS: + return 0; + default: + error_report("%s", sd_strerror(rsp->result)); + return -EIO; + } +} + +static int read_object(int fd, AioContext *aio_context, char *buf, + uint64_t oid, uint8_t copies, + unsigned int datalen, uint64_t offset, + uint32_t cache_flags) +{ + return read_write_object(fd, aio_context, buf, oid, copies, + datalen, offset, false, + false, cache_flags); +} + +static int write_object(int fd, AioContext *aio_context, char *buf, + uint64_t oid, uint8_t copies, + unsigned int datalen, uint64_t offset, bool create, + uint32_t cache_flags) +{ + return read_write_object(fd, aio_context, buf, oid, copies, + datalen, offset, true, + create, cache_flags); +} + +/* update inode with the latest state */ +static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag) +{ + Error *local_err = NULL; + SheepdogInode *inode; + int ret = 0, fd; + uint32_t vid = 0; + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + return -EIO; + } + + inode = g_malloc(SD_INODE_HEADER_SIZE); + + ret = find_vdi_name(s, s->name, snapid, tag, &vid, false, &local_err); + if (ret) { + error_report_err(local_err); + goto out; + } + + ret = read_object(fd, s->aio_context, (char *)inode, vid_to_vdi_oid(vid), + s->inode.nr_copies, SD_INODE_HEADER_SIZE, 0, + s->cache_flags); + if (ret < 0) { + goto out; + } + + if (inode->vdi_id != s->inode.vdi_id) { + memcpy(&s->inode, inode, SD_INODE_HEADER_SIZE); + } + +out: + g_free(inode); + closesocket(fd); + + return ret; +} + +/* Return true if the specified request is linked to the pending list. */ +static bool check_simultaneous_create(BDRVSheepdogState *s, AIOReq *aio_req) +{ + AIOReq *areq; + QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) { + if (areq != aio_req && areq->oid == aio_req->oid) { + /* + * Sheepdog cannot handle simultaneous create requests to the same + * object, so we cannot send the request until the previous request + * finishes. + */ + DPRINTF("simultaneous create to %" PRIx64 "\n", aio_req->oid); + aio_req->flags = 0; + aio_req->base_oid = 0; + aio_req->create = false; + QLIST_REMOVE(aio_req, aio_siblings); + QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req, aio_siblings); + return true; + } + } + + return false; +} + +static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req) +{ + SheepdogAIOCB *acb = aio_req->aiocb; + + aio_req->create = false; + + /* check whether this request becomes a CoW one */ + if (acb->aiocb_type == AIOCB_WRITE_UDATA && is_data_obj(aio_req->oid)) { + int idx = data_oid_to_idx(aio_req->oid); + + if (is_data_obj_writable(&s->inode, idx)) { + goto out; + } + + if (check_simultaneous_create(s, aio_req)) { + return; + } + + if (s->inode.data_vdi_id[idx]) { + aio_req->base_oid = vid_to_data_oid(s->inode.data_vdi_id[idx], idx); + aio_req->flags |= SD_FLAG_CMD_COW; + } + aio_req->create = true; + } +out: + if (is_data_obj(aio_req->oid)) { + add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, + acb->aiocb_type); + } else { + struct iovec iov; + iov.iov_base = &s->inode; + iov.iov_len = sizeof(s->inode); + add_aio_request(s, aio_req, &iov, 1, AIOCB_WRITE_UDATA); + } +} + +static void sd_detach_aio_context(BlockDriverState *bs) +{ + BDRVSheepdogState *s = bs->opaque; + + aio_set_fd_handler(s->aio_context, s->fd, NULL, NULL, NULL); +} + +static void sd_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVSheepdogState *s = bs->opaque; + + s->aio_context = new_context; + aio_set_fd_handler(new_context, s->fd, co_read_response, NULL, s); +} + +/* TODO Convert to fine grained options */ +static QemuOptsList runtime_opts = { + .name = "sheepdog", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "URL to the sheepdog image", + }, + { /* end of list */ } + }, +}; + +static int sd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + int ret, fd; + uint32_t vid = 0; + BDRVSheepdogState *s = bs->opaque; + char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; + uint32_t snapid; + char *buf = NULL; + QemuOpts *opts; + Error *local_err = NULL; + const char *filename; + + s->bs = bs; + s->aio_context = bdrv_get_aio_context(bs); + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + filename = qemu_opt_get(opts, "filename"); + + QLIST_INIT(&s->inflight_aio_head); + QLIST_INIT(&s->pending_aio_head); + QLIST_INIT(&s->failed_aio_head); + s->fd = -1; + + memset(vdi, 0, sizeof(vdi)); + memset(tag, 0, sizeof(tag)); + + if (strstr(filename, "://")) { + ret = sd_parse_uri(s, filename, vdi, &snapid, tag); + } else { + ret = parse_vdiname(s, filename, vdi, &snapid, tag); + } + if (ret < 0) { + error_setg(errp, "Can't parse filename"); + goto out; + } + s->fd = get_sheep_fd(s, errp); + if (s->fd < 0) { + ret = s->fd; + goto out; + } + + ret = find_vdi_name(s, vdi, snapid, tag, &vid, true, errp); + if (ret) { + goto out; + } + + /* + * QEMU block layer emulates writethrough cache as 'writeback + flush', so + * we always set SD_FLAG_CMD_CACHE (writeback cache) as default. + */ + s->cache_flags = SD_FLAG_CMD_CACHE; + if (flags & BDRV_O_NOCACHE) { + s->cache_flags = SD_FLAG_CMD_DIRECT; + } + s->discard_supported = true; + + if (snapid || tag[0] != '\0') { + DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid); + s->is_snapshot = true; + } + + fd = connect_to_sdog(s, errp); + if (fd < 0) { + ret = fd; + goto out; + } + + buf = g_malloc(SD_INODE_SIZE); + ret = read_object(fd, s->aio_context, buf, vid_to_vdi_oid(vid), + 0, SD_INODE_SIZE, 0, s->cache_flags); + + closesocket(fd); + + if (ret) { + error_setg(errp, "Can't read snapshot inode"); + goto out; + } + + memcpy(&s->inode, buf, sizeof(s->inode)); + s->min_dirty_data_idx = UINT32_MAX; + s->max_dirty_data_idx = 0; + + bs->total_sectors = s->inode.vdi_size / BDRV_SECTOR_SIZE; + pstrcpy(s->name, sizeof(s->name), vdi); + qemu_co_mutex_init(&s->lock); + qemu_opts_del(opts); + g_free(buf); + return 0; +out: + aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, NULL, NULL, NULL); + if (s->fd >= 0) { + closesocket(s->fd); + } + qemu_opts_del(opts); + g_free(buf); + return ret; +} + +static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, + Error **errp) +{ + SheepdogVdiReq hdr; + SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; + int fd, ret; + unsigned int wlen, rlen = 0; + char buf[SD_MAX_VDI_LEN]; + + fd = connect_to_sdog(s, errp); + if (fd < 0) { + return fd; + } + + /* FIXME: would it be better to fail (e.g., return -EIO) when filename + * does not fit in buf? For now, just truncate and avoid buffer overrun. + */ + memset(buf, 0, sizeof(buf)); + pstrcpy(buf, sizeof(buf), s->name); + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = SD_OP_NEW_VDI; + hdr.base_vdi_id = s->inode.vdi_id; + + wlen = SD_MAX_VDI_LEN; + + hdr.flags = SD_FLAG_CMD_WRITE; + hdr.snapid = snapshot; + + hdr.data_length = wlen; + hdr.vdi_size = s->inode.vdi_size; + hdr.copy_policy = s->inode.copy_policy; + hdr.copies = s->inode.nr_copies; + hdr.block_size_shift = s->inode.block_size_shift; + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + + closesocket(fd); + + if (ret) { + error_setg_errno(errp, -ret, "create failed"); + return ret; + } + + if (rsp->result != SD_RES_SUCCESS) { + error_setg(errp, "%s, %s", sd_strerror(rsp->result), s->inode.name); + return -EIO; + } + + if (vdi_id) { + *vdi_id = rsp->vdi_id; + } + + return 0; +} + +static int sd_prealloc(const char *filename, Error **errp) +{ + BlockDriverState *bs = NULL; + BDRVSheepdogState *base = NULL; + unsigned long buf_size; + uint32_t idx, max_idx; + uint32_t object_size; + int64_t vdi_size; + void *buf = NULL; + int ret; + + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, errp); + if (ret < 0) { + goto out_with_err_set; + } + + vdi_size = bdrv_getlength(bs); + if (vdi_size < 0) { + ret = vdi_size; + goto out; + } + + base = bs->opaque; + object_size = (UINT32_C(1) << base->inode.block_size_shift); + buf_size = MIN(object_size, SD_DATA_OBJ_SIZE); + buf = g_malloc0(buf_size); + + max_idx = DIV_ROUND_UP(vdi_size, buf_size); + + for (idx = 0; idx < max_idx; idx++) { + /* + * The created image can be a cloned image, so we need to read + * a data from the source image. + */ + ret = bdrv_pread(bs, idx * buf_size, buf, buf_size); + if (ret < 0) { + goto out; + } + ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size); + if (ret < 0) { + goto out; + } + } + +out: + if (ret < 0) { + error_setg_errno(errp, -ret, "Can't pre-allocate"); + } +out_with_err_set: + if (bs) { + bdrv_unref(bs); + } + g_free(buf); + + return ret; +} + +/* + * Sheepdog support two kinds of redundancy, full replication and erasure + * coding. + * + * # create a fully replicated vdi with x copies + * -o redundancy=x (1 <= x <= SD_MAX_COPIES) + * + * # create a erasure coded vdi with x data strips and y parity strips + * -o redundancy=x:y (x must be one of {2,4,8,16} and 1 <= y < SD_EC_MAX_STRIP) + */ +static int parse_redundancy(BDRVSheepdogState *s, const char *opt) +{ + struct SheepdogInode *inode = &s->inode; + const char *n1, *n2; + long copy, parity; + char p[10]; + + pstrcpy(p, sizeof(p), opt); + n1 = strtok(p, ":"); + n2 = strtok(NULL, ":"); + + if (!n1) { + return -EINVAL; + } + + copy = strtol(n1, NULL, 10); + if (copy > SD_MAX_COPIES || copy < 1) { + return -EINVAL; + } + if (!n2) { + inode->copy_policy = 0; + inode->nr_copies = copy; + return 0; + } + + if (copy != 2 && copy != 4 && copy != 8 && copy != 16) { + return -EINVAL; + } + + parity = strtol(n2, NULL, 10); + if (parity >= SD_EC_MAX_STRIP || parity < 1) { + return -EINVAL; + } + + /* + * 4 bits for parity and 4 bits for data. + * We have to compress upper data bits because it can't represent 16 + */ + inode->copy_policy = ((copy / 2) << 4) + parity; + inode->nr_copies = copy + parity; + + return 0; +} + +static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt) +{ + struct SheepdogInode *inode = &s->inode; + uint64_t object_size; + int obj_order; + + object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0); + if (object_size) { + if ((object_size - 1) & object_size) { /* not a power of 2? */ + return -EINVAL; + } + obj_order = ctz32(object_size); + if (obj_order < 20 || obj_order > 31) { + return -EINVAL; + } + inode->block_size_shift = (uint8_t)obj_order; + } + + return 0; +} + +static int sd_create(const char *filename, QemuOpts *opts, + Error **errp) +{ + int ret = 0; + uint32_t vid = 0; + char *backing_file = NULL; + char *buf = NULL; + BDRVSheepdogState *s; + char tag[SD_MAX_VDI_TAG_LEN]; + uint32_t snapid; + uint64_t max_vdi_size; + bool prealloc = false; + + s = g_new0(BDRVSheepdogState, 1); + + memset(tag, 0, sizeof(tag)); + if (strstr(filename, "://")) { + ret = sd_parse_uri(s, filename, s->name, &snapid, tag); + } else { + ret = parse_vdiname(s, filename, s->name, &snapid, tag); + } + if (ret < 0) { + error_setg(errp, "Can't parse filename"); + goto out; + } + + s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); + buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); + if (!buf || !strcmp(buf, "off")) { + prealloc = false; + } else if (!strcmp(buf, "full")) { + prealloc = true; + } else { + error_setg(errp, "Invalid preallocation mode: '%s'", buf); + ret = -EINVAL; + goto out; + } + + g_free(buf); + buf = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY); + if (buf) { + ret = parse_redundancy(s, buf); + if (ret < 0) { + error_setg(errp, "Invalid redundancy mode: '%s'", buf); + goto out; + } + } + ret = parse_block_size_shift(s, opts); + if (ret < 0) { + error_setg(errp, "Invalid object_size." + " obect_size needs to be power of 2" + " and be limited from 2^20 to 2^31"); + goto out; + } + + if (backing_file) { + BlockDriverState *bs; + BDRVSheepdogState *base; + BlockDriver *drv; + + /* Currently, only Sheepdog backing image is supported. */ + drv = bdrv_find_protocol(backing_file, true, NULL); + if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) { + error_setg(errp, "backing_file must be a sheepdog image"); + ret = -EINVAL; + goto out; + } + + bs = NULL; + ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL, + errp); + if (ret < 0) { + goto out; + } + + base = bs->opaque; + + if (!is_snapshot(&base->inode)) { + error_setg(errp, "cannot clone from a non snapshot vdi"); + bdrv_unref(bs); + ret = -EINVAL; + goto out; + } + s->inode.vdi_id = base->inode.vdi_id; + bdrv_unref(bs); + } + + s->aio_context = qemu_get_aio_context(); + + /* if block_size_shift is not specified, get cluster default value */ + if (s->inode.block_size_shift == 0) { + SheepdogVdiReq hdr; + SheepdogClusterRsp *rsp = (SheepdogClusterRsp *)&hdr; + Error *local_err = NULL; + int fd; + unsigned int wlen = 0, rlen = 0; + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + ret = -EIO; + goto out; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = SD_OP_GET_CLUSTER_DEFAULT; + hdr.proto_ver = SD_PROTO_VER; + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + NULL, &wlen, &rlen); + closesocket(fd); + if (ret) { + error_setg_errno(errp, -ret, "failed to get cluster default"); + goto out; + } + if (rsp->result == SD_RES_SUCCESS) { + s->inode.block_size_shift = rsp->block_size_shift; + } else { + s->inode.block_size_shift = SD_DEFAULT_BLOCK_SIZE_SHIFT; + } + } + + max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; + + if (s->inode.vdi_size > max_vdi_size) { + error_setg(errp, "An image is too large." + " The maximum image size is %"PRIu64 "GB", + max_vdi_size / 1024 / 1024 / 1024); + ret = -EINVAL; + goto out; + } + + ret = do_sd_create(s, &vid, 0, errp); + if (ret) { + goto out; + } + + if (prealloc) { + ret = sd_prealloc(filename, errp); + } +out: + g_free(backing_file); + g_free(buf); + g_free(s); + return ret; +} + +static void sd_close(BlockDriverState *bs) +{ + Error *local_err = NULL; + BDRVSheepdogState *s = bs->opaque; + SheepdogVdiReq hdr; + SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; + unsigned int wlen, rlen = 0; + int fd, ret; + + DPRINTF("%s\n", s->name); + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + return; + } + + memset(&hdr, 0, sizeof(hdr)); + + hdr.opcode = SD_OP_RELEASE_VDI; + hdr.type = LOCK_TYPE_NORMAL; + hdr.base_vdi_id = s->inode.vdi_id; + wlen = strlen(s->name) + 1; + hdr.data_length = wlen; + hdr.flags = SD_FLAG_CMD_WRITE; + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + s->name, &wlen, &rlen); + + closesocket(fd); + + if (!ret && rsp->result != SD_RES_SUCCESS && + rsp->result != SD_RES_VDI_NOT_LOCKED) { + error_report("%s, %s", sd_strerror(rsp->result), s->name); + } + + aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, NULL, NULL, NULL); + closesocket(s->fd); + g_free(s->host_spec); +} + +static int64_t sd_getlength(BlockDriverState *bs) +{ + BDRVSheepdogState *s = bs->opaque; + + return s->inode.vdi_size; +} + +static int sd_truncate(BlockDriverState *bs, int64_t offset) +{ + Error *local_err = NULL; + BDRVSheepdogState *s = bs->opaque; + int ret, fd; + unsigned int datalen; + uint64_t max_vdi_size; + + max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; + if (offset < s->inode.vdi_size) { + error_report("shrinking is not supported"); + return -EINVAL; + } else if (offset > max_vdi_size) { + error_report("too big image size"); + return -EINVAL; + } + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + return fd; + } + + /* we don't need to update entire object */ + datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + s->inode.vdi_size = offset; + ret = write_object(fd, s->aio_context, (char *)&s->inode, + vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies, + datalen, 0, false, s->cache_flags); + close(fd); + + if (ret < 0) { + error_report("failed to update an inode."); + } + + return ret; +} + +/* + * This function is called after writing data objects. If we need to + * update metadata, this sends a write request to the vdi object. + * Otherwise, this switches back to sd_co_readv/writev. + */ +static void coroutine_fn sd_write_done(SheepdogAIOCB *acb) +{ + BDRVSheepdogState *s = acb->common.bs->opaque; + struct iovec iov; + AIOReq *aio_req; + uint32_t offset, data_len, mn, mx; + + mn = s->min_dirty_data_idx; + mx = s->max_dirty_data_idx; + if (mn <= mx) { + /* we need to update the vdi object. */ + offset = sizeof(s->inode) - sizeof(s->inode.data_vdi_id) + + mn * sizeof(s->inode.data_vdi_id[0]); + data_len = (mx - mn + 1) * sizeof(s->inode.data_vdi_id[0]); + + s->min_dirty_data_idx = UINT32_MAX; + s->max_dirty_data_idx = 0; + + iov.iov_base = &s->inode; + iov.iov_len = sizeof(s->inode); + aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id), + data_len, offset, 0, false, 0, offset); + QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); + add_aio_request(s, aio_req, &iov, 1, AIOCB_WRITE_UDATA); + + acb->aio_done_func = sd_finish_aiocb; + acb->aiocb_type = AIOCB_WRITE_UDATA; + return; + } + + sd_finish_aiocb(acb); +} + +/* Delete current working VDI on the snapshot chain */ +static bool sd_delete(BDRVSheepdogState *s) +{ + Error *local_err = NULL; + unsigned int wlen = SD_MAX_VDI_LEN, rlen = 0; + SheepdogVdiReq hdr = { + .opcode = SD_OP_DEL_VDI, + .base_vdi_id = s->inode.vdi_id, + .data_length = wlen, + .flags = SD_FLAG_CMD_WRITE, + }; + SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; + int fd, ret; + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + return false; + } + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + s->name, &wlen, &rlen); + closesocket(fd); + if (ret) { + return false; + } + switch (rsp->result) { + case SD_RES_NO_VDI: + error_report("%s was already deleted", s->name); + /* fall through */ + case SD_RES_SUCCESS: + break; + default: + error_report("%s, %s", sd_strerror(rsp->result), s->name); + return false; + } + + return true; +} + +/* + * Create a writable VDI from a snapshot + */ +static int sd_create_branch(BDRVSheepdogState *s) +{ + Error *local_err = NULL; + int ret, fd; + uint32_t vid; + char *buf; + bool deleted; + + DPRINTF("%" PRIx32 " is snapshot.\n", s->inode.vdi_id); + + buf = g_malloc(SD_INODE_SIZE); + + /* + * Even If deletion fails, we will just create extra snapshot based on + * the working VDI which was supposed to be deleted. So no need to + * false bail out. + */ + deleted = sd_delete(s); + ret = do_sd_create(s, &vid, !deleted, &local_err); + if (ret) { + error_report_err(local_err); + goto out; + } + + DPRINTF("%" PRIx32 " is created.\n", vid); + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + ret = fd; + goto out; + } + + ret = read_object(fd, s->aio_context, buf, vid_to_vdi_oid(vid), + s->inode.nr_copies, SD_INODE_SIZE, 0, s->cache_flags); + + closesocket(fd); + + if (ret < 0) { + goto out; + } + + memcpy(&s->inode, buf, sizeof(s->inode)); + + s->is_snapshot = false; + ret = 0; + DPRINTF("%" PRIx32 " was newly created.\n", s->inode.vdi_id); + +out: + g_free(buf); + + return ret; +} + +/* + * Send I/O requests to the server. + * + * This function sends requests to the server, links the requests to + * the inflight_list in BDRVSheepdogState, and exits without + * waiting the response. The responses are received in the + * `aio_read_response' function which is called from the main loop as + * a fd handler. + * + * Returns 1 when we need to wait a response, 0 when there is no sent + * request and -errno in error cases. + */ +static int coroutine_fn sd_co_rw_vector(void *p) +{ + SheepdogAIOCB *acb = p; + int ret = 0; + unsigned long len, done = 0, total = acb->nb_sectors * BDRV_SECTOR_SIZE; + unsigned long idx; + uint32_t object_size; + uint64_t oid; + uint64_t offset; + BDRVSheepdogState *s = acb->common.bs->opaque; + SheepdogInode *inode = &s->inode; + AIOReq *aio_req; + + if (acb->aiocb_type == AIOCB_WRITE_UDATA && s->is_snapshot) { + /* + * In the case we open the snapshot VDI, Sheepdog creates the + * writable VDI when we do a write operation first. + */ + ret = sd_create_branch(s); + if (ret) { + acb->ret = -EIO; + goto out; + } + } + + object_size = (UINT32_C(1) << inode->block_size_shift); + idx = acb->sector_num * BDRV_SECTOR_SIZE / object_size; + offset = (acb->sector_num * BDRV_SECTOR_SIZE) % object_size; + + /* + * Make sure we don't free the aiocb before we are done with all requests. + * This additional reference is dropped at the end of this function. + */ + acb->nr_pending++; + + while (done != total) { + uint8_t flags = 0; + uint64_t old_oid = 0; + bool create = false; + + oid = vid_to_data_oid(inode->data_vdi_id[idx], idx); + + len = MIN(total - done, object_size - offset); + + switch (acb->aiocb_type) { + case AIOCB_READ_UDATA: + if (!inode->data_vdi_id[idx]) { + qemu_iovec_memset(acb->qiov, done, 0, len); + goto done; + } + break; + case AIOCB_WRITE_UDATA: + if (!inode->data_vdi_id[idx]) { + create = true; + } else if (!is_data_obj_writable(inode, idx)) { + /* Copy-On-Write */ + create = true; + old_oid = oid; + flags = SD_FLAG_CMD_COW; + } + break; + case AIOCB_DISCARD_OBJ: + /* + * We discard the object only when the whole object is + * 1) allocated 2) trimmed. Otherwise, simply skip it. + */ + if (len != object_size || inode->data_vdi_id[idx] == 0) { + goto done; + } + break; + default: + break; + } + + if (create) { + DPRINTF("update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld\n", + inode->vdi_id, oid, + vid_to_data_oid(inode->data_vdi_id[idx], idx), idx); + oid = vid_to_data_oid(inode->vdi_id, idx); + DPRINTF("new oid %" PRIx64 "\n", oid); + } + + aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create, + old_oid, done); + QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); + + if (create) { + if (check_simultaneous_create(s, aio_req)) { + goto done; + } + } + + add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, + acb->aiocb_type); + done: + offset = 0; + idx++; + done += len; + } +out: + if (!--acb->nr_pending) { + return acb->ret; + } + return 1; +} + +static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + SheepdogAIOCB *acb; + int ret; + int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE; + BDRVSheepdogState *s = bs->opaque; + + if (offset > s->inode.vdi_size) { + ret = sd_truncate(bs, offset); + if (ret < 0) { + return ret; + } + } + + acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors); + acb->aio_done_func = sd_write_done; + acb->aiocb_type = AIOCB_WRITE_UDATA; + + ret = sd_co_rw_vector(acb); + if (ret <= 0) { + qemu_aio_unref(acb); + return ret; + } + + qemu_coroutine_yield(); + + return acb->ret; +} + +static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + SheepdogAIOCB *acb; + int ret; + + acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors); + acb->aiocb_type = AIOCB_READ_UDATA; + acb->aio_done_func = sd_finish_aiocb; + + ret = sd_co_rw_vector(acb); + if (ret <= 0) { + qemu_aio_unref(acb); + return ret; + } + + qemu_coroutine_yield(); + + return acb->ret; +} + +static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs) +{ + BDRVSheepdogState *s = bs->opaque; + SheepdogAIOCB *acb; + AIOReq *aio_req; + + if (s->cache_flags != SD_FLAG_CMD_CACHE) { + return 0; + } + + acb = sd_aio_setup(bs, NULL, 0, 0); + acb->aiocb_type = AIOCB_FLUSH_CACHE; + acb->aio_done_func = sd_finish_aiocb; + + aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id), + 0, 0, 0, false, 0, 0); + QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); + add_aio_request(s, aio_req, NULL, 0, acb->aiocb_type); + + qemu_coroutine_yield(); + return acb->ret; +} + +static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) +{ + Error *local_err = NULL; + BDRVSheepdogState *s = bs->opaque; + int ret, fd; + uint32_t new_vid; + SheepdogInode *inode; + unsigned int datalen; + + DPRINTF("sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " + "is_snapshot %d\n", sn_info->name, sn_info->id_str, + s->name, sn_info->vm_state_size, s->is_snapshot); + + if (s->is_snapshot) { + error_report("You can't create a snapshot of a snapshot VDI, " + "%s (%" PRIu32 ").", s->name, s->inode.vdi_id); + + return -EINVAL; + } + + DPRINTF("%s %s\n", sn_info->name, sn_info->id_str); + + s->inode.vm_state_size = sn_info->vm_state_size; + s->inode.vm_clock_nsec = sn_info->vm_clock_nsec; + /* It appears that inode.tag does not require a NUL terminator, + * which means this use of strncpy is ok. + */ + strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag)); + /* we don't need to update entire object */ + datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + inode = g_malloc(datalen); + + /* refresh inode. */ + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + ret = fd; + goto cleanup; + } + + ret = write_object(fd, s->aio_context, (char *)&s->inode, + vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies, + datalen, 0, false, s->cache_flags); + if (ret < 0) { + error_report("failed to write snapshot's inode."); + goto cleanup; + } + + ret = do_sd_create(s, &new_vid, 1, &local_err); + if (ret < 0) { + error_report("failed to create inode for snapshot: %s", + error_get_pretty(local_err)); + error_free(local_err); + goto cleanup; + } + + ret = read_object(fd, s->aio_context, (char *)inode, + vid_to_vdi_oid(new_vid), s->inode.nr_copies, datalen, 0, + s->cache_flags); + + if (ret < 0) { + error_report("failed to read new inode info. %s", strerror(errno)); + goto cleanup; + } + + memcpy(&s->inode, inode, datalen); + DPRINTF("s->inode: name %s snap_id %x oid %x\n", + s->inode.name, s->inode.snap_id, s->inode.vdi_id); + +cleanup: + g_free(inode); + closesocket(fd); + return ret; +} + +/* + * We implement rollback(loadvm) operation to the specified snapshot by + * 1) switch to the snapshot + * 2) rely on sd_create_branch to delete working VDI and + * 3) create a new working VDI based on the specified snapshot + */ +static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) +{ + BDRVSheepdogState *s = bs->opaque; + BDRVSheepdogState *old_s; + char tag[SD_MAX_VDI_TAG_LEN]; + uint32_t snapid = 0; + int ret = 0; + + old_s = g_new(BDRVSheepdogState, 1); + + memcpy(old_s, s, sizeof(BDRVSheepdogState)); + + snapid = strtoul(snapshot_id, NULL, 10); + if (snapid) { + tag[0] = 0; + } else { + pstrcpy(tag, sizeof(tag), snapshot_id); + } + + ret = reload_inode(s, snapid, tag); + if (ret) { + goto out; + } + + ret = sd_create_branch(s); + if (ret) { + goto out; + } + + g_free(old_s); + + return 0; +out: + /* recover bdrv_sd_state */ + memcpy(s, old_s, sizeof(BDRVSheepdogState)); + g_free(old_s); + + error_report("failed to open. recover old bdrv_sd_state."); + + return ret; +} + +static int sd_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) +{ + /* FIXME: Delete specified snapshot id. */ + return 0; +} + +static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) +{ + Error *local_err = NULL; + BDRVSheepdogState *s = bs->opaque; + SheepdogReq req; + int fd, nr = 1024, ret, max = BITS_TO_LONGS(SD_NR_VDIS) * sizeof(long); + QEMUSnapshotInfo *sn_tab = NULL; + unsigned wlen, rlen; + int found = 0; + static SheepdogInode inode; + unsigned long *vdi_inuse; + unsigned int start_nr; + uint64_t hval; + uint32_t vid; + + vdi_inuse = g_malloc(max); + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + ret = fd; + goto out; + } + + rlen = max; + wlen = 0; + + memset(&req, 0, sizeof(req)); + + req.opcode = SD_OP_READ_VDIS; + req.data_length = max; + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&req, + vdi_inuse, &wlen, &rlen); + + closesocket(fd); + if (ret) { + goto out; + } + + sn_tab = g_new0(QEMUSnapshotInfo, nr); + + /* calculate a vdi id with hash function */ + hval = fnv_64a_buf(s->name, strlen(s->name), FNV1A_64_INIT); + start_nr = hval & (SD_NR_VDIS - 1); + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + ret = fd; + goto out; + } + + for (vid = start_nr; found < nr; vid = (vid + 1) % SD_NR_VDIS) { + if (!test_bit(vid, vdi_inuse)) { + break; + } + + /* we don't need to read entire object */ + ret = read_object(fd, s->aio_context, (char *)&inode, + vid_to_vdi_oid(vid), + 0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0, + s->cache_flags); + + if (ret) { + continue; + } + + if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) { + sn_tab[found].date_sec = inode.snap_ctime >> 32; + sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff; + sn_tab[found].vm_state_size = inode.vm_state_size; + sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec; + + snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), + "%" PRIu32, inode.snap_id); + pstrcpy(sn_tab[found].name, + MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)), + inode.tag); + found++; + } + } + + closesocket(fd); +out: + *psn_tab = sn_tab; + + g_free(vdi_inuse); + + if (ret < 0) { + return ret; + } + + return found; +} + +static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, + int64_t pos, int size, int load) +{ + Error *local_err = NULL; + bool create; + int fd, ret = 0, remaining = size; + unsigned int data_len; + uint64_t vmstate_oid; + uint64_t offset; + uint32_t vdi_index; + uint32_t vdi_id = load ? s->inode.parent_vdi_id : s->inode.vdi_id; + uint32_t object_size = (UINT32_C(1) << s->inode.block_size_shift); + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + return fd; + } + + while (remaining) { + vdi_index = pos / object_size; + offset = pos % object_size; + + data_len = MIN(remaining, object_size - offset); + + vmstate_oid = vid_to_vmstate_oid(vdi_id, vdi_index); + + create = (offset == 0); + if (load) { + ret = read_object(fd, s->aio_context, (char *)data, vmstate_oid, + s->inode.nr_copies, data_len, offset, + s->cache_flags); + } else { + ret = write_object(fd, s->aio_context, (char *)data, vmstate_oid, + s->inode.nr_copies, data_len, offset, create, + s->cache_flags); + } + + if (ret < 0) { + error_report("failed to save vmstate %s", strerror(errno)); + goto cleanup; + } + + pos += data_len; + data += data_len; + remaining -= data_len; + } + ret = size; +cleanup: + closesocket(fd); + return ret; +} + +static int sd_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t pos) +{ + BDRVSheepdogState *s = bs->opaque; + void *buf; + int ret; + + buf = qemu_blockalign(bs, qiov->size); + qemu_iovec_to_buf(qiov, 0, buf, qiov->size); + ret = do_load_save_vmstate(s, (uint8_t *) buf, pos, qiov->size, 0); + qemu_vfree(buf); + + return ret; +} + +static int sd_load_vmstate(BlockDriverState *bs, uint8_t *data, + int64_t pos, int size) +{ + BDRVSheepdogState *s = bs->opaque; + + return do_load_save_vmstate(s, data, pos, size, 1); +} + + +static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + SheepdogAIOCB *acb; + QEMUIOVector dummy; + BDRVSheepdogState *s = bs->opaque; + int ret; + + if (!s->discard_supported) { + return 0; + } + + acb = sd_aio_setup(bs, &dummy, sector_num, nb_sectors); + acb->aiocb_type = AIOCB_DISCARD_OBJ; + acb->aio_done_func = sd_finish_aiocb; + + ret = sd_co_rw_vector(acb); + if (ret <= 0) { + qemu_aio_unref(acb); + return ret; + } + + qemu_coroutine_yield(); + + return acb->ret; +} + +static coroutine_fn int64_t +sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + int *pnum) +{ + BDRVSheepdogState *s = bs->opaque; + SheepdogInode *inode = &s->inode; + uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); + uint64_t offset = sector_num * BDRV_SECTOR_SIZE; + unsigned long start = offset / object_size, + end = DIV_ROUND_UP((sector_num + nb_sectors) * + BDRV_SECTOR_SIZE, object_size); + unsigned long idx; + int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; + + for (idx = start; idx < end; idx++) { + if (inode->data_vdi_id[idx] == 0) { + break; + } + } + if (idx == start) { + /* Get the longest length of unallocated sectors */ + ret = 0; + for (idx = start + 1; idx < end; idx++) { + if (inode->data_vdi_id[idx] != 0) { + break; + } + } + } + + *pnum = (idx - start) * object_size / BDRV_SECTOR_SIZE; + if (*pnum > nb_sectors) { + *pnum = nb_sectors; + } + return ret; +} + +static int64_t sd_get_allocated_file_size(BlockDriverState *bs) +{ + BDRVSheepdogState *s = bs->opaque; + SheepdogInode *inode = &s->inode; + uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); + unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, object_size); + uint64_t size = 0; + + for (i = 0; i < last; i++) { + if (inode->data_vdi_id[i] == 0) { + continue; + } + size += object_size; + } + return size; +} + +static QemuOptsList sd_create_opts = { + .name = "sheepdog-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(sd_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_BACKING_FILE, + .type = QEMU_OPT_STRING, + .help = "File name of a base image" + }, + { + .name = BLOCK_OPT_PREALLOC, + .type = QEMU_OPT_STRING, + .help = "Preallocation mode (allowed values: off, full)" + }, + { + .name = BLOCK_OPT_REDUNDANCY, + .type = QEMU_OPT_STRING, + .help = "Redundancy of the image" + }, + { + .name = BLOCK_OPT_OBJECT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Object size of the image" + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_sheepdog = { + .format_name = "sheepdog", + .protocol_name = "sheepdog", + .instance_size = sizeof(BDRVSheepdogState), + .bdrv_needs_filename = true, + .bdrv_file_open = sd_open, + .bdrv_close = sd_close, + .bdrv_create = sd_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_getlength = sd_getlength, + .bdrv_get_allocated_file_size = sd_get_allocated_file_size, + .bdrv_truncate = sd_truncate, + + .bdrv_co_readv = sd_co_readv, + .bdrv_co_writev = sd_co_writev, + .bdrv_co_flush_to_disk = sd_co_flush_to_disk, + .bdrv_co_discard = sd_co_discard, + .bdrv_co_get_block_status = sd_co_get_block_status, + + .bdrv_snapshot_create = sd_snapshot_create, + .bdrv_snapshot_goto = sd_snapshot_goto, + .bdrv_snapshot_delete = sd_snapshot_delete, + .bdrv_snapshot_list = sd_snapshot_list, + + .bdrv_save_vmstate = sd_save_vmstate, + .bdrv_load_vmstate = sd_load_vmstate, + + .bdrv_detach_aio_context = sd_detach_aio_context, + .bdrv_attach_aio_context = sd_attach_aio_context, + + .create_opts = &sd_create_opts, +}; + +static BlockDriver bdrv_sheepdog_tcp = { + .format_name = "sheepdog", + .protocol_name = "sheepdog+tcp", + .instance_size = sizeof(BDRVSheepdogState), + .bdrv_needs_filename = true, + .bdrv_file_open = sd_open, + .bdrv_close = sd_close, + .bdrv_create = sd_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_getlength = sd_getlength, + .bdrv_get_allocated_file_size = sd_get_allocated_file_size, + .bdrv_truncate = sd_truncate, + + .bdrv_co_readv = sd_co_readv, + .bdrv_co_writev = sd_co_writev, + .bdrv_co_flush_to_disk = sd_co_flush_to_disk, + .bdrv_co_discard = sd_co_discard, + .bdrv_co_get_block_status = sd_co_get_block_status, + + .bdrv_snapshot_create = sd_snapshot_create, + .bdrv_snapshot_goto = sd_snapshot_goto, + .bdrv_snapshot_delete = sd_snapshot_delete, + .bdrv_snapshot_list = sd_snapshot_list, + + .bdrv_save_vmstate = sd_save_vmstate, + .bdrv_load_vmstate = sd_load_vmstate, + + .bdrv_detach_aio_context = sd_detach_aio_context, + .bdrv_attach_aio_context = sd_attach_aio_context, + + .create_opts = &sd_create_opts, +}; + +static BlockDriver bdrv_sheepdog_unix = { + .format_name = "sheepdog", + .protocol_name = "sheepdog+unix", + .instance_size = sizeof(BDRVSheepdogState), + .bdrv_needs_filename = true, + .bdrv_file_open = sd_open, + .bdrv_close = sd_close, + .bdrv_create = sd_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_getlength = sd_getlength, + .bdrv_get_allocated_file_size = sd_get_allocated_file_size, + .bdrv_truncate = sd_truncate, + + .bdrv_co_readv = sd_co_readv, + .bdrv_co_writev = sd_co_writev, + .bdrv_co_flush_to_disk = sd_co_flush_to_disk, + .bdrv_co_discard = sd_co_discard, + .bdrv_co_get_block_status = sd_co_get_block_status, + + .bdrv_snapshot_create = sd_snapshot_create, + .bdrv_snapshot_goto = sd_snapshot_goto, + .bdrv_snapshot_delete = sd_snapshot_delete, + .bdrv_snapshot_list = sd_snapshot_list, + + .bdrv_save_vmstate = sd_save_vmstate, + .bdrv_load_vmstate = sd_load_vmstate, + + .bdrv_detach_aio_context = sd_detach_aio_context, + .bdrv_attach_aio_context = sd_attach_aio_context, + + .create_opts = &sd_create_opts, +}; + +static void bdrv_sheepdog_init(void) +{ + bdrv_register(&bdrv_sheepdog); + bdrv_register(&bdrv_sheepdog_tcp); + bdrv_register(&bdrv_sheepdog_unix); +} +block_init(bdrv_sheepdog_init); diff --git a/block/snapshot.c b/block/snapshot.c new file mode 100644 index 0000000..50ae610 --- /dev/null +++ b/block/snapshot.c @@ -0,0 +1,357 @@ +/* + * Block layer snapshot related functions + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block/snapshot.h" +#include "block/block_int.h" + +QemuOptsList internal_snapshot_opts = { + .name = "snapshot", + .head = QTAILQ_HEAD_INITIALIZER(internal_snapshot_opts.head), + .desc = { + { + .name = SNAPSHOT_OPT_ID, + .type = QEMU_OPT_STRING, + .help = "snapshot id" + },{ + .name = SNAPSHOT_OPT_NAME, + .type = QEMU_OPT_STRING, + .help = "snapshot name" + },{ + /* end of list */ + } + }, +}; + +int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, + const char *name) +{ + QEMUSnapshotInfo *sn_tab, *sn; + int nb_sns, i, ret; + + ret = -ENOENT; + nb_sns = bdrv_snapshot_list(bs, &sn_tab); + if (nb_sns < 0) { + return ret; + } + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { + *sn_info = *sn; + ret = 0; + break; + } + } + g_free(sn_tab); + return ret; +} + +/** + * Look up an internal snapshot by @id and @name. + * @bs: block device to search + * @id: unique snapshot ID, or NULL + * @name: snapshot name, or NULL + * @sn_info: location to store information on the snapshot found + * @errp: location to store error, will be set only for exception + * + * This function will traverse snapshot list in @bs to search the matching + * one, @id and @name are the matching condition: + * If both @id and @name are specified, find the first one with id @id and + * name @name. + * If only @id is specified, find the first one with id @id. + * If only @name is specified, find the first one with name @name. + * if none is specified, abort(). + * + * Returns: true when a snapshot is found and @sn_info will be filled, false + * when error or not found. If all operation succeed but no matching one is + * found, @errp will NOT be set. + */ +bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, + const char *id, + const char *name, + QEMUSnapshotInfo *sn_info, + Error **errp) +{ + QEMUSnapshotInfo *sn_tab, *sn; + int nb_sns, i; + bool ret = false; + + assert(id || name); + + nb_sns = bdrv_snapshot_list(bs, &sn_tab); + if (nb_sns < 0) { + error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list"); + return false; + } else if (nb_sns == 0) { + return false; + } + + if (id && name) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) { + *sn_info = *sn; + ret = true; + break; + } + } + } else if (id) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->id_str, id)) { + *sn_info = *sn; + ret = true; + break; + } + } + } else if (name) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->name, name)) { + *sn_info = *sn; + ret = true; + break; + } + } + } + + g_free(sn_tab); + return ret; +} + +int bdrv_can_snapshot(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { + return 0; + } + + if (!drv->bdrv_snapshot_create) { + if (bs->file != NULL) { + return bdrv_can_snapshot(bs->file); + } + return 0; + } + + return 1; +} + +int bdrv_snapshot_create(BlockDriverState *bs, + QEMUSnapshotInfo *sn_info) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_snapshot_create) { + return drv->bdrv_snapshot_create(bs, sn_info); + } + if (bs->file) { + return bdrv_snapshot_create(bs->file, sn_info); + } + return -ENOTSUP; +} + +int bdrv_snapshot_goto(BlockDriverState *bs, + const char *snapshot_id) +{ + BlockDriver *drv = bs->drv; + int ret, open_ret; + + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_snapshot_goto) { + return drv->bdrv_snapshot_goto(bs, snapshot_id); + } + + if (bs->file) { + drv->bdrv_close(bs); + ret = bdrv_snapshot_goto(bs->file, snapshot_id); + open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL); + if (open_ret < 0) { + bdrv_unref(bs->file); + bs->drv = NULL; + return open_ret; + } + return ret; + } + + return -ENOTSUP; +} + +/** + * Delete an internal snapshot by @snapshot_id and @name. + * @bs: block device used in the operation + * @snapshot_id: unique snapshot ID, or NULL + * @name: snapshot name, or NULL + * @errp: location to store error + * + * If both @snapshot_id and @name are specified, delete the first one with + * id @snapshot_id and name @name. + * If only @snapshot_id is specified, delete the first one with id + * @snapshot_id. + * If only @name is specified, delete the first one with name @name. + * if none is specified, return -EINVAL. + * + * Returns: 0 on success, -errno on failure. If @bs is not inserted, return + * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs + * does not support internal snapshot deletion, return -ENOTSUP. If @bs does + * not support parameter @snapshot_id or @name, or one of them is not correctly + * specified, return -EINVAL. If @bs can't find one matching @id and @name, + * return -ENOENT. If @errp != NULL, it will always be filled with error + * message on failure. + */ +int bdrv_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); + return -ENOMEDIUM; + } + if (!snapshot_id && !name) { + error_setg(errp, "snapshot_id and name are both NULL"); + return -EINVAL; + } + + /* drain all pending i/o before deleting snapshot */ + bdrv_drain_all(); + + if (drv->bdrv_snapshot_delete) { + return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); + } + if (bs->file) { + return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp); + } + error_setg(errp, "Block format '%s' used by device '%s' " + "does not support internal snapshot deletion", + drv->format_name, bdrv_get_device_name(bs)); + return -ENOTSUP; +} + +void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, + const char *id_or_name, + Error **errp) +{ + int ret; + Error *local_err = NULL; + + ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err); + if (ret == -ENOENT || ret == -EINVAL) { + error_free(local_err); + local_err = NULL; + ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err); + } + + if (ret < 0) { + error_propagate(errp, local_err); + } +} + +int bdrv_snapshot_list(BlockDriverState *bs, + QEMUSnapshotInfo **psn_info) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_snapshot_list) { + return drv->bdrv_snapshot_list(bs, psn_info); + } + if (bs->file) { + return bdrv_snapshot_list(bs->file, psn_info); + } + return -ENOTSUP; +} + +/** + * Temporarily load an internal snapshot by @snapshot_id and @name. + * @bs: block device used in the operation + * @snapshot_id: unique snapshot ID, or NULL + * @name: snapshot name, or NULL + * @errp: location to store error + * + * If both @snapshot_id and @name are specified, load the first one with + * id @snapshot_id and name @name. + * If only @snapshot_id is specified, load the first one with id + * @snapshot_id. + * If only @name is specified, load the first one with name @name. + * if none is specified, return -EINVAL. + * + * Returns: 0 on success, -errno on fail. If @bs is not inserted, return + * -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support + * internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and + * @name, return -ENOENT. If @errp != NULL, it will always be filled on + * failure. + */ +int bdrv_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) +{ + BlockDriver *drv = bs->drv; + + if (!drv) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); + return -ENOMEDIUM; + } + if (!snapshot_id && !name) { + error_setg(errp, "snapshot_id and name are both NULL"); + return -EINVAL; + } + if (!bs->read_only) { + error_setg(errp, "Device is not readonly"); + return -EINVAL; + } + if (drv->bdrv_snapshot_load_tmp) { + return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp); + } + error_setg(errp, "Block format '%s' used by device '%s' " + "does not support temporarily loading internal snapshots", + drv->format_name, bdrv_get_device_name(bs)); + return -ENOTSUP; +} + +int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, + const char *id_or_name, + Error **errp) +{ + int ret; + Error *local_err = NULL; + + ret = bdrv_snapshot_load_tmp(bs, id_or_name, NULL, &local_err); + if (ret == -ENOENT || ret == -EINVAL) { + error_free(local_err); + local_err = NULL; + ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err); + } + + if (local_err) { + error_propagate(errp, local_err); + } + + return ret; +} diff --git a/block/ssh.c b/block/ssh.c new file mode 100644 index 0000000..f466cbf --- /dev/null +++ b/block/ssh.c @@ -0,0 +1,1112 @@ +/* + * Secure Shell (ssh) backend for QEMU. + * + * Copyright (C) 2013 Red Hat Inc., Richard W.M. Jones + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include "block/block_int.h" +#include "qemu/sockets.h" +#include "qemu/uri.h" +#include "qapi/qmp/qint.h" + +/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in + * this block driver code. + * + * TRACE_LIBSSH2= enables tracing in libssh2 itself. Note + * that this requires that libssh2 was specially compiled with the + * `./configure --enable-debug' option, so most likely you will have + * to compile it yourself. The meaning of is described + * here: http://www.libssh2.org/libssh2_trace.html + */ +#define DEBUG_SSH 0 +#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */ + +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_SSH) { \ + fprintf(stderr, "ssh: %-15s " fmt "\n", \ + __func__, ##__VA_ARGS__); \ + } \ + } while (0) + +typedef struct BDRVSSHState { + /* Coroutine. */ + CoMutex lock; + + /* SSH connection. */ + int sock; /* socket */ + LIBSSH2_SESSION *session; /* ssh session */ + LIBSSH2_SFTP *sftp; /* sftp session */ + LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */ + + /* See ssh_seek() function below. */ + int64_t offset; + bool offset_op_read; + + /* File attributes at open. We try to keep the .filesize field + * updated if it changes (eg by writing at the end of the file). + */ + LIBSSH2_SFTP_ATTRIBUTES attrs; + + /* Used to warn if 'flush' is not supported. */ + char *hostport; + bool unsafe_flush_warning; +} BDRVSSHState; + +static void ssh_state_init(BDRVSSHState *s) +{ + memset(s, 0, sizeof *s); + s->sock = -1; + s->offset = -1; + qemu_co_mutex_init(&s->lock); +} + +static void ssh_state_free(BDRVSSHState *s) +{ + g_free(s->hostport); + if (s->sftp_handle) { + libssh2_sftp_close(s->sftp_handle); + } + if (s->sftp) { + libssh2_sftp_shutdown(s->sftp); + } + if (s->session) { + libssh2_session_disconnect(s->session, + "from qemu ssh client: " + "user closed the connection"); + libssh2_session_free(s->session); + } + if (s->sock >= 0) { + close(s->sock); + } +} + +static void GCC_FMT_ATTR(3, 4) +session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...) +{ + va_list args; + char *msg; + + va_start(args, fs); + msg = g_strdup_vprintf(fs, args); + va_end(args); + + if (s->session) { + char *ssh_err; + int ssh_err_code; + + /* This is not an errno. See . */ + ssh_err_code = libssh2_session_last_error(s->session, + &ssh_err, NULL, 0); + error_setg(errp, "%s: %s (libssh2 error code: %d)", + msg, ssh_err, ssh_err_code); + } else { + error_setg(errp, "%s", msg); + } + g_free(msg); +} + +static void GCC_FMT_ATTR(3, 4) +sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...) +{ + va_list args; + char *msg; + + va_start(args, fs); + msg = g_strdup_vprintf(fs, args); + va_end(args); + + if (s->sftp) { + char *ssh_err; + int ssh_err_code; + unsigned long sftp_err_code; + + /* This is not an errno. See . */ + ssh_err_code = libssh2_session_last_error(s->session, + &ssh_err, NULL, 0); + /* See . */ + sftp_err_code = libssh2_sftp_last_error((s)->sftp); + + error_setg(errp, + "%s: %s (libssh2 error code: %d, sftp error code: %lu)", + msg, ssh_err, ssh_err_code, sftp_err_code); + } else { + error_setg(errp, "%s", msg); + } + g_free(msg); +} + +static void GCC_FMT_ATTR(2, 3) +sftp_error_report(BDRVSSHState *s, const char *fs, ...) +{ + va_list args; + + va_start(args, fs); + error_vprintf(fs, args); + + if ((s)->sftp) { + char *ssh_err; + int ssh_err_code; + unsigned long sftp_err_code; + + /* This is not an errno. See . */ + ssh_err_code = libssh2_session_last_error(s->session, + &ssh_err, NULL, 0); + /* See . */ + sftp_err_code = libssh2_sftp_last_error((s)->sftp); + + error_printf(": %s (libssh2 error code: %d, sftp error code: %lu)", + ssh_err, ssh_err_code, sftp_err_code); + } + + va_end(args); + error_printf("\n"); +} + +static int parse_uri(const char *filename, QDict *options, Error **errp) +{ + URI *uri = NULL; + QueryParams *qp = NULL; + int i; + + uri = uri_parse(filename); + if (!uri) { + return -EINVAL; + } + + if (strcmp(uri->scheme, "ssh") != 0) { + error_setg(errp, "URI scheme must be 'ssh'"); + goto err; + } + + if (!uri->server || strcmp(uri->server, "") == 0) { + error_setg(errp, "missing hostname in URI"); + goto err; + } + + if (!uri->path || strcmp(uri->path, "") == 0) { + error_setg(errp, "missing remote path in URI"); + goto err; + } + + qp = query_params_parse(uri->query); + if (!qp) { + error_setg(errp, "could not parse query parameters"); + goto err; + } + + if(uri->user && strcmp(uri->user, "") != 0) { + qdict_put(options, "user", qstring_from_str(uri->user)); + } + + qdict_put(options, "host", qstring_from_str(uri->server)); + + if (uri->port) { + qdict_put(options, "port", qint_from_int(uri->port)); + } + + qdict_put(options, "path", qstring_from_str(uri->path)); + + /* Pick out any query parameters that we understand, and ignore + * the rest. + */ + for (i = 0; i < qp->n; ++i) { + if (strcmp(qp->p[i].name, "host_key_check") == 0) { + qdict_put(options, "host_key_check", + qstring_from_str(qp->p[i].value)); + } + } + + query_params_free(qp); + uri_free(uri); + return 0; + + err: + if (qp) { + query_params_free(qp); + } + if (uri) { + uri_free(uri); + } + return -EINVAL; +} + +static void ssh_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + if (qdict_haskey(options, "user") || + qdict_haskey(options, "host") || + qdict_haskey(options, "port") || + qdict_haskey(options, "path") || + qdict_haskey(options, "host_key_check")) { + error_setg(errp, "user, host, port, path, host_key_check cannot be used at the same time as a file option"); + return; + } + + parse_uri(filename, options, errp); +} + +static int check_host_key_knownhosts(BDRVSSHState *s, + const char *host, int port, Error **errp) +{ + const char *home; + char *knh_file = NULL; + LIBSSH2_KNOWNHOSTS *knh = NULL; + struct libssh2_knownhost *found; + int ret, r; + const char *hostkey; + size_t len; + int type; + + hostkey = libssh2_session_hostkey(s->session, &len, &type); + if (!hostkey) { + ret = -EINVAL; + session_error_setg(errp, s, "failed to read remote host key"); + goto out; + } + + knh = libssh2_knownhost_init(s->session); + if (!knh) { + ret = -EINVAL; + session_error_setg(errp, s, + "failed to initialize known hosts support"); + goto out; + } + + home = getenv("HOME"); + if (home) { + knh_file = g_strdup_printf("%s/.ssh/known_hosts", home); + } else { + knh_file = g_strdup_printf("/root/.ssh/known_hosts"); + } + + /* Read all known hosts from OpenSSH-style known_hosts file. */ + libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH); + + r = libssh2_knownhost_checkp(knh, host, port, hostkey, len, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW, + &found); + switch (r) { + case LIBSSH2_KNOWNHOST_CHECK_MATCH: + /* OK */ + DPRINTF("host key OK: %s", found->key); + break; + case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: + ret = -EINVAL; + session_error_setg(errp, s, + "host key does not match the one in known_hosts" + " (found key %s)", found->key); + goto out; + case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: + ret = -EINVAL; + session_error_setg(errp, s, "no host key was found in known_hosts"); + goto out; + case LIBSSH2_KNOWNHOST_CHECK_FAILURE: + ret = -EINVAL; + session_error_setg(errp, s, + "failure matching the host key with known_hosts"); + goto out; + default: + ret = -EINVAL; + session_error_setg(errp, s, "unknown error matching the host key" + " with known_hosts (%d)", r); + goto out; + } + + /* known_hosts checking successful. */ + ret = 0; + + out: + if (knh != NULL) { + libssh2_knownhost_free(knh); + } + g_free(knh_file); + return ret; +} + +static unsigned hex2decimal(char ch) +{ + if (ch >= '0' && ch <= '9') { + return (ch - '0'); + } else if (ch >= 'a' && ch <= 'f') { + return 10 + (ch - 'a'); + } else if (ch >= 'A' && ch <= 'F') { + return 10 + (ch - 'A'); + } + + return -1; +} + +/* Compare the binary fingerprint (hash of host key) with the + * host_key_check parameter. + */ +static int compare_fingerprint(const unsigned char *fingerprint, size_t len, + const char *host_key_check) +{ + unsigned c; + + while (len > 0) { + while (*host_key_check == ':') + host_key_check++; + if (!qemu_isxdigit(host_key_check[0]) || + !qemu_isxdigit(host_key_check[1])) + return 1; + c = hex2decimal(host_key_check[0]) * 16 + + hex2decimal(host_key_check[1]); + if (c - *fingerprint != 0) + return c - *fingerprint; + fingerprint++; + len--; + host_key_check += 2; + } + return *host_key_check - '\0'; +} + +static int +check_host_key_hash(BDRVSSHState *s, const char *hash, + int hash_type, size_t fingerprint_len, Error **errp) +{ + const char *fingerprint; + + fingerprint = libssh2_hostkey_hash(s->session, hash_type); + if (!fingerprint) { + session_error_setg(errp, s, "failed to read remote host key"); + return -EINVAL; + } + + if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len, + hash) != 0) { + error_setg(errp, "remote host key does not match host_key_check '%s'", + hash); + return -EPERM; + } + + return 0; +} + +static int check_host_key(BDRVSSHState *s, const char *host, int port, + const char *host_key_check, Error **errp) +{ + /* host_key_check=no */ + if (strcmp(host_key_check, "no") == 0) { + return 0; + } + + /* host_key_check=md5:xx:yy:zz:... */ + if (strncmp(host_key_check, "md5:", 4) == 0) { + return check_host_key_hash(s, &host_key_check[4], + LIBSSH2_HOSTKEY_HASH_MD5, 16, errp); + } + + /* host_key_check=sha1:xx:yy:zz:... */ + if (strncmp(host_key_check, "sha1:", 5) == 0) { + return check_host_key_hash(s, &host_key_check[5], + LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp); + } + + /* host_key_check=yes */ + if (strcmp(host_key_check, "yes") == 0) { + return check_host_key_knownhosts(s, host, port, errp); + } + + error_setg(errp, "unknown host_key_check setting (%s)", host_key_check); + return -EINVAL; +} + +static int authenticate(BDRVSSHState *s, const char *user, Error **errp) +{ + int r, ret; + const char *userauthlist; + LIBSSH2_AGENT *agent = NULL; + struct libssh2_agent_publickey *identity; + struct libssh2_agent_publickey *prev_identity = NULL; + + userauthlist = libssh2_userauth_list(s->session, user, strlen(user)); + if (strstr(userauthlist, "publickey") == NULL) { + ret = -EPERM; + error_setg(errp, + "remote server does not support \"publickey\" authentication"); + goto out; + } + + /* Connect to ssh-agent and try each identity in turn. */ + agent = libssh2_agent_init(s->session); + if (!agent) { + ret = -EINVAL; + session_error_setg(errp, s, "failed to initialize ssh-agent support"); + goto out; + } + if (libssh2_agent_connect(agent)) { + ret = -ECONNREFUSED; + session_error_setg(errp, s, "failed to connect to ssh-agent"); + goto out; + } + if (libssh2_agent_list_identities(agent)) { + ret = -EINVAL; + session_error_setg(errp, s, + "failed requesting identities from ssh-agent"); + goto out; + } + + for(;;) { + r = libssh2_agent_get_identity(agent, &identity, prev_identity); + if (r == 1) { /* end of list */ + break; + } + if (r < 0) { + ret = -EINVAL; + session_error_setg(errp, s, + "failed to obtain identity from ssh-agent"); + goto out; + } + r = libssh2_agent_userauth(agent, user, identity); + if (r == 0) { + /* Authenticated! */ + ret = 0; + goto out; + } + /* Failed to authenticate with this identity, try the next one. */ + prev_identity = identity; + } + + ret = -EPERM; + error_setg(errp, "failed to authenticate using publickey authentication " + "and the identities held by your ssh-agent"); + + out: + if (agent != NULL) { + /* Note: libssh2 implementation implicitly calls + * libssh2_agent_disconnect if necessary. + */ + libssh2_agent_free(agent); + } + + return ret; +} + +static int connect_to_ssh(BDRVSSHState *s, QDict *options, + int ssh_flags, int creat_mode, Error **errp) +{ + int r, ret; + const char *host, *user, *path, *host_key_check; + int port; + + if (!qdict_haskey(options, "host")) { + ret = -EINVAL; + error_setg(errp, "No hostname was specified"); + goto err; + } + host = qdict_get_str(options, "host"); + + if (qdict_haskey(options, "port")) { + port = qdict_get_int(options, "port"); + } else { + port = 22; + } + + if (!qdict_haskey(options, "path")) { + ret = -EINVAL; + error_setg(errp, "No path was specified"); + goto err; + } + path = qdict_get_str(options, "path"); + + if (qdict_haskey(options, "user")) { + user = qdict_get_str(options, "user"); + } else { + user = g_get_user_name(); + if (!user) { + error_setg_errno(errp, errno, "Can't get user name"); + ret = -errno; + goto err; + } + } + + if (qdict_haskey(options, "host_key_check")) { + host_key_check = qdict_get_str(options, "host_key_check"); + } else { + host_key_check = "yes"; + } + + /* Construct the host:port name for inet_connect. */ + g_free(s->hostport); + s->hostport = g_strdup_printf("%s:%d", host, port); + + /* Open the socket and connect. */ + s->sock = inet_connect(s->hostport, errp); + if (s->sock < 0) { + ret = -errno; + goto err; + } + + /* Create SSH session. */ + s->session = libssh2_session_init(); + if (!s->session) { + ret = -EINVAL; + session_error_setg(errp, s, "failed to initialize libssh2 session"); + goto err; + } + +#if TRACE_LIBSSH2 != 0 + libssh2_trace(s->session, TRACE_LIBSSH2); +#endif + + r = libssh2_session_handshake(s->session, s->sock); + if (r != 0) { + ret = -EINVAL; + session_error_setg(errp, s, "failed to establish SSH session"); + goto err; + } + + /* Check the remote host's key against known_hosts. */ + ret = check_host_key(s, host, port, host_key_check, errp); + if (ret < 0) { + goto err; + } + + /* Authenticate. */ + ret = authenticate(s, user, errp); + if (ret < 0) { + goto err; + } + + /* Start SFTP. */ + s->sftp = libssh2_sftp_init(s->session); + if (!s->sftp) { + session_error_setg(errp, s, "failed to initialize sftp handle"); + ret = -EINVAL; + goto err; + } + + /* Open the remote file. */ + DPRINTF("opening file %s flags=0x%x creat_mode=0%o", + path, ssh_flags, creat_mode); + s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode); + if (!s->sftp_handle) { + session_error_setg(errp, s, "failed to open remote file '%s'", path); + ret = -EINVAL; + goto err; + } + + r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs); + if (r < 0) { + sftp_error_setg(errp, s, "failed to read file attributes"); + return -EINVAL; + } + + /* Delete the options we've used; any not deleted will cause the + * block layer to give an error about unused options. + */ + qdict_del(options, "host"); + qdict_del(options, "port"); + qdict_del(options, "user"); + qdict_del(options, "path"); + qdict_del(options, "host_key_check"); + + return 0; + + err: + if (s->sftp_handle) { + libssh2_sftp_close(s->sftp_handle); + } + s->sftp_handle = NULL; + if (s->sftp) { + libssh2_sftp_shutdown(s->sftp); + } + s->sftp = NULL; + if (s->session) { + libssh2_session_disconnect(s->session, + "from qemu ssh client: " + "error opening connection"); + libssh2_session_free(s->session); + } + s->session = NULL; + + return ret; +} + +static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, + Error **errp) +{ + BDRVSSHState *s = bs->opaque; + int ret; + int ssh_flags; + + ssh_state_init(s); + + ssh_flags = LIBSSH2_FXF_READ; + if (bdrv_flags & BDRV_O_RDWR) { + ssh_flags |= LIBSSH2_FXF_WRITE; + } + + /* Start up SSH. */ + ret = connect_to_ssh(s, options, ssh_flags, 0, errp); + if (ret < 0) { + goto err; + } + + /* Go non-blocking. */ + libssh2_session_set_blocking(s->session, 0); + + return 0; + + err: + if (s->sock >= 0) { + close(s->sock); + } + s->sock = -1; + + return ret; +} + +static QemuOptsList ssh_create_opts = { + .name = "ssh-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(ssh_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { /* end of list */ } + } +}; + +static int ssh_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int r, ret; + int64_t total_size = 0; + QDict *uri_options = NULL; + BDRVSSHState s; + ssize_t r2; + char c[1] = { '\0' }; + + ssh_state_init(&s); + + /* Get desired file size. */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + DPRINTF("total_size=%" PRIi64, total_size); + + uri_options = qdict_new(); + r = parse_uri(filename, uri_options, errp); + if (r < 0) { + ret = r; + goto out; + } + + r = connect_to_ssh(&s, uri_options, + LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE| + LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, + 0644, errp); + if (r < 0) { + ret = r; + goto out; + } + + if (total_size > 0) { + libssh2_sftp_seek64(s.sftp_handle, total_size-1); + r2 = libssh2_sftp_write(s.sftp_handle, c, 1); + if (r2 < 0) { + sftp_error_setg(errp, &s, "truncate failed"); + ret = -EINVAL; + goto out; + } + s.attrs.filesize = total_size; + } + + ret = 0; + + out: + ssh_state_free(&s); + if (uri_options != NULL) { + QDECREF(uri_options); + } + return ret; +} + +static void ssh_close(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + + ssh_state_free(s); +} + +static int ssh_has_zero_init(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + /* Assume false, unless we can positively prove it's true. */ + int has_zero_init = 0; + + if (s->attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + if (s->attrs.permissions & LIBSSH2_SFTP_S_IFREG) { + has_zero_init = 1; + } + } + + return has_zero_init; +} + +static void restart_coroutine(void *opaque) +{ + Coroutine *co = opaque; + + DPRINTF("co=%p", co); + + qemu_coroutine_enter(co, NULL); +} + +static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs) +{ + int r; + IOHandler *rd_handler = NULL, *wr_handler = NULL; + Coroutine *co = qemu_coroutine_self(); + + r = libssh2_session_block_directions(s->session); + + if (r & LIBSSH2_SESSION_BLOCK_INBOUND) { + rd_handler = restart_coroutine; + } + if (r & LIBSSH2_SESSION_BLOCK_OUTBOUND) { + wr_handler = restart_coroutine; + } + + DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock, + rd_handler, wr_handler); + + aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, + rd_handler, wr_handler, co); +} + +static coroutine_fn void clear_fd_handler(BDRVSSHState *s, + BlockDriverState *bs) +{ + DPRINTF("s->sock=%d", s->sock); + aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, NULL, NULL, NULL); +} + +/* A non-blocking call returned EAGAIN, so yield, ensuring the + * handlers are set up so that we'll be rescheduled when there is an + * interesting event on the socket. + */ +static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs) +{ + set_fd_handler(s, bs); + qemu_coroutine_yield(); + clear_fd_handler(s, bs); +} + +/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position + * in the remote file. Notice that it just updates a field in the + * sftp_handle structure, so there is no network traffic and it cannot + * fail. + * + * However, `libssh2_sftp_seek64' does have a catastrophic effect on + * performance since it causes the handle to throw away all in-flight + * reads and buffered readahead data. Therefore this function tries + * to be intelligent about when to call the underlying libssh2 function. + */ +#define SSH_SEEK_WRITE 0 +#define SSH_SEEK_READ 1 +#define SSH_SEEK_FORCE 2 + +static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags) +{ + bool op_read = (flags & SSH_SEEK_READ) != 0; + bool force = (flags & SSH_SEEK_FORCE) != 0; + + if (force || op_read != s->offset_op_read || offset != s->offset) { + DPRINTF("seeking to offset=%" PRIi64, offset); + libssh2_sftp_seek64(s->sftp_handle, offset); + s->offset = offset; + s->offset_op_read = op_read; + } +} + +static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, + int64_t offset, size_t size, + QEMUIOVector *qiov) +{ + ssize_t r; + size_t got; + char *buf, *end_of_vec; + struct iovec *i; + + DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); + + ssh_seek(s, offset, SSH_SEEK_READ); + + /* This keeps track of the current iovec element ('i'), where we + * will write to next ('buf'), and the end of the current iovec + * ('end_of_vec'). + */ + i = &qiov->iov[0]; + buf = i->iov_base; + end_of_vec = i->iov_base + i->iov_len; + + /* libssh2 has a hard-coded limit of 2000 bytes per request, + * although it will also do readahead behind our backs. Therefore + * we may have to do repeated reads here until we have read 'size' + * bytes. + */ + for (got = 0; got < size; ) { + again: + DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf); + r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf); + DPRINTF("sftp_read returned %zd", r); + + if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { + co_yield(s, bs); + goto again; + } + if (r < 0) { + sftp_error_report(s, "read failed"); + s->offset = -1; + return -EIO; + } + if (r == 0) { + /* EOF: Short read so pad the buffer with zeroes and return it. */ + qemu_iovec_memset(qiov, got, 0, size - got); + return 0; + } + + got += r; + buf += r; + s->offset += r; + if (buf >= end_of_vec && got < size) { + i++; + buf = i->iov_base; + end_of_vec = i->iov_base + i->iov_len; + } + } + + return 0; +} + +static coroutine_fn int ssh_co_readv(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVSSHState *s = bs->opaque; + int ret; + + qemu_co_mutex_lock(&s->lock); + ret = ssh_read(s, bs, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, qiov); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + +static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, + int64_t offset, size_t size, + QEMUIOVector *qiov) +{ + ssize_t r; + size_t written; + char *buf, *end_of_vec; + struct iovec *i; + + DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); + + ssh_seek(s, offset, SSH_SEEK_WRITE); + + /* This keeps track of the current iovec element ('i'), where we + * will read from next ('buf'), and the end of the current iovec + * ('end_of_vec'). + */ + i = &qiov->iov[0]; + buf = i->iov_base; + end_of_vec = i->iov_base + i->iov_len; + + for (written = 0; written < size; ) { + again: + DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf); + r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf); + DPRINTF("sftp_write returned %zd", r); + + if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { + co_yield(s, bs); + goto again; + } + if (r < 0) { + sftp_error_report(s, "write failed"); + s->offset = -1; + return -EIO; + } + /* The libssh2 API is very unclear about this. A comment in + * the code says "nothing was acked, and no EAGAIN was + * received!" which apparently means that no data got sent + * out, and the underlying channel didn't return any EAGAIN + * indication. I think this is a bug in either libssh2 or + * OpenSSH (server-side). In any case, forcing a seek (to + * discard libssh2 internal buffers), and then trying again + * works for me. + */ + if (r == 0) { + ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE); + co_yield(s, bs); + goto again; + } + + written += r; + buf += r; + s->offset += r; + if (buf >= end_of_vec && written < size) { + i++; + buf = i->iov_base; + end_of_vec = i->iov_base + i->iov_len; + } + + if (offset + written > s->attrs.filesize) + s->attrs.filesize = offset + written; + } + + return 0; +} + +static coroutine_fn int ssh_co_writev(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVSSHState *s = bs->opaque; + int ret; + + qemu_co_mutex_lock(&s->lock); + ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, qiov); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + +static void unsafe_flush_warning(BDRVSSHState *s, const char *what) +{ + if (!s->unsafe_flush_warning) { + error_report("warning: ssh server %s does not support fsync", + s->hostport); + if (what) { + error_report("to support fsync, you need %s", what); + } + s->unsafe_flush_warning = true; + } +} + +#ifdef HAS_LIBSSH2_SFTP_FSYNC + +static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs) +{ + int r; + + DPRINTF("fsync"); + again: + r = libssh2_sftp_fsync(s->sftp_handle); + if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { + co_yield(s, bs); + goto again; + } + if (r == LIBSSH2_ERROR_SFTP_PROTOCOL && + libssh2_sftp_last_error(s->sftp) == LIBSSH2_FX_OP_UNSUPPORTED) { + unsafe_flush_warning(s, "OpenSSH >= 6.3"); + return 0; + } + if (r < 0) { + sftp_error_report(s, "fsync failed"); + return -EIO; + } + + return 0; +} + +static coroutine_fn int ssh_co_flush(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + int ret; + + qemu_co_mutex_lock(&s->lock); + ret = ssh_flush(s, bs); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + +#else /* !HAS_LIBSSH2_SFTP_FSYNC */ + +static coroutine_fn int ssh_co_flush(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + + unsafe_flush_warning(s, "libssh2 >= 1.4.4"); + return 0; +} + +#endif /* !HAS_LIBSSH2_SFTP_FSYNC */ + +static int64_t ssh_getlength(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + int64_t length; + + /* Note we cannot make a libssh2 call here. */ + length = (int64_t) s->attrs.filesize; + DPRINTF("length=%" PRIi64, length); + + return length; +} + +static BlockDriver bdrv_ssh = { + .format_name = "ssh", + .protocol_name = "ssh", + .instance_size = sizeof(BDRVSSHState), + .bdrv_parse_filename = ssh_parse_filename, + .bdrv_file_open = ssh_file_open, + .bdrv_create = ssh_create, + .bdrv_close = ssh_close, + .bdrv_has_zero_init = ssh_has_zero_init, + .bdrv_co_readv = ssh_co_readv, + .bdrv_co_writev = ssh_co_writev, + .bdrv_getlength = ssh_getlength, + .bdrv_co_flush_to_disk = ssh_co_flush, + .create_opts = &ssh_create_opts, +}; + +static void bdrv_ssh_init(void) +{ + int r; + + r = libssh2_init(0); + if (r != 0) { + fprintf(stderr, "libssh2 initialization failed, %d\n", r); + exit(EXIT_FAILURE); + } + + bdrv_register(&bdrv_ssh); +} + +block_init(bdrv_ssh_init); diff --git a/block/stream.c b/block/stream.c new file mode 100644 index 0000000..a628901 --- /dev/null +++ b/block/stream.c @@ -0,0 +1,269 @@ +/* + * Image streaming + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "trace.h" +#include "block/block_int.h" +#include "block/blockjob.h" +#include "qemu/ratelimit.h" + +enum { + /* + * Size of data buffer for populating the image file. This should be large + * enough to process multiple clusters in a single call, so that populating + * contiguous regions of the image is efficient. + */ + STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */ +}; + +#define SLICE_TIME 100000000ULL /* ns */ + +typedef struct StreamBlockJob { + BlockJob common; + RateLimit limit; + BlockDriverState *base; + BlockdevOnError on_error; + char *backing_file_str; +} StreamBlockJob; + +static int coroutine_fn stream_populate(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + void *buf) +{ + struct iovec iov = { + .iov_base = buf, + .iov_len = nb_sectors * BDRV_SECTOR_SIZE, + }; + QEMUIOVector qiov; + + qemu_iovec_init_external(&qiov, &iov, 1); + + /* Copy-on-read the unallocated clusters */ + return bdrv_co_copy_on_readv(bs, sector_num, nb_sectors, &qiov); +} + +static void close_unused_images(BlockDriverState *top, BlockDriverState *base, + const char *base_id) +{ + BlockDriverState *intermediate; + intermediate = top->backing_hd; + + /* Must assign before bdrv_delete() to prevent traversing dangling pointer + * while we delete backing image instances. + */ + bdrv_set_backing_hd(top, base); + + while (intermediate) { + BlockDriverState *unused; + + /* reached base */ + if (intermediate == base) { + break; + } + + unused = intermediate; + intermediate = intermediate->backing_hd; + bdrv_set_backing_hd(unused, NULL); + bdrv_unref(unused); + } + + bdrv_refresh_limits(top, NULL); +} + +typedef struct { + int ret; + bool reached_end; +} StreamCompleteData; + +static void stream_complete(BlockJob *job, void *opaque) +{ + StreamBlockJob *s = container_of(job, StreamBlockJob, common); + StreamCompleteData *data = opaque; + BlockDriverState *base = s->base; + + if (!block_job_is_cancelled(&s->common) && data->reached_end && + data->ret == 0) { + const char *base_id = NULL, *base_fmt = NULL; + if (base) { + base_id = s->backing_file_str; + if (base->drv) { + base_fmt = base->drv->format_name; + } + } + data->ret = bdrv_change_backing_file(job->bs, base_id, base_fmt); + close_unused_images(job->bs, base, base_id); + } + + g_free(s->backing_file_str); + block_job_completed(&s->common, data->ret); + g_free(data); +} + +static void coroutine_fn stream_run(void *opaque) +{ + StreamBlockJob *s = opaque; + StreamCompleteData *data; + BlockDriverState *bs = s->common.bs; + BlockDriverState *base = s->base; + int64_t sector_num, end; + int error = 0; + int ret = 0; + int n = 0; + void *buf; + + if (!bs->backing_hd) { + block_job_completed(&s->common, 0); + return; + } + + s->common.len = bdrv_getlength(bs); + if (s->common.len < 0) { + block_job_completed(&s->common, s->common.len); + return; + } + + end = s->common.len >> BDRV_SECTOR_BITS; + buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); + + /* Turn on copy-on-read for the whole block device so that guest read + * requests help us make progress. Only do this when copying the entire + * backing chain since the copy-on-read operation does not take base into + * account. + */ + if (!base) { + bdrv_enable_copy_on_read(bs); + } + + for (sector_num = 0; sector_num < end; sector_num += n) { + uint64_t delay_ns = 0; + bool copy; + +wait: + /* Note that even when no rate limit is applied we need to yield + * with no pending I/O here so that bdrv_drain_all() returns. + */ + block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); + if (block_job_is_cancelled(&s->common)) { + break; + } + + copy = false; + + ret = bdrv_is_allocated(bs, sector_num, + STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n); + if (ret == 1) { + /* Allocated in the top, no need to copy. */ + } else if (ret >= 0) { + /* Copy if allocated in the intermediate images. Limit to the + * known-unallocated area [sector_num, sector_num+n). */ + ret = bdrv_is_allocated_above(bs->backing_hd, base, + sector_num, n, &n); + + /* Finish early if end of backing file has been reached */ + if (ret == 0 && n == 0) { + n = end - sector_num; + } + + copy = (ret == 1); + } + trace_stream_one_iteration(s, sector_num, n, ret); + if (copy) { + if (s->common.speed) { + delay_ns = ratelimit_calculate_delay(&s->limit, n); + if (delay_ns > 0) { + goto wait; + } + } + ret = stream_populate(bs, sector_num, n, buf); + } + if (ret < 0) { + BlockErrorAction action = + block_job_error_action(&s->common, s->common.bs, s->on_error, + true, -ret); + if (action == BLOCK_ERROR_ACTION_STOP) { + n = 0; + continue; + } + if (error == 0) { + error = ret; + } + if (action == BLOCK_ERROR_ACTION_REPORT) { + break; + } + } + ret = 0; + + /* Publish progress */ + s->common.offset += n * BDRV_SECTOR_SIZE; + } + + if (!base) { + bdrv_disable_copy_on_read(bs); + } + + /* Do not remove the backing file if an error was there but ignored. */ + ret = error; + + qemu_vfree(buf); + + /* Modify backing chain and close BDSes in main loop */ + data = g_malloc(sizeof(*data)); + data->ret = ret; + data->reached_end = sector_num == end; + block_job_defer_to_main_loop(&s->common, stream_complete, data); +} + +static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) +{ + StreamBlockJob *s = container_of(job, StreamBlockJob, common); + + if (speed < 0) { + error_set(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } + ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); +} + +static const BlockJobDriver stream_job_driver = { + .instance_size = sizeof(StreamBlockJob), + .job_type = BLOCK_JOB_TYPE_STREAM, + .set_speed = stream_set_speed, +}; + +void stream_start(BlockDriverState *bs, BlockDriverState *base, + const char *backing_file_str, int64_t speed, + BlockdevOnError on_error, + BlockCompletionFunc *cb, + void *opaque, Error **errp) +{ + StreamBlockJob *s; + + if ((on_error == BLOCKDEV_ON_ERROR_STOP || + on_error == BLOCKDEV_ON_ERROR_ENOSPC) && + !bdrv_iostatus_is_enabled(bs)) { + error_set(errp, QERR_INVALID_PARAMETER, "on-error"); + return; + } + + s = block_job_create(&stream_job_driver, bs, speed, cb, opaque, errp); + if (!s) { + return; + } + + s->base = base; + s->backing_file_str = g_strdup(backing_file_str); + + s->on_error = on_error; + s->common.co = qemu_coroutine_create(stream_run); + trace_stream_start(bs, base, s, s->common.co, opaque); + qemu_coroutine_enter(s->common.co, s); +} diff --git a/block/vdi.c b/block/vdi.c new file mode 100644 index 0000000..7642ef3 --- /dev/null +++ b/block/vdi.c @@ -0,0 +1,914 @@ +/* + * Block driver for the Virtual Disk Image (VDI) format + * + * Copyright (c) 2009, 2012 Stefan Weil + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) version 3 or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Reference: + * http://forums.virtualbox.org/viewtopic.php?t=8046 + * + * This driver supports create / read / write operations on VDI images. + * + * Todo (see also TODO in code): + * + * Some features like snapshots are still missing. + * + * Deallocation of zero-filled blocks and shrinking images are missing, too + * (might be added to common block layer). + * + * Allocation of blocks could be optimized (less writes to block map and + * header). + * + * Read and write of adjacent blocks could be done in one operation + * (current code uses one operation per block (1 MiB). + * + * The code is not thread safe (missing locks for changes in header and + * block table, no problem with current QEMU). + * + * Hints: + * + * Blocks (VDI documentation) correspond to clusters (QEMU). + * QEMU's backing files could be implemented using VDI snapshot files (TODO). + * VDI snapshot files may also contain the complete machine state. + * Maybe this machine state can be converted to QEMU PC machine snapshot data. + * + * The driver keeps a block cache (little endian entries) in memory. + * For the standard block size (1 MiB), a 1 TiB disk will use 4 MiB RAM, + * so this seems to be reasonable. + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "migration/migration.h" +#include "block/coroutine.h" + +#if defined(CONFIG_UUID) +#include +#else +/* TODO: move uuid emulation to some central place in QEMU. */ +#include "sysemu/sysemu.h" /* UUID_FMT */ +typedef unsigned char uuid_t[16]; +#endif + +/* Code configuration options. */ + +/* Enable debug messages. */ +//~ #define CONFIG_VDI_DEBUG + +/* Support write operations on VDI images. */ +#define CONFIG_VDI_WRITE + +/* Support non-standard block (cluster) size. This is untested. + * Maybe it will be needed for very large images. + */ +//~ #define CONFIG_VDI_BLOCK_SIZE + +/* Support static (fixed, pre-allocated) images. */ +#define CONFIG_VDI_STATIC_IMAGE + +/* Command line option for static images. */ +#define BLOCK_OPT_STATIC "static" + +#define KiB 1024 +#define MiB (KiB * KiB) + +#define SECTOR_SIZE 512 +#define DEFAULT_CLUSTER_SIZE (1 * MiB) + +#if defined(CONFIG_VDI_DEBUG) +#define logout(fmt, ...) \ + fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__) +#else +#define logout(fmt, ...) ((void)0) +#endif + +/* Image signature. */ +#define VDI_SIGNATURE 0xbeda107f + +/* Image version. */ +#define VDI_VERSION_1_1 0x00010001 + +/* Image type. */ +#define VDI_TYPE_DYNAMIC 1 +#define VDI_TYPE_STATIC 2 + +/* Innotek / SUN images use these strings in header.text: + * "<<< innotek VirtualBox Disk Image >>>\n" + * "<<< Sun xVM VirtualBox Disk Image >>>\n" + * "<<< Sun VirtualBox Disk Image >>>\n" + * The value does not matter, so QEMU created images use a different text. + */ +#define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n" + +/* A never-allocated block; semantically arbitrary content. */ +#define VDI_UNALLOCATED 0xffffffffU + +/* A discarded (no longer allocated) block; semantically zero-filled. */ +#define VDI_DISCARDED 0xfffffffeU + +#define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED) + +/* The bmap will take up VDI_BLOCKS_IN_IMAGE_MAX * sizeof(uint32_t) bytes; since + * the bmap is read and written in a single operation, its size needs to be + * limited to INT_MAX; furthermore, when opening an image, the bmap size is + * rounded up to be aligned on BDRV_SECTOR_SIZE. + * Therefore this should satisfy the following: + * VDI_BLOCKS_IN_IMAGE_MAX * sizeof(uint32_t) + BDRV_SECTOR_SIZE == INT_MAX + 1 + * (INT_MAX + 1 is the first value not representable as an int) + * This guarantees that any value below or equal to the constant will, when + * multiplied by sizeof(uint32_t) and rounded up to a BDRV_SECTOR_SIZE boundary, + * still be below or equal to INT_MAX. */ +#define VDI_BLOCKS_IN_IMAGE_MAX \ + ((unsigned)((INT_MAX + 1u - BDRV_SECTOR_SIZE) / sizeof(uint32_t))) +#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \ + (uint64_t)DEFAULT_CLUSTER_SIZE) + +#if !defined(CONFIG_UUID) +static inline void uuid_generate(uuid_t out) +{ + memset(out, 0, sizeof(uuid_t)); +} + +static inline int uuid_is_null(const uuid_t uu) +{ + uuid_t null_uuid = { 0 }; + return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0; +} + +# if defined(CONFIG_VDI_DEBUG) +static inline void uuid_unparse(const uuid_t uu, char *out) +{ + snprintf(out, 37, UUID_FMT, + uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], + uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); +} +# endif +#endif + +typedef struct { + char text[0x40]; + uint32_t signature; + uint32_t version; + uint32_t header_size; + uint32_t image_type; + uint32_t image_flags; + char description[256]; + uint32_t offset_bmap; + uint32_t offset_data; + uint32_t cylinders; /* disk geometry, unused here */ + uint32_t heads; /* disk geometry, unused here */ + uint32_t sectors; /* disk geometry, unused here */ + uint32_t sector_size; + uint32_t unused1; + uint64_t disk_size; + uint32_t block_size; + uint32_t block_extra; /* unused here */ + uint32_t blocks_in_image; + uint32_t blocks_allocated; + uuid_t uuid_image; + uuid_t uuid_last_snap; + uuid_t uuid_link; + uuid_t uuid_parent; + uint64_t unused2[7]; +} QEMU_PACKED VdiHeader; + +typedef struct { + /* The block map entries are little endian (even in memory). */ + uint32_t *bmap; + /* Size of block (bytes). */ + uint32_t block_size; + /* Size of block (sectors). */ + uint32_t block_sectors; + /* First sector of block map. */ + uint32_t bmap_sector; + /* VDI header (converted to host endianness). */ + VdiHeader header; + + CoMutex write_lock; + + Error *migration_blocker; +} BDRVVdiState; + +/* Change UUID from little endian (IPRT = VirtualBox format) to big endian + * format (network byte order, standard, see RFC 4122) and vice versa. + */ +static void uuid_convert(uuid_t uuid) +{ + bswap32s((uint32_t *)&uuid[0]); + bswap16s((uint16_t *)&uuid[4]); + bswap16s((uint16_t *)&uuid[6]); +} + +static void vdi_header_to_cpu(VdiHeader *header) +{ + le32_to_cpus(&header->signature); + le32_to_cpus(&header->version); + le32_to_cpus(&header->header_size); + le32_to_cpus(&header->image_type); + le32_to_cpus(&header->image_flags); + le32_to_cpus(&header->offset_bmap); + le32_to_cpus(&header->offset_data); + le32_to_cpus(&header->cylinders); + le32_to_cpus(&header->heads); + le32_to_cpus(&header->sectors); + le32_to_cpus(&header->sector_size); + le64_to_cpus(&header->disk_size); + le32_to_cpus(&header->block_size); + le32_to_cpus(&header->block_extra); + le32_to_cpus(&header->blocks_in_image); + le32_to_cpus(&header->blocks_allocated); + uuid_convert(header->uuid_image); + uuid_convert(header->uuid_last_snap); + uuid_convert(header->uuid_link); + uuid_convert(header->uuid_parent); +} + +static void vdi_header_to_le(VdiHeader *header) +{ + cpu_to_le32s(&header->signature); + cpu_to_le32s(&header->version); + cpu_to_le32s(&header->header_size); + cpu_to_le32s(&header->image_type); + cpu_to_le32s(&header->image_flags); + cpu_to_le32s(&header->offset_bmap); + cpu_to_le32s(&header->offset_data); + cpu_to_le32s(&header->cylinders); + cpu_to_le32s(&header->heads); + cpu_to_le32s(&header->sectors); + cpu_to_le32s(&header->sector_size); + cpu_to_le64s(&header->disk_size); + cpu_to_le32s(&header->block_size); + cpu_to_le32s(&header->block_extra); + cpu_to_le32s(&header->blocks_in_image); + cpu_to_le32s(&header->blocks_allocated); + uuid_convert(header->uuid_image); + uuid_convert(header->uuid_last_snap); + uuid_convert(header->uuid_link); + uuid_convert(header->uuid_parent); +} + +#if defined(CONFIG_VDI_DEBUG) +static void vdi_header_print(VdiHeader *header) +{ + char uuid[37]; + logout("text %s", header->text); + logout("signature 0x%08x\n", header->signature); + logout("header size 0x%04x\n", header->header_size); + logout("image type 0x%04x\n", header->image_type); + logout("image flags 0x%04x\n", header->image_flags); + logout("description %s\n", header->description); + logout("offset bmap 0x%04x\n", header->offset_bmap); + logout("offset data 0x%04x\n", header->offset_data); + logout("cylinders 0x%04x\n", header->cylinders); + logout("heads 0x%04x\n", header->heads); + logout("sectors 0x%04x\n", header->sectors); + logout("sector size 0x%04x\n", header->sector_size); + logout("image size 0x%" PRIx64 " B (%" PRIu64 " MiB)\n", + header->disk_size, header->disk_size / MiB); + logout("block size 0x%04x\n", header->block_size); + logout("block extra 0x%04x\n", header->block_extra); + logout("blocks tot. 0x%04x\n", header->blocks_in_image); + logout("blocks all. 0x%04x\n", header->blocks_allocated); + uuid_unparse(header->uuid_image, uuid); + logout("uuid image %s\n", uuid); + uuid_unparse(header->uuid_last_snap, uuid); + logout("uuid snap %s\n", uuid); + uuid_unparse(header->uuid_link, uuid); + logout("uuid link %s\n", uuid); + uuid_unparse(header->uuid_parent, uuid); + logout("uuid parent %s\n", uuid); +} +#endif + +static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + /* TODO: additional checks possible. */ + BDRVVdiState *s = (BDRVVdiState *)bs->opaque; + uint32_t blocks_allocated = 0; + uint32_t block; + uint32_t *bmap; + logout("\n"); + + if (fix) { + return -ENOTSUP; + } + + bmap = g_try_new(uint32_t, s->header.blocks_in_image); + if (s->header.blocks_in_image && bmap == NULL) { + res->check_errors++; + return -ENOMEM; + } + + memset(bmap, 0xff, s->header.blocks_in_image * sizeof(uint32_t)); + + /* Check block map and value of blocks_allocated. */ + for (block = 0; block < s->header.blocks_in_image; block++) { + uint32_t bmap_entry = le32_to_cpu(s->bmap[block]); + if (VDI_IS_ALLOCATED(bmap_entry)) { + if (bmap_entry < s->header.blocks_in_image) { + blocks_allocated++; + if (!VDI_IS_ALLOCATED(bmap[bmap_entry])) { + bmap[bmap_entry] = bmap_entry; + } else { + fprintf(stderr, "ERROR: block index %" PRIu32 + " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry); + res->corruptions++; + } + } else { + fprintf(stderr, "ERROR: block index %" PRIu32 + " too large, is %" PRIu32 "\n", block, bmap_entry); + res->corruptions++; + } + } + } + if (blocks_allocated != s->header.blocks_allocated) { + fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32 + ", should be %" PRIu32 "\n", + blocks_allocated, s->header.blocks_allocated); + res->corruptions++; + } + + g_free(bmap); + + return 0; +} + +static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + /* TODO: vdi_get_info would be needed for machine snapshots. + vm_state_offset is still missing. */ + BDRVVdiState *s = (BDRVVdiState *)bs->opaque; + logout("\n"); + bdi->cluster_size = s->block_size; + bdi->vm_state_offset = 0; + bdi->unallocated_blocks_are_zero = true; + return 0; +} + +static int vdi_make_empty(BlockDriverState *bs) +{ + /* TODO: missing code. */ + logout("\n"); + /* The return value for missing code must be 0, see block.c. */ + return 0; +} + +static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const VdiHeader *header = (const VdiHeader *)buf; + int ret = 0; + + logout("\n"); + + if (buf_size < sizeof(*header)) { + /* Header too small, no VDI. */ + } else if (le32_to_cpu(header->signature) == VDI_SIGNATURE) { + ret = 100; + } + + if (ret == 0) { + logout("no vdi image\n"); + } else { + logout("%s", header->text); + } + + return ret; +} + +static int vdi_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVVdiState *s = bs->opaque; + VdiHeader header; + size_t bmap_size; + int ret; + + logout("\n"); + + ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); + if (ret < 0) { + goto fail; + } + + vdi_header_to_cpu(&header); +#if defined(CONFIG_VDI_DEBUG) + vdi_header_print(&header); +#endif + + if (header.disk_size > VDI_DISK_SIZE_MAX) { + error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64 + ", max supported is 0x%" PRIx64 ")", + header.disk_size, VDI_DISK_SIZE_MAX); + ret = -ENOTSUP; + goto fail; + } + + if (header.disk_size % SECTOR_SIZE != 0) { + /* 'VBoxManage convertfromraw' can create images with odd disk sizes. + We accept them but round the disk size to the next multiple of + SECTOR_SIZE. */ + logout("odd disk size %" PRIu64 " B, round up\n", header.disk_size); + header.disk_size = ROUND_UP(header.disk_size, SECTOR_SIZE); + } + + if (header.signature != VDI_SIGNATURE) { + error_setg(errp, "Image not in VDI format (bad signature %08" PRIx32 + ")", header.signature); + ret = -EINVAL; + goto fail; + } else if (header.version != VDI_VERSION_1_1) { + error_setg(errp, "unsupported VDI image (version %" PRIu32 ".%" PRIu32 + ")", header.version >> 16, header.version & 0xffff); + ret = -ENOTSUP; + goto fail; + } else if (header.offset_bmap % SECTOR_SIZE != 0) { + /* We only support block maps which start on a sector boundary. */ + error_setg(errp, "unsupported VDI image (unaligned block map offset " + "0x%" PRIx32 ")", header.offset_bmap); + ret = -ENOTSUP; + goto fail; + } else if (header.offset_data % SECTOR_SIZE != 0) { + /* We only support data blocks which start on a sector boundary. */ + error_setg(errp, "unsupported VDI image (unaligned data offset 0x%" + PRIx32 ")", header.offset_data); + ret = -ENOTSUP; + goto fail; + } else if (header.sector_size != SECTOR_SIZE) { + error_setg(errp, "unsupported VDI image (sector size %" PRIu32 + " is not %u)", header.sector_size, SECTOR_SIZE); + ret = -ENOTSUP; + goto fail; + } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { + error_setg(errp, "unsupported VDI image (block size %" PRIu32 + " is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE); + ret = -ENOTSUP; + goto fail; + } else if (header.disk_size > + (uint64_t)header.blocks_in_image * header.block_size) { + error_setg(errp, "unsupported VDI image (disk size %" PRIu64 ", " + "image bitmap has room for %" PRIu64 ")", + header.disk_size, + (uint64_t)header.blocks_in_image * header.block_size); + ret = -ENOTSUP; + goto fail; + } else if (!uuid_is_null(header.uuid_link)) { + error_setg(errp, "unsupported VDI image (non-NULL link UUID)"); + ret = -ENOTSUP; + goto fail; + } else if (!uuid_is_null(header.uuid_parent)) { + error_setg(errp, "unsupported VDI image (non-NULL parent UUID)"); + ret = -ENOTSUP; + goto fail; + } else if (header.blocks_in_image > VDI_BLOCKS_IN_IMAGE_MAX) { + error_setg(errp, "unsupported VDI image " + "(too many blocks %u, max is %u)", + header.blocks_in_image, VDI_BLOCKS_IN_IMAGE_MAX); + ret = -ENOTSUP; + goto fail; + } + + bs->total_sectors = header.disk_size / SECTOR_SIZE; + + s->block_size = header.block_size; + s->block_sectors = header.block_size / SECTOR_SIZE; + s->bmap_sector = header.offset_bmap / SECTOR_SIZE; + s->header = header; + + bmap_size = header.blocks_in_image * sizeof(uint32_t); + bmap_size = DIV_ROUND_UP(bmap_size, SECTOR_SIZE); + s->bmap = qemu_try_blockalign(bs->file, bmap_size * SECTOR_SIZE); + if (s->bmap == NULL) { + ret = -ENOMEM; + goto fail; + } + + ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size); + if (ret < 0) { + goto fail_free_bmap; + } + + /* Disable migration when vdi images are used */ + error_setg(&s->migration_blocker, "The vdi format used by node '%s' " + "does not support live migration", + bdrv_get_device_or_node_name(bs)); + migrate_add_blocker(s->migration_blocker); + + qemu_co_mutex_init(&s->write_lock); + + return 0; + + fail_free_bmap: + qemu_vfree(s->bmap); + + fail: + return ret; +} + +static int vdi_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + return 0; +} + +static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */ + BDRVVdiState *s = (BDRVVdiState *)bs->opaque; + size_t bmap_index = sector_num / s->block_sectors; + size_t sector_in_block = sector_num % s->block_sectors; + int n_sectors = s->block_sectors - sector_in_block; + uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]); + uint64_t offset; + int result; + + logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum); + if (n_sectors > nb_sectors) { + n_sectors = nb_sectors; + } + *pnum = n_sectors; + result = VDI_IS_ALLOCATED(bmap_entry); + if (!result) { + return 0; + } + + offset = s->header.offset_data + + (uint64_t)bmap_entry * s->block_size + + sector_in_block * SECTOR_SIZE; + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; +} + +static int vdi_co_read(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors) +{ + BDRVVdiState *s = bs->opaque; + uint32_t bmap_entry; + uint32_t block_index; + uint32_t sector_in_block; + uint32_t n_sectors; + int ret = 0; + + logout("\n"); + + while (ret >= 0 && nb_sectors > 0) { + block_index = sector_num / s->block_sectors; + sector_in_block = sector_num % s->block_sectors; + n_sectors = s->block_sectors - sector_in_block; + if (n_sectors > nb_sectors) { + n_sectors = nb_sectors; + } + + logout("will read %u sectors starting at sector %" PRIu64 "\n", + n_sectors, sector_num); + + /* prepare next AIO request */ + bmap_entry = le32_to_cpu(s->bmap[block_index]); + if (!VDI_IS_ALLOCATED(bmap_entry)) { + /* Block not allocated, return zeros, no need to wait. */ + memset(buf, 0, n_sectors * SECTOR_SIZE); + ret = 0; + } else { + uint64_t offset = s->header.offset_data / SECTOR_SIZE + + (uint64_t)bmap_entry * s->block_sectors + + sector_in_block; + ret = bdrv_read(bs->file, offset, buf, n_sectors); + } + logout("%u sectors read\n", n_sectors); + + nb_sectors -= n_sectors; + sector_num += n_sectors; + buf += n_sectors * SECTOR_SIZE; + } + + return ret; +} + +static int vdi_co_write(BlockDriverState *bs, + int64_t sector_num, const uint8_t *buf, int nb_sectors) +{ + BDRVVdiState *s = bs->opaque; + uint32_t bmap_entry; + uint32_t block_index; + uint32_t sector_in_block; + uint32_t n_sectors; + uint32_t bmap_first = VDI_UNALLOCATED; + uint32_t bmap_last = VDI_UNALLOCATED; + uint8_t *block = NULL; + int ret = 0; + + logout("\n"); + + while (ret >= 0 && nb_sectors > 0) { + block_index = sector_num / s->block_sectors; + sector_in_block = sector_num % s->block_sectors; + n_sectors = s->block_sectors - sector_in_block; + if (n_sectors > nb_sectors) { + n_sectors = nb_sectors; + } + + logout("will write %u sectors starting at sector %" PRIu64 "\n", + n_sectors, sector_num); + + /* prepare next AIO request */ + bmap_entry = le32_to_cpu(s->bmap[block_index]); + if (!VDI_IS_ALLOCATED(bmap_entry)) { + /* Allocate new block and write to it. */ + uint64_t offset; + bmap_entry = s->header.blocks_allocated; + s->bmap[block_index] = cpu_to_le32(bmap_entry); + s->header.blocks_allocated++; + offset = s->header.offset_data / SECTOR_SIZE + + (uint64_t)bmap_entry * s->block_sectors; + if (block == NULL) { + block = g_malloc(s->block_size); + bmap_first = block_index; + } + bmap_last = block_index; + /* Copy data to be written to new block and zero unused parts. */ + memset(block, 0, sector_in_block * SECTOR_SIZE); + memcpy(block + sector_in_block * SECTOR_SIZE, + buf, n_sectors * SECTOR_SIZE); + memset(block + (sector_in_block + n_sectors) * SECTOR_SIZE, 0, + (s->block_sectors - n_sectors - sector_in_block) * SECTOR_SIZE); + + /* Note that this coroutine does not yield anywhere from reading the + * bmap entry until here, so in regards to all the coroutines trying + * to write to this cluster, the one doing the allocation will + * always be the first to try to acquire the lock. + * Therefore, it is also the first that will actually be able to + * acquire the lock and thus the padded cluster is written before + * the other coroutines can write to the affected area. */ + qemu_co_mutex_lock(&s->write_lock); + ret = bdrv_write(bs->file, offset, block, s->block_sectors); + qemu_co_mutex_unlock(&s->write_lock); + } else { + uint64_t offset = s->header.offset_data / SECTOR_SIZE + + (uint64_t)bmap_entry * s->block_sectors + + sector_in_block; + qemu_co_mutex_lock(&s->write_lock); + /* This lock is only used to make sure the following write operation + * is executed after the write issued by the coroutine allocating + * this cluster, therefore we do not need to keep it locked. + * As stated above, the allocating coroutine will always try to lock + * the mutex before all the other concurrent accesses to that + * cluster, therefore at this point we can be absolutely certain + * that that write operation has returned (there may be other writes + * in flight, but they do not concern this very operation). */ + qemu_co_mutex_unlock(&s->write_lock); + ret = bdrv_write(bs->file, offset, buf, n_sectors); + } + + nb_sectors -= n_sectors; + sector_num += n_sectors; + buf += n_sectors * SECTOR_SIZE; + + logout("%u sectors written\n", n_sectors); + } + + logout("finished data write\n"); + if (ret < 0) { + return ret; + } + + if (block) { + /* One or more new blocks were allocated. */ + VdiHeader *header = (VdiHeader *) block; + uint8_t *base; + uint64_t offset; + + logout("now writing modified header\n"); + assert(VDI_IS_ALLOCATED(bmap_first)); + *header = s->header; + vdi_header_to_le(header); + ret = bdrv_write(bs->file, 0, block, 1); + g_free(block); + block = NULL; + + if (ret < 0) { + return ret; + } + + logout("now writing modified block map entry %u...%u\n", + bmap_first, bmap_last); + /* Write modified sectors from block map. */ + bmap_first /= (SECTOR_SIZE / sizeof(uint32_t)); + bmap_last /= (SECTOR_SIZE / sizeof(uint32_t)); + n_sectors = bmap_last - bmap_first + 1; + offset = s->bmap_sector + bmap_first; + base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE; + logout("will write %u block map sectors starting from entry %u\n", + n_sectors, bmap_first); + ret = bdrv_write(bs->file, offset, base, n_sectors); + } + + return ret; +} + +static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int ret = 0; + uint64_t bytes = 0; + uint32_t blocks; + size_t block_size = DEFAULT_CLUSTER_SIZE; + uint32_t image_type = VDI_TYPE_DYNAMIC; + VdiHeader header; + size_t i; + size_t bmap_size; + int64_t offset = 0; + Error *local_err = NULL; + BlockDriverState *bs = NULL; + uint32_t *bmap = NULL; + + logout("\n"); + + /* Read out options. */ + bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); +#if defined(CONFIG_VDI_BLOCK_SIZE) + /* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */ + block_size = qemu_opt_get_size_del(opts, + BLOCK_OPT_CLUSTER_SIZE, + DEFAULT_CLUSTER_SIZE); +#endif +#if defined(CONFIG_VDI_STATIC_IMAGE) + if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) { + image_type = VDI_TYPE_STATIC; + } +#endif + + if (bytes > VDI_DISK_SIZE_MAX) { + ret = -ENOTSUP; + error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64 + ", max supported is 0x%" PRIx64 ")", + bytes, VDI_DISK_SIZE_MAX); + goto exit; + } + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + + /* We need enough blocks to store the given disk size, + so always round up. */ + blocks = DIV_ROUND_UP(bytes, block_size); + + bmap_size = blocks * sizeof(uint32_t); + bmap_size = ROUND_UP(bmap_size, SECTOR_SIZE); + + memset(&header, 0, sizeof(header)); + pstrcpy(header.text, sizeof(header.text), VDI_TEXT); + header.signature = VDI_SIGNATURE; + header.version = VDI_VERSION_1_1; + header.header_size = 0x180; + header.image_type = image_type; + header.offset_bmap = 0x200; + header.offset_data = 0x200 + bmap_size; + header.sector_size = SECTOR_SIZE; + header.disk_size = bytes; + header.block_size = block_size; + header.blocks_in_image = blocks; + if (image_type == VDI_TYPE_STATIC) { + header.blocks_allocated = blocks; + } + uuid_generate(header.uuid_image); + uuid_generate(header.uuid_last_snap); + /* There is no need to set header.uuid_link or header.uuid_parent here. */ +#if defined(CONFIG_VDI_DEBUG) + vdi_header_print(&header); +#endif + vdi_header_to_le(&header); + ret = bdrv_pwrite_sync(bs, offset, &header, sizeof(header)); + if (ret < 0) { + error_setg(errp, "Error writing header to %s", filename); + goto exit; + } + offset += sizeof(header); + + if (bmap_size > 0) { + bmap = g_try_malloc0(bmap_size); + if (bmap == NULL) { + ret = -ENOMEM; + error_setg(errp, "Could not allocate bmap"); + goto exit; + } + for (i = 0; i < blocks; i++) { + if (image_type == VDI_TYPE_STATIC) { + bmap[i] = i; + } else { + bmap[i] = VDI_UNALLOCATED; + } + } + ret = bdrv_pwrite_sync(bs, offset, bmap, bmap_size); + if (ret < 0) { + error_setg(errp, "Error writing bmap to %s", filename); + goto exit; + } + offset += bmap_size; + } + + if (image_type == VDI_TYPE_STATIC) { + ret = bdrv_truncate(bs, offset + blocks * block_size); + if (ret < 0) { + error_setg(errp, "Failed to statically allocate %s", filename); + goto exit; + } + } + +exit: + bdrv_unref(bs); + g_free(bmap); + return ret; +} + +static void vdi_close(BlockDriverState *bs) +{ + BDRVVdiState *s = bs->opaque; + + qemu_vfree(s->bmap); + + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); +} + +static QemuOptsList vdi_create_opts = { + .name = "vdi-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(vdi_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, +#if defined(CONFIG_VDI_BLOCK_SIZE) + { + .name = BLOCK_OPT_CLUSTER_SIZE, + .type = QEMU_OPT_SIZE, + .help = "VDI cluster (block) size", + .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) + }, +#endif +#if defined(CONFIG_VDI_STATIC_IMAGE) + { + .name = BLOCK_OPT_STATIC, + .type = QEMU_OPT_BOOL, + .help = "VDI static (pre-allocated) image", + .def_value_str = "off" + }, +#endif + /* TODO: An additional option to set UUID values might be useful. */ + { /* end of list */ } + } +}; + +static BlockDriver bdrv_vdi = { + .format_name = "vdi", + .instance_size = sizeof(BDRVVdiState), + .bdrv_probe = vdi_probe, + .bdrv_open = vdi_open, + .bdrv_close = vdi_close, + .bdrv_reopen_prepare = vdi_reopen_prepare, + .bdrv_create = vdi_create, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_co_get_block_status = vdi_co_get_block_status, + .bdrv_make_empty = vdi_make_empty, + + .bdrv_read = vdi_co_read, +#if defined(CONFIG_VDI_WRITE) + .bdrv_write = vdi_co_write, +#endif + + .bdrv_get_info = vdi_get_info, + + .create_opts = &vdi_create_opts, + .bdrv_check = vdi_check, +}; + +static void bdrv_vdi_init(void) +{ + logout("\n"); + bdrv_register(&bdrv_vdi); +} + +block_init(bdrv_vdi_init); diff --git a/block/vhdx-endian.c b/block/vhdx-endian.c new file mode 100644 index 0000000..0640d3f --- /dev/null +++ b/block/vhdx-endian.c @@ -0,0 +1,223 @@ +/* + * Block driver for Hyper-V VHDX Images + * + * Copyright (c) 2013 Red Hat, Inc., + * + * Authors: + * Jeff Cody + * + * This is based on the "VHDX Format Specification v1.00", published 8/25/2012 + * by Microsoft: + * https://www.microsoft.com/en-us/download/details.aspx?id=34750 + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "block/vhdx.h" + +#include + + +/* + * All the VHDX formats on disk are little endian - the following + * are helper import/export functions to correctly convert + * endianness from disk read to native cpu format, and back again. + */ + + +/* VHDX File Header */ + + +void vhdx_header_le_import(VHDXHeader *h) +{ + assert(h != NULL); + + le32_to_cpus(&h->signature); + le32_to_cpus(&h->checksum); + le64_to_cpus(&h->sequence_number); + + leguid_to_cpus(&h->file_write_guid); + leguid_to_cpus(&h->data_write_guid); + leguid_to_cpus(&h->log_guid); + + le16_to_cpus(&h->log_version); + le16_to_cpus(&h->version); + le32_to_cpus(&h->log_length); + le64_to_cpus(&h->log_offset); +} + +void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h) +{ + assert(orig_h != NULL); + assert(new_h != NULL); + + new_h->signature = cpu_to_le32(orig_h->signature); + new_h->checksum = cpu_to_le32(orig_h->checksum); + new_h->sequence_number = cpu_to_le64(orig_h->sequence_number); + + new_h->file_write_guid = orig_h->file_write_guid; + new_h->data_write_guid = orig_h->data_write_guid; + new_h->log_guid = orig_h->log_guid; + + cpu_to_leguids(&new_h->file_write_guid); + cpu_to_leguids(&new_h->data_write_guid); + cpu_to_leguids(&new_h->log_guid); + + new_h->log_version = cpu_to_le16(orig_h->log_version); + new_h->version = cpu_to_le16(orig_h->version); + new_h->log_length = cpu_to_le32(orig_h->log_length); + new_h->log_offset = cpu_to_le64(orig_h->log_offset); +} + + +/* VHDX Log Headers */ + + +void vhdx_log_desc_le_import(VHDXLogDescriptor *d) +{ + assert(d != NULL); + + le32_to_cpus(&d->signature); + le64_to_cpus(&d->file_offset); + le64_to_cpus(&d->sequence_number); +} + +void vhdx_log_desc_le_export(VHDXLogDescriptor *d) +{ + assert(d != NULL); + + cpu_to_le32s(&d->signature); + cpu_to_le32s(&d->trailing_bytes); + cpu_to_le64s(&d->leading_bytes); + cpu_to_le64s(&d->file_offset); + cpu_to_le64s(&d->sequence_number); +} + +void vhdx_log_data_le_import(VHDXLogDataSector *d) +{ + assert(d != NULL); + + le32_to_cpus(&d->data_signature); + le32_to_cpus(&d->sequence_high); + le32_to_cpus(&d->sequence_low); +} + +void vhdx_log_data_le_export(VHDXLogDataSector *d) +{ + assert(d != NULL); + + cpu_to_le32s(&d->data_signature); + cpu_to_le32s(&d->sequence_high); + cpu_to_le32s(&d->sequence_low); +} + +void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr) +{ + assert(hdr != NULL); + + le32_to_cpus(&hdr->signature); + le32_to_cpus(&hdr->checksum); + le32_to_cpus(&hdr->entry_length); + le32_to_cpus(&hdr->tail); + le64_to_cpus(&hdr->sequence_number); + le32_to_cpus(&hdr->descriptor_count); + leguid_to_cpus(&hdr->log_guid); + le64_to_cpus(&hdr->flushed_file_offset); + le64_to_cpus(&hdr->last_file_offset); +} + +void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr) +{ + assert(hdr != NULL); + + cpu_to_le32s(&hdr->signature); + cpu_to_le32s(&hdr->checksum); + cpu_to_le32s(&hdr->entry_length); + cpu_to_le32s(&hdr->tail); + cpu_to_le64s(&hdr->sequence_number); + cpu_to_le32s(&hdr->descriptor_count); + cpu_to_leguids(&hdr->log_guid); + cpu_to_le64s(&hdr->flushed_file_offset); + cpu_to_le64s(&hdr->last_file_offset); +} + + +/* Region table entries */ +void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr) +{ + assert(hdr != NULL); + + le32_to_cpus(&hdr->signature); + le32_to_cpus(&hdr->checksum); + le32_to_cpus(&hdr->entry_count); +} + +void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr) +{ + assert(hdr != NULL); + + cpu_to_le32s(&hdr->signature); + cpu_to_le32s(&hdr->checksum); + cpu_to_le32s(&hdr->entry_count); +} + +void vhdx_region_entry_le_import(VHDXRegionTableEntry *e) +{ + assert(e != NULL); + + leguid_to_cpus(&e->guid); + le64_to_cpus(&e->file_offset); + le32_to_cpus(&e->length); + le32_to_cpus(&e->data_bits); +} + +void vhdx_region_entry_le_export(VHDXRegionTableEntry *e) +{ + assert(e != NULL); + + cpu_to_leguids(&e->guid); + cpu_to_le64s(&e->file_offset); + cpu_to_le32s(&e->length); + cpu_to_le32s(&e->data_bits); +} + + +/* Metadata headers & table */ +void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr) +{ + assert(hdr != NULL); + + le64_to_cpus(&hdr->signature); + le16_to_cpus(&hdr->entry_count); +} + +void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr) +{ + assert(hdr != NULL); + + cpu_to_le64s(&hdr->signature); + cpu_to_le16s(&hdr->entry_count); +} + +void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e) +{ + assert(e != NULL); + + leguid_to_cpus(&e->item_id); + le32_to_cpus(&e->offset); + le32_to_cpus(&e->length); + le32_to_cpus(&e->data_bits); +} +void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e) +{ + assert(e != NULL); + + cpu_to_leguids(&e->item_id); + cpu_to_le32s(&e->offset); + cpu_to_le32s(&e->length); + cpu_to_le32s(&e->data_bits); +} diff --git a/block/vhdx-log.c b/block/vhdx-log.c new file mode 100644 index 0000000..6547bec --- /dev/null +++ b/block/vhdx-log.c @@ -0,0 +1,1038 @@ +/* + * Block driver for Hyper-V VHDX Images + * + * Copyright (c) 2013 Red Hat, Inc., + * + * Authors: + * Jeff Cody + * + * This is based on the "VHDX Format Specification v1.00", published 8/25/2012 + * by Microsoft: + * https://www.microsoft.com/en-us/download/details.aspx?id=34750 + * + * This file covers the functionality of the metadata log writing, parsing, and + * replay. + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "block/vhdx.h" + + +typedef struct VHDXLogSequence { + bool valid; + uint32_t count; + VHDXLogEntries log; + VHDXLogEntryHeader hdr; +} VHDXLogSequence; + +typedef struct VHDXLogDescEntries { + VHDXLogEntryHeader hdr; + VHDXLogDescriptor desc[]; +} VHDXLogDescEntries; + +static const MSGUID zero_guid = { 0 }; + +/* The log located on the disk is circular buffer containing + * sectors of 4096 bytes each. + * + * It is assumed for the read/write functions below that the + * circular buffer scheme uses a 'one sector open' to indicate + * the buffer is full. Given the validation methods used for each + * sector, this method should be compatible with other methods that + * do not waste a sector. + */ + + +/* Allow peeking at the hdr entry at the beginning of the current + * read index, without advancing the read index */ +static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, + VHDXLogEntryHeader *hdr) +{ + int ret = 0; + uint64_t offset; + uint32_t read; + + assert(hdr != NULL); + + /* peek is only supported on sector boundaries */ + if (log->read % VHDX_LOG_SECTOR_SIZE) { + ret = -EFAULT; + goto exit; + } + + read = log->read; + /* we are guaranteed that a) log sectors are 4096 bytes, + * and b) the log length is a multiple of 1MB. So, there + * is always a round number of sectors in the buffer */ + if ((read + sizeof(VHDXLogEntryHeader)) > log->length) { + read = 0; + } + + if (read == log->write) { + ret = -EINVAL; + goto exit; + } + + offset = log->offset + read; + + ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader)); + if (ret < 0) { + goto exit; + } + vhdx_log_entry_hdr_le_import(hdr); + +exit: + return ret; +} + +/* Index increment for log, based on sector boundaries */ +static int vhdx_log_inc_idx(uint32_t idx, uint64_t length) +{ + idx += VHDX_LOG_SECTOR_SIZE; + /* we are guaranteed that a) log sectors are 4096 bytes, + * and b) the log length is a multiple of 1MB. So, there + * is always a round number of sectors in the buffer */ + return idx >= length ? 0 : idx; +} + + +/* Reset the log to empty */ +static void vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s) +{ + MSGUID guid = { 0 }; + s->log.read = s->log.write = 0; + /* a log guid of 0 indicates an empty log to any parser of v0 + * VHDX logs */ + vhdx_update_headers(bs, s, false, &guid); +} + +/* Reads num_sectors from the log (all log sectors are 4096 bytes), + * into buffer 'buffer'. Upon return, *sectors_read will contain + * the number of sectors successfully read. + * + * It is assumed that 'buffer' is already allocated, and of sufficient + * size (i.e. >= 4096*num_sectors). + * + * If 'peek' is true, then the tail (read) pointer for the circular buffer is + * not modified. + * + * 0 is returned on success, -errno otherwise. */ +static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, + uint32_t *sectors_read, void *buffer, + uint32_t num_sectors, bool peek) +{ + int ret = 0; + uint64_t offset; + uint32_t read; + + read = log->read; + + *sectors_read = 0; + while (num_sectors) { + if (read == log->write) { + /* empty */ + break; + } + offset = log->offset + read; + + ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + read = vhdx_log_inc_idx(read, log->length); + + *sectors_read = *sectors_read + 1; + num_sectors--; + } + +exit: + if (!peek) { + log->read = read; + } + return ret; +} + +/* Writes num_sectors to the log (all log sectors are 4096 bytes), + * from buffer 'buffer'. Upon return, *sectors_written will contain + * the number of sectors successfully written. + * + * It is assumed that 'buffer' is at least 4096*num_sectors large. + * + * 0 is returned on success, -errno otherwise */ +static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, + uint32_t *sectors_written, void *buffer, + uint32_t num_sectors) +{ + int ret = 0; + uint64_t offset; + uint32_t write; + void *buffer_tmp; + BDRVVHDXState *s = bs->opaque; + + ret = vhdx_user_visible_write(bs, s); + if (ret < 0) { + goto exit; + } + + write = log->write; + + buffer_tmp = buffer; + while (num_sectors) { + + offset = log->offset + write; + write = vhdx_log_inc_idx(write, log->length); + if (write == log->read) { + /* full */ + break; + } + ret = bdrv_pwrite(bs->file, offset, buffer_tmp, VHDX_LOG_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + buffer_tmp += VHDX_LOG_SECTOR_SIZE; + + log->write = write; + *sectors_written = *sectors_written + 1; + num_sectors--; + } + +exit: + return ret; +} + + +/* Validates a log entry header */ +static bool vhdx_log_hdr_is_valid(VHDXLogEntries *log, VHDXLogEntryHeader *hdr, + BDRVVHDXState *s) +{ + int valid = false; + + if (hdr->signature != VHDX_LOG_SIGNATURE) { + goto exit; + } + + /* if the individual entry length is larger than the whole log + * buffer, that is obviously invalid */ + if (log->length < hdr->entry_length) { + goto exit; + } + + /* length of entire entry must be in units of 4KB (log sector size) */ + if (hdr->entry_length % (VHDX_LOG_SECTOR_SIZE)) { + goto exit; + } + + /* per spec, sequence # must be > 0 */ + if (hdr->sequence_number == 0) { + goto exit; + } + + /* log entries are only valid if they match the file-wide log guid + * found in the active header */ + if (!guid_eq(hdr->log_guid, s->headers[s->curr_header]->log_guid)) { + goto exit; + } + + if (hdr->descriptor_count * sizeof(VHDXLogDescriptor) > hdr->entry_length) { + goto exit; + } + + valid = true; + +exit: + return valid; +} + +/* + * Given a log header, this will validate that the descriptors and the + * corresponding data sectors (if applicable) + * + * Validation consists of: + * 1. Making sure the sequence numbers matches the entry header + * 2. Verifying a valid signature ('zero' or 'desc' for descriptors) + * 3. File offset field is a multiple of 4KB + * 4. If a data descriptor, the corresponding data sector + * has its signature ('data') and matching sequence number + * + * @desc: the data buffer containing the descriptor + * @hdr: the log entry header + * + * Returns true if valid + */ +static bool vhdx_log_desc_is_valid(VHDXLogDescriptor *desc, + VHDXLogEntryHeader *hdr) +{ + bool ret = false; + + if (desc->sequence_number != hdr->sequence_number) { + goto exit; + } + if (desc->file_offset % VHDX_LOG_SECTOR_SIZE) { + goto exit; + } + + if (desc->signature == VHDX_LOG_ZERO_SIGNATURE) { + if (desc->zero_length % VHDX_LOG_SECTOR_SIZE == 0) { + /* valid */ + ret = true; + } + } else if (desc->signature == VHDX_LOG_DESC_SIGNATURE) { + /* valid */ + ret = true; + } + +exit: + return ret; +} + + +/* Prior to sector data for a log entry, there is the header + * and the descriptors referenced in the header: + * + * [] = 4KB sector + * + * [ hdr, desc ][ desc ][ ... ][ data ][ ... ] + * + * The first sector in a log entry has a 64 byte header, and + * up to 126 32-byte descriptors. If more descriptors than + * 126 are required, then subsequent sectors can have up to 128 + * descriptors. Each sector is 4KB. Data follows the descriptor + * sectors. + * + * This will return the number of sectors needed to encompass + * the passed number of descriptors in desc_cnt. + * + * This will never return 0, even if desc_cnt is 0. + */ +static int vhdx_compute_desc_sectors(uint32_t desc_cnt) +{ + uint32_t desc_sectors; + + desc_cnt += 2; /* account for header in first sector */ + desc_sectors = desc_cnt / 128; + if (desc_cnt % 128) { + desc_sectors++; + } + + return desc_sectors; +} + + +/* Reads the log header, and subsequent descriptors (if any). This + * will allocate all the space for buffer, which must be NULL when + * passed into this function. Each descriptor will also be validated, + * and error returned if any are invalid. */ +static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogEntries *log, VHDXLogDescEntries **buffer, + bool convert_endian) +{ + int ret = 0; + uint32_t desc_sectors; + uint32_t sectors_read; + VHDXLogEntryHeader hdr; + VHDXLogDescEntries *desc_entries = NULL; + VHDXLogDescriptor desc; + int i; + + assert(*buffer == NULL); + + ret = vhdx_log_peek_hdr(bs, log, &hdr); + if (ret < 0) { + goto exit; + } + + if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) { + ret = -EINVAL; + goto exit; + } + + desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count); + desc_entries = qemu_try_blockalign(bs->file, + desc_sectors * VHDX_LOG_SECTOR_SIZE); + if (desc_entries == NULL) { + ret = -ENOMEM; + goto exit; + } + + ret = vhdx_log_read_sectors(bs, log, §ors_read, desc_entries, + desc_sectors, false); + if (ret < 0) { + goto free_and_exit; + } + if (sectors_read != desc_sectors) { + ret = -EINVAL; + goto free_and_exit; + } + + /* put in proper endianness, and validate each desc */ + for (i = 0; i < hdr.descriptor_count; i++) { + desc = desc_entries->desc[i]; + vhdx_log_desc_le_import(&desc); + if (convert_endian) { + desc_entries->desc[i] = desc; + } + if (vhdx_log_desc_is_valid(&desc, &hdr) == false) { + ret = -EINVAL; + goto free_and_exit; + } + } + if (convert_endian) { + desc_entries->hdr = hdr; + } + + *buffer = desc_entries; + goto exit; + +free_and_exit: + qemu_vfree(desc_entries); +exit: + return ret; +} + + +/* Flushes the descriptor described by desc to the VHDX image file. + * If the descriptor is a data descriptor, than 'data' must be non-NULL, + * and >= 4096 bytes (VHDX_LOG_SECTOR_SIZE), containing the data to be + * written. + * + * Verification is performed to make sure the sequence numbers of a data + * descriptor match the sequence number in the desc. + * + * For a zero descriptor, it may describe multiple sectors to fill with zeroes. + * In this case, it should be noted that zeroes are written to disk, and the + * image file is not extended as a sparse file. */ +static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, + VHDXLogDataSector *data) +{ + int ret = 0; + uint64_t seq, file_offset; + uint32_t offset = 0; + void *buffer = NULL; + uint64_t count = 1; + int i; + + buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); + + if (desc->signature == VHDX_LOG_DESC_SIGNATURE) { + /* data sector */ + if (data == NULL) { + ret = -EFAULT; + goto exit; + } + + /* The sequence number of the data sector must match that + * in the descriptor */ + seq = data->sequence_high; + seq <<= 32; + seq |= data->sequence_low & 0xffffffff; + + if (seq != desc->sequence_number) { + ret = -EINVAL; + goto exit; + } + + /* Each data sector is in total 4096 bytes, however the first + * 8 bytes, and last 4 bytes, are located in the descriptor */ + memcpy(buffer, &desc->leading_bytes, 8); + offset += 8; + + memcpy(buffer+offset, data->data, 4084); + offset += 4084; + + memcpy(buffer+offset, &desc->trailing_bytes, 4); + + } else if (desc->signature == VHDX_LOG_ZERO_SIGNATURE) { + /* write 'count' sectors of sector */ + memset(buffer, 0, VHDX_LOG_SECTOR_SIZE); + count = desc->zero_length / VHDX_LOG_SECTOR_SIZE; + } else { + error_report("Invalid VHDX log descriptor entry signature 0x%" PRIx32, + desc->signature); + ret = -EINVAL; + goto exit; + } + + file_offset = desc->file_offset; + + /* count is only > 1 if we are writing zeroes */ + for (i = 0; i < count; i++) { + ret = bdrv_pwrite_sync(bs->file, file_offset, buffer, + VHDX_LOG_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + file_offset += VHDX_LOG_SECTOR_SIZE; + } + +exit: + qemu_vfree(buffer); + return ret; +} + +/* Flush the entire log (as described by 'logs') to the VHDX image + * file, and then set the log to 'empty' status once complete. + * + * The log entries should be validate prior to flushing */ +static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogSequence *logs) +{ + int ret = 0; + int i; + uint32_t cnt, sectors_read; + uint64_t new_file_size; + void *data = NULL; + VHDXLogDescEntries *desc_entries = NULL; + VHDXLogEntryHeader hdr_tmp = { 0 }; + + cnt = logs->count; + + data = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); + + ret = vhdx_user_visible_write(bs, s); + if (ret < 0) { + goto exit; + } + + /* each iteration represents one log sequence, which may span multiple + * sectors */ + while (cnt--) { + ret = vhdx_log_peek_hdr(bs, &logs->log, &hdr_tmp); + if (ret < 0) { + goto exit; + } + /* if the log shows a FlushedFileOffset larger than our current file + * size, then that means the file has been truncated / corrupted, and + * we must refused to open it / use it */ + if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file)) { + ret = -EINVAL; + goto exit; + } + + ret = vhdx_log_read_desc(bs, s, &logs->log, &desc_entries, true); + if (ret < 0) { + goto exit; + } + + for (i = 0; i < desc_entries->hdr.descriptor_count; i++) { + if (desc_entries->desc[i].signature == VHDX_LOG_DESC_SIGNATURE) { + /* data sector, so read a sector to flush */ + ret = vhdx_log_read_sectors(bs, &logs->log, §ors_read, + data, 1, false); + if (ret < 0) { + goto exit; + } + if (sectors_read != 1) { + ret = -EINVAL; + goto exit; + } + vhdx_log_data_le_import(data); + } + + ret = vhdx_log_flush_desc(bs, &desc_entries->desc[i], data); + if (ret < 0) { + goto exit; + } + } + if (bdrv_getlength(bs->file) < desc_entries->hdr.last_file_offset) { + new_file_size = desc_entries->hdr.last_file_offset; + if (new_file_size % (1024*1024)) { + /* round up to nearest 1MB boundary */ + new_file_size = ((new_file_size >> 20) + 1) << 20; + bdrv_truncate(bs->file, new_file_size); + } + } + qemu_vfree(desc_entries); + desc_entries = NULL; + } + + bdrv_flush(bs); + /* once the log is fully flushed, indicate that we have an empty log + * now. This also sets the log guid to 0, to indicate an empty log */ + vhdx_log_reset(bs, s); + +exit: + qemu_vfree(data); + qemu_vfree(desc_entries); + return ret; +} + +static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogEntries *log, uint64_t seq, + bool *valid, VHDXLogEntryHeader *entry) +{ + int ret = 0; + VHDXLogEntryHeader hdr; + void *buffer = NULL; + uint32_t i, desc_sectors, total_sectors, crc; + uint32_t sectors_read = 0; + VHDXLogDescEntries *desc_buffer = NULL; + + *valid = false; + + ret = vhdx_log_peek_hdr(bs, log, &hdr); + if (ret < 0) { + goto inc_and_exit; + } + + if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) { + goto inc_and_exit; + } + + if (seq > 0) { + if (hdr.sequence_number != seq + 1) { + goto inc_and_exit; + } + } + + desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count); + + /* Read all log sectors, and calculate log checksum */ + + total_sectors = hdr.entry_length / VHDX_LOG_SECTOR_SIZE; + + + /* read_desc() will increment the read idx */ + ret = vhdx_log_read_desc(bs, s, log, &desc_buffer, false); + if (ret < 0) { + goto free_and_exit; + } + + crc = vhdx_checksum_calc(0xffffffff, (void *)desc_buffer, + desc_sectors * VHDX_LOG_SECTOR_SIZE, 4); + crc ^= 0xffffffff; + + buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); + if (total_sectors > desc_sectors) { + for (i = 0; i < total_sectors - desc_sectors; i++) { + sectors_read = 0; + ret = vhdx_log_read_sectors(bs, log, §ors_read, buffer, + 1, false); + if (ret < 0 || sectors_read != 1) { + goto free_and_exit; + } + crc = vhdx_checksum_calc(crc, buffer, VHDX_LOG_SECTOR_SIZE, -1); + crc ^= 0xffffffff; + } + } + crc ^= 0xffffffff; + if (crc != hdr.checksum) { + goto free_and_exit; + } + + *valid = true; + *entry = hdr; + goto free_and_exit; + +inc_and_exit: + log->read = vhdx_log_inc_idx(log->read, log->length); + +free_and_exit: + qemu_vfree(buffer); + qemu_vfree(desc_buffer); + return ret; +} + +/* Search through the log circular buffer, and find the valid, active + * log sequence, if any exists + * */ +static int vhdx_log_search(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogSequence *logs) +{ + int ret = 0; + uint32_t tail; + bool seq_valid = false; + VHDXLogSequence candidate = { 0 }; + VHDXLogEntryHeader hdr = { 0 }; + VHDXLogEntries curr_log; + + memcpy(&curr_log, &s->log, sizeof(VHDXLogEntries)); + curr_log.write = curr_log.length; /* assume log is full */ + curr_log.read = 0; + + + /* now we will go through the whole log sector by sector, until + * we find a valid, active log sequence, or reach the end of the + * log buffer */ + for (;;) { + uint64_t curr_seq = 0; + VHDXLogSequence current = { 0 }; + + tail = curr_log.read; + + ret = vhdx_validate_log_entry(bs, s, &curr_log, curr_seq, + &seq_valid, &hdr); + if (ret < 0) { + goto exit; + } + + if (seq_valid) { + current.valid = true; + current.log = curr_log; + current.log.read = tail; + current.log.write = curr_log.read; + current.count = 1; + current.hdr = hdr; + + + for (;;) { + ret = vhdx_validate_log_entry(bs, s, &curr_log, curr_seq, + &seq_valid, &hdr); + if (ret < 0) { + goto exit; + } + if (seq_valid == false) { + break; + } + current.log.write = curr_log.read; + current.count++; + + curr_seq = hdr.sequence_number; + } + } + + if (current.valid) { + if (candidate.valid == false || + current.hdr.sequence_number > candidate.hdr.sequence_number) { + candidate = current; + } + } + + if (curr_log.read < tail) { + break; + } + } + + *logs = candidate; + + if (candidate.valid) { + /* this is the next sequence number, for writes */ + s->log.sequence = candidate.hdr.sequence_number + 1; + } + + +exit: + return ret; +} + +/* Parse the replay log. Per the VHDX spec, if the log is present + * it must be replayed prior to opening the file, even read-only. + * + * If read-only, we must replay the log in RAM (or refuse to open + * a dirty VHDX file read-only) */ +int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed, + Error **errp) +{ + int ret = 0; + VHDXHeader *hdr; + VHDXLogSequence logs = { 0 }; + + hdr = s->headers[s->curr_header]; + + *flushed = false; + + /* s->log.hdr is freed in vhdx_close() */ + if (s->log.hdr == NULL) { + s->log.hdr = qemu_blockalign(bs, sizeof(VHDXLogEntryHeader)); + } + + s->log.offset = hdr->log_offset; + s->log.length = hdr->log_length; + + if (s->log.offset < VHDX_LOG_MIN_SIZE || + s->log.offset % VHDX_LOG_MIN_SIZE) { + ret = -EINVAL; + goto exit; + } + + /* per spec, only log version of 0 is supported */ + if (hdr->log_version != 0) { + ret = -EINVAL; + goto exit; + } + + /* If either the log guid, or log length is zero, + * then a replay log is not present */ + if (guid_eq(hdr->log_guid, zero_guid)) { + goto exit; + } + + if (hdr->log_length == 0) { + goto exit; + } + + if (hdr->log_length % VHDX_LOG_MIN_SIZE) { + ret = -EINVAL; + goto exit; + } + + + /* The log is present, we need to find if and where there is an active + * sequence of valid entries present in the log. */ + + ret = vhdx_log_search(bs, s, &logs); + if (ret < 0) { + goto exit; + } + + if (logs.valid) { + if (bs->read_only) { + ret = -EPERM; + error_setg_errno(errp, EPERM, + "VHDX image file '%s' opened read-only, but " + "contains a log that needs to be replayed. To " + "replay the log, execute:\n qemu-img check -r " + "all '%s'", + bs->filename, bs->filename); + goto exit; + } + /* now flush the log */ + ret = vhdx_log_flush(bs, s, &logs); + if (ret < 0) { + goto exit; + } + *flushed = true; + } + + +exit: + return ret; +} + + + +static void vhdx_log_raw_to_le_sector(VHDXLogDescriptor *desc, + VHDXLogDataSector *sector, void *data, + uint64_t seq) +{ + /* 8 + 4084 + 4 = 4096, 1 log sector */ + memcpy(&desc->leading_bytes, data, 8); + data += 8; + cpu_to_le64s(&desc->leading_bytes); + memcpy(sector->data, data, 4084); + data += 4084; + memcpy(&desc->trailing_bytes, data, 4); + cpu_to_le32s(&desc->trailing_bytes); + data += 4; + + sector->sequence_high = (uint32_t) (seq >> 32); + sector->sequence_low = (uint32_t) (seq & 0xffffffff); + sector->data_signature = VHDX_LOG_DATA_SIGNATURE; + + vhdx_log_desc_le_export(desc); + vhdx_log_data_le_export(sector); +} + + +static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset) +{ + int ret = 0; + void *buffer = NULL; + void *merged_sector = NULL; + void *data_tmp, *sector_write; + unsigned int i; + int sector_offset; + uint32_t desc_sectors, sectors, total_length; + uint32_t sectors_written = 0; + uint32_t aligned_length; + uint32_t leading_length = 0; + uint32_t trailing_length = 0; + uint32_t partial_sectors = 0; + uint32_t bytes_written = 0; + uint64_t file_offset; + VHDXHeader *header; + VHDXLogEntryHeader new_hdr; + VHDXLogDescriptor *new_desc = NULL; + VHDXLogDataSector *data_sector = NULL; + MSGUID new_guid = { 0 }; + + header = s->headers[s->curr_header]; + + /* need to have offset read data, and be on 4096 byte boundary */ + + if (length > header->log_length) { + /* no log present. we could create a log here instead of failing */ + ret = -EINVAL; + goto exit; + } + + if (guid_eq(header->log_guid, zero_guid)) { + vhdx_guid_generate(&new_guid); + vhdx_update_headers(bs, s, false, &new_guid); + } else { + /* currently, we require that the log be flushed after + * every write. */ + ret = -ENOTSUP; + goto exit; + } + + /* 0 is an invalid sequence number, but may also represent the first + * log write (or a wrapped seq) */ + if (s->log.sequence == 0) { + s->log.sequence = 1; + } + + sector_offset = offset % VHDX_LOG_SECTOR_SIZE; + file_offset = (offset / VHDX_LOG_SECTOR_SIZE) * VHDX_LOG_SECTOR_SIZE; + + aligned_length = length; + + /* add in the unaligned head and tail bytes */ + if (sector_offset) { + leading_length = (VHDX_LOG_SECTOR_SIZE - sector_offset); + leading_length = leading_length > length ? length : leading_length; + aligned_length -= leading_length; + partial_sectors++; + } + + sectors = aligned_length / VHDX_LOG_SECTOR_SIZE; + trailing_length = aligned_length - (sectors * VHDX_LOG_SECTOR_SIZE); + if (trailing_length) { + partial_sectors++; + } + + sectors += partial_sectors; + + /* sectors is now how many sectors the data itself takes, not + * including the header and descriptor metadata */ + + new_hdr = (VHDXLogEntryHeader) { + .signature = VHDX_LOG_SIGNATURE, + .tail = s->log.tail, + .sequence_number = s->log.sequence, + .descriptor_count = sectors, + .reserved = 0, + .flushed_file_offset = bdrv_getlength(bs->file), + .last_file_offset = bdrv_getlength(bs->file), + }; + + new_hdr.log_guid = header->log_guid; + + desc_sectors = vhdx_compute_desc_sectors(new_hdr.descriptor_count); + + total_length = (desc_sectors + sectors) * VHDX_LOG_SECTOR_SIZE; + new_hdr.entry_length = total_length; + + vhdx_log_entry_hdr_le_export(&new_hdr); + + buffer = qemu_blockalign(bs, total_length); + memcpy(buffer, &new_hdr, sizeof(new_hdr)); + + new_desc = buffer + sizeof(new_hdr); + data_sector = buffer + (desc_sectors * VHDX_LOG_SECTOR_SIZE); + data_tmp = data; + + /* All log sectors are 4KB, so for any partial sectors we must + * merge the data with preexisting data from the final file + * destination */ + merged_sector = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); + + for (i = 0; i < sectors; i++) { + new_desc->signature = VHDX_LOG_DESC_SIGNATURE; + new_desc->sequence_number = s->log.sequence; + new_desc->file_offset = file_offset; + + if (i == 0 && leading_length) { + /* partial sector at the front of the buffer */ + ret = bdrv_pread(bs->file, file_offset, merged_sector, + VHDX_LOG_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + memcpy(merged_sector + sector_offset, data_tmp, leading_length); + bytes_written = leading_length; + sector_write = merged_sector; + } else if (i == sectors - 1 && trailing_length) { + /* partial sector at the end of the buffer */ + ret = bdrv_pread(bs->file, + file_offset, + merged_sector + trailing_length, + VHDX_LOG_SECTOR_SIZE - trailing_length); + if (ret < 0) { + goto exit; + } + memcpy(merged_sector, data_tmp, trailing_length); + bytes_written = trailing_length; + sector_write = merged_sector; + } else { + bytes_written = VHDX_LOG_SECTOR_SIZE; + sector_write = data_tmp; + } + + /* populate the raw sector data into the proper structures, + * as well as update the descriptor, and convert to proper + * endianness */ + vhdx_log_raw_to_le_sector(new_desc, data_sector, sector_write, + s->log.sequence); + + data_tmp += bytes_written; + data_sector++; + new_desc++; + file_offset += VHDX_LOG_SECTOR_SIZE; + } + + /* checksum covers entire entry, from the log header through the + * last data sector */ + vhdx_update_checksum(buffer, total_length, + offsetof(VHDXLogEntryHeader, checksum)); + + /* now write to the log */ + ret = vhdx_log_write_sectors(bs, &s->log, §ors_written, buffer, + desc_sectors + sectors); + if (ret < 0) { + goto exit; + } + + if (sectors_written != desc_sectors + sectors) { + /* instead of failing, we could flush the log here */ + ret = -EINVAL; + goto exit; + } + + s->log.sequence++; + /* write new tail */ + s->log.tail = s->log.write; + +exit: + qemu_vfree(buffer); + qemu_vfree(merged_sector); + return ret; +} + +/* Perform a log write, and then immediately flush the entire log */ +int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset) +{ + int ret = 0; + VHDXLogSequence logs = { .valid = true, + .count = 1, + .hdr = { 0 } }; + + + /* Make sure data written (new and/or changed blocks) is stable + * on disk, before creating log entry */ + bdrv_flush(bs); + ret = vhdx_log_write(bs, s, data, length, offset); + if (ret < 0) { + goto exit; + } + logs.log = s->log; + + /* Make sure log is stable on disk */ + bdrv_flush(bs); + ret = vhdx_log_flush(bs, s, &logs); + if (ret < 0) { + goto exit; + } + + s->log = logs.log; + +exit: + return ret; +} + diff --git a/block/vhdx.c b/block/vhdx.c new file mode 100644 index 0000000..0776de7 --- /dev/null +++ b/block/vhdx.c @@ -0,0 +1,1980 @@ +/* + * Block driver for Hyper-V VHDX Images + * + * Copyright (c) 2013 Red Hat, Inc., + * + * Authors: + * Jeff Cody + * + * This is based on the "VHDX Format Specification v1.00", published 8/25/2012 + * by Microsoft: + * https://www.microsoft.com/en-us/download/details.aspx?id=34750 + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "qemu/crc32c.h" +#include "block/vhdx.h" +#include "migration/migration.h" + +#include +#include + +/* Options for VHDX creation */ + +#define VHDX_BLOCK_OPT_LOG_SIZE "log_size" +#define VHDX_BLOCK_OPT_BLOCK_SIZE "block_size" +#define VHDX_BLOCK_OPT_ZERO "block_state_zero" + +typedef enum VHDXImageType { + VHDX_TYPE_DYNAMIC = 0, + VHDX_TYPE_FIXED, + VHDX_TYPE_DIFFERENCING, /* Currently unsupported */ +} VHDXImageType; + +/* Several metadata and region table data entries are identified by + * guids in a MS-specific GUID format. */ + + +/* ------- Known Region Table GUIDs ---------------------- */ +static const MSGUID bat_guid = { .data1 = 0x2dc27766, + .data2 = 0xf623, + .data3 = 0x4200, + .data4 = { 0x9d, 0x64, 0x11, 0x5e, + 0x9b, 0xfd, 0x4a, 0x08} }; + +static const MSGUID metadata_guid = { .data1 = 0x8b7ca206, + .data2 = 0x4790, + .data3 = 0x4b9a, + .data4 = { 0xb8, 0xfe, 0x57, 0x5f, + 0x05, 0x0f, 0x88, 0x6e} }; + + + +/* ------- Known Metadata Entry GUIDs ---------------------- */ +static const MSGUID file_param_guid = { .data1 = 0xcaa16737, + .data2 = 0xfa36, + .data3 = 0x4d43, + .data4 = { 0xb3, 0xb6, 0x33, 0xf0, + 0xaa, 0x44, 0xe7, 0x6b} }; + +static const MSGUID virtual_size_guid = { .data1 = 0x2FA54224, + .data2 = 0xcd1b, + .data3 = 0x4876, + .data4 = { 0xb2, 0x11, 0x5d, 0xbe, + 0xd8, 0x3b, 0xf4, 0xb8} }; + +static const MSGUID page83_guid = { .data1 = 0xbeca12ab, + .data2 = 0xb2e6, + .data3 = 0x4523, + .data4 = { 0x93, 0xef, 0xc3, 0x09, + 0xe0, 0x00, 0xc7, 0x46} }; + + +static const MSGUID phys_sector_guid = { .data1 = 0xcda348c7, + .data2 = 0x445d, + .data3 = 0x4471, + .data4 = { 0x9c, 0xc9, 0xe9, 0x88, + 0x52, 0x51, 0xc5, 0x56} }; + +static const MSGUID parent_locator_guid = { .data1 = 0xa8d35f2d, + .data2 = 0xb30b, + .data3 = 0x454d, + .data4 = { 0xab, 0xf7, 0xd3, + 0xd8, 0x48, 0x34, + 0xab, 0x0c} }; + +static const MSGUID logical_sector_guid = { .data1 = 0x8141bf1d, + .data2 = 0xa96f, + .data3 = 0x4709, + .data4 = { 0xba, 0x47, 0xf2, + 0x33, 0xa8, 0xfa, + 0xab, 0x5f} }; + +/* Each parent type must have a valid GUID; this is for parent images + * of type 'VHDX'. If we were to allow e.g. a QCOW2 parent, we would + * need to make up our own QCOW2 GUID type */ +static const MSGUID parent_vhdx_guid __attribute__((unused)) + = { .data1 = 0xb04aefb7, + .data2 = 0xd19e, + .data3 = 0x4a81, + .data4 = { 0xb7, 0x89, 0x25, 0xb8, + 0xe9, 0x44, 0x59, 0x13} }; + + +#define META_FILE_PARAMETER_PRESENT 0x01 +#define META_VIRTUAL_DISK_SIZE_PRESENT 0x02 +#define META_PAGE_83_PRESENT 0x04 +#define META_LOGICAL_SECTOR_SIZE_PRESENT 0x08 +#define META_PHYS_SECTOR_SIZE_PRESENT 0x10 +#define META_PARENT_LOCATOR_PRESENT 0x20 + +#define META_ALL_PRESENT \ + (META_FILE_PARAMETER_PRESENT | META_VIRTUAL_DISK_SIZE_PRESENT | \ + META_PAGE_83_PRESENT | META_LOGICAL_SECTOR_SIZE_PRESENT | \ + META_PHYS_SECTOR_SIZE_PRESENT) + + +typedef struct VHDXSectorInfo { + uint32_t bat_idx; /* BAT entry index */ + uint32_t sectors_avail; /* sectors available in payload block */ + uint32_t bytes_left; /* bytes left in the block after data to r/w */ + uint32_t bytes_avail; /* bytes available in payload block */ + uint64_t file_offset; /* absolute offset in bytes, in file */ + uint64_t block_offset; /* block offset, in bytes */ +} VHDXSectorInfo; + +/* Calculates new checksum. + * + * Zero is substituted during crc calculation for the original crc field + * crc_offset: byte offset in buf of the buffer crc + * buf: buffer pointer + * size: size of buffer (must be > crc_offset+4) + * + * Note: The buffer should have all multi-byte data in little-endian format, + * and the resulting checksum is in little endian format. + */ +uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset) +{ + uint32_t crc; + + assert(buf != NULL); + assert(size > (crc_offset + sizeof(crc))); + + memset(buf + crc_offset, 0, sizeof(crc)); + crc = crc32c(0xffffffff, buf, size); + cpu_to_le32s(&crc); + memcpy(buf + crc_offset, &crc, sizeof(crc)); + + return crc; +} + +uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, + int crc_offset) +{ + uint32_t crc_new; + uint32_t crc_orig; + assert(buf != NULL); + + if (crc_offset > 0) { + memcpy(&crc_orig, buf + crc_offset, sizeof(crc_orig)); + memset(buf + crc_offset, 0, sizeof(crc_orig)); + } + + crc_new = crc32c(crc, buf, size); + if (crc_offset > 0) { + memcpy(buf + crc_offset, &crc_orig, sizeof(crc_orig)); + } + + return crc_new; +} + +/* Validates the checksum of the buffer, with an in-place CRC. + * + * Zero is substituted during crc calculation for the original crc field, + * and the crc field is restored afterwards. But the buffer will be modifed + * during the calculation, so this may not be not suitable for multi-threaded + * use. + * + * crc_offset: byte offset in buf of the buffer crc + * buf: buffer pointer + * size: size of buffer (must be > crc_offset+4) + * + * returns true if checksum is valid, false otherwise + */ +bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset) +{ + uint32_t crc_orig; + uint32_t crc; + + assert(buf != NULL); + assert(size > (crc_offset + 4)); + + memcpy(&crc_orig, buf + crc_offset, sizeof(crc_orig)); + crc_orig = le32_to_cpu(crc_orig); + + crc = vhdx_checksum_calc(0xffffffff, buf, size, crc_offset); + + return crc == crc_orig; +} + + +/* + * This generates a UUID that is compliant with the MS GUIDs used + * in the VHDX spec (and elsewhere). + */ +void vhdx_guid_generate(MSGUID *guid) +{ + uuid_t uuid; + assert(guid != NULL); + + uuid_generate(uuid); + memcpy(guid, uuid, sizeof(MSGUID)); +} + +/* Check for region overlaps inside the VHDX image */ +static int vhdx_region_check(BDRVVHDXState *s, uint64_t start, uint64_t length) +{ + int ret = 0; + uint64_t end; + VHDXRegionEntry *r; + + end = start + length; + QLIST_FOREACH(r, &s->regions, entries) { + if (!((start >= r->end) || (end <= r->start))) { + ret = -EINVAL; + goto exit; + } + } + +exit: + return ret; +} + +/* Register a region for future checks */ +static void vhdx_region_register(BDRVVHDXState *s, + uint64_t start, uint64_t length) +{ + VHDXRegionEntry *r; + + r = g_malloc0(sizeof(*r)); + + r->start = start; + r->end = start + length; + + QLIST_INSERT_HEAD(&s->regions, r, entries); +} + +/* Free all registered regions */ +static void vhdx_region_unregister_all(BDRVVHDXState *s) +{ + VHDXRegionEntry *r, *r_next; + + QLIST_FOREACH_SAFE(r, &s->regions, entries, r_next) { + QLIST_REMOVE(r, entries); + g_free(r); + } +} + +static void vhdx_set_shift_bits(BDRVVHDXState *s) +{ + s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size); + s->sectors_per_block_bits = 31 - clz32(s->sectors_per_block); + s->chunk_ratio_bits = 63 - clz64(s->chunk_ratio); + s->block_size_bits = 31 - clz32(s->block_size); +} + +/* + * Per the MS VHDX Specification, for every VHDX file: + * - The header section is fixed size - 1 MB + * - The header section is always the first "object" + * - The first 64KB of the header is the File Identifier + * - The first uint64 (8 bytes) is the VHDX Signature ("vhdxfile") + * - The following 512 bytes constitute a UTF-16 string identifiying the + * software that created the file, and is optional and diagnostic only. + * + * Therefore, we probe by looking for the vhdxfile signature "vhdxfile" + */ +static int vhdx_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + if (buf_size >= 8 && !memcmp(buf, "vhdxfile", 8)) { + return 100; + } + return 0; +} + +/* + * Writes the header to the specified offset. + * + * This will optionally read in buffer data from disk (otherwise zero-fill), + * and then update the header checksum. Header is converted to proper + * endianness before being written to the specified file offset + */ +static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr, + uint64_t offset, bool read) +{ + uint8_t *buffer = NULL; + int ret; + VHDXHeader *header_le; + + assert(bs_file != NULL); + assert(hdr != NULL); + + /* the header checksum is not over just the packed size of VHDXHeader, + * but rather over the entire 'reserved' range for the header, which is + * 4KB (VHDX_HEADER_SIZE). */ + + buffer = qemu_blockalign(bs_file, VHDX_HEADER_SIZE); + if (read) { + /* if true, we can't assume the extra reserved bytes are 0 */ + ret = bdrv_pread(bs_file, offset, buffer, VHDX_HEADER_SIZE); + if (ret < 0) { + goto exit; + } + } else { + memset(buffer, 0, VHDX_HEADER_SIZE); + } + + /* overwrite the actual VHDXHeader portion */ + header_le = (VHDXHeader *)buffer; + memcpy(header_le, hdr, sizeof(VHDXHeader)); + vhdx_header_le_export(hdr, header_le); + vhdx_update_checksum(buffer, VHDX_HEADER_SIZE, + offsetof(VHDXHeader, checksum)); + ret = bdrv_pwrite_sync(bs_file, offset, header_le, sizeof(VHDXHeader)); + +exit: + qemu_vfree(buffer); + return ret; +} + +/* Update the VHDX headers + * + * This follows the VHDX spec procedures for header updates. + * + * - non-current header is updated with largest sequence number + */ +static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s, + bool generate_data_write_guid, MSGUID *log_guid) +{ + int ret = 0; + int hdr_idx = 0; + uint64_t header_offset = VHDX_HEADER1_OFFSET; + + VHDXHeader *active_header; + VHDXHeader *inactive_header; + + /* operate on the non-current header */ + if (s->curr_header == 0) { + hdr_idx = 1; + header_offset = VHDX_HEADER2_OFFSET; + } + + active_header = s->headers[s->curr_header]; + inactive_header = s->headers[hdr_idx]; + + inactive_header->sequence_number = active_header->sequence_number + 1; + + /* a new file guid must be generated before any file write, including + * headers */ + inactive_header->file_write_guid = s->session_guid; + + /* a new data guid only needs to be generated before any guest-visible + * writes (i.e. something observable via virtual disk read) */ + if (generate_data_write_guid) { + vhdx_guid_generate(&inactive_header->data_write_guid); + } + + /* update the log guid if present */ + if (log_guid) { + inactive_header->log_guid = *log_guid; + } + + ret = vhdx_write_header(bs->file, inactive_header, header_offset, true); + if (ret < 0) { + goto exit; + } + s->curr_header = hdr_idx; + +exit: + return ret; +} + +/* + * The VHDX spec calls for header updates to be performed twice, so that both + * the current and non-current header have valid info + */ +int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, + bool generate_data_write_guid, MSGUID *log_guid) +{ + int ret; + + ret = vhdx_update_header(bs, s, generate_data_write_guid, log_guid); + if (ret < 0) { + return ret; + } + ret = vhdx_update_header(bs, s, generate_data_write_guid, log_guid); + return ret; +} + +/* opens the specified header block from the VHDX file header section */ +static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, + Error **errp) +{ + int ret; + VHDXHeader *header1; + VHDXHeader *header2; + bool h1_valid = false; + bool h2_valid = false; + uint64_t h1_seq = 0; + uint64_t h2_seq = 0; + uint8_t *buffer; + + /* header1 & header2 are freed in vhdx_close() */ + header1 = qemu_blockalign(bs, sizeof(VHDXHeader)); + header2 = qemu_blockalign(bs, sizeof(VHDXHeader)); + + buffer = qemu_blockalign(bs, VHDX_HEADER_SIZE); + + s->headers[0] = header1; + s->headers[1] = header2; + + /* We have to read the whole VHDX_HEADER_SIZE instead of + * sizeof(VHDXHeader), because the checksum is over the whole + * region */ + ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, buffer, VHDX_HEADER_SIZE); + if (ret < 0) { + goto fail; + } + /* copy over just the relevant portion that we need */ + memcpy(header1, buffer, sizeof(VHDXHeader)); + + if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4)) { + vhdx_header_le_import(header1); + if (header1->signature == VHDX_HEADER_SIGNATURE && + header1->version == 1) { + h1_seq = header1->sequence_number; + h1_valid = true; + } + } + + ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, VHDX_HEADER_SIZE); + if (ret < 0) { + goto fail; + } + /* copy over just the relevant portion that we need */ + memcpy(header2, buffer, sizeof(VHDXHeader)); + + if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4)) { + vhdx_header_le_import(header2); + if (header2->signature == VHDX_HEADER_SIGNATURE && + header2->version == 1) { + h2_seq = header2->sequence_number; + h2_valid = true; + } + } + + /* If there is only 1 valid header (or no valid headers), we + * don't care what the sequence numbers are */ + if (h1_valid && !h2_valid) { + s->curr_header = 0; + } else if (!h1_valid && h2_valid) { + s->curr_header = 1; + } else if (!h1_valid && !h2_valid) { + goto fail; + } else { + /* If both headers are valid, then we choose the active one by the + * highest sequence number. If the sequence numbers are equal, that is + * invalid */ + if (h1_seq > h2_seq) { + s->curr_header = 0; + } else if (h2_seq > h1_seq) { + s->curr_header = 1; + } else { + /* The Microsoft Disk2VHD tool will create 2 identical + * headers, with identical sequence numbers. If the headers are + * identical, don't consider the file corrupt */ + if (!memcmp(header1, header2, sizeof(VHDXHeader))) { + s->curr_header = 0; + } else { + goto fail; + } + } + } + + vhdx_region_register(s, s->headers[s->curr_header]->log_offset, + s->headers[s->curr_header]->log_length); + goto exit; + +fail: + error_setg_errno(errp, -ret, "No valid VHDX header found"); + qemu_vfree(header1); + qemu_vfree(header2); + s->headers[0] = NULL; + s->headers[1] = NULL; +exit: + qemu_vfree(buffer); +} + + +static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) +{ + int ret = 0; + uint8_t *buffer; + int offset = 0; + VHDXRegionTableEntry rt_entry; + uint32_t i; + bool bat_rt_found = false; + bool metadata_rt_found = false; + + /* We have to read the whole 64KB block, because the crc32 is over the + * whole block */ + buffer = qemu_blockalign(bs, VHDX_HEADER_BLOCK_SIZE); + + ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, buffer, + VHDX_HEADER_BLOCK_SIZE); + if (ret < 0) { + goto fail; + } + memcpy(&s->rt, buffer, sizeof(s->rt)); + offset += sizeof(s->rt); + + if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4)) { + ret = -EINVAL; + goto fail; + } + + vhdx_region_header_le_import(&s->rt); + + if (s->rt.signature != VHDX_REGION_SIGNATURE) { + ret = -EINVAL; + goto fail; + } + + + /* Per spec, maximum region table entry count is 2047 */ + if (s->rt.entry_count > 2047) { + ret = -EINVAL; + goto fail; + } + + for (i = 0; i < s->rt.entry_count; i++) { + memcpy(&rt_entry, buffer + offset, sizeof(rt_entry)); + offset += sizeof(rt_entry); + + vhdx_region_entry_le_import(&rt_entry); + + /* check for region overlap between these entries, and any + * other memory regions in the file */ + ret = vhdx_region_check(s, rt_entry.file_offset, rt_entry.length); + if (ret < 0) { + goto fail; + } + + vhdx_region_register(s, rt_entry.file_offset, rt_entry.length); + + /* see if we recognize the entry */ + if (guid_eq(rt_entry.guid, bat_guid)) { + /* must be unique; if we have already found it this is invalid */ + if (bat_rt_found) { + ret = -EINVAL; + goto fail; + } + bat_rt_found = true; + s->bat_rt = rt_entry; + continue; + } + + if (guid_eq(rt_entry.guid, metadata_guid)) { + /* must be unique; if we have already found it this is invalid */ + if (metadata_rt_found) { + ret = -EINVAL; + goto fail; + } + metadata_rt_found = true; + s->metadata_rt = rt_entry; + continue; + } + + if (rt_entry.data_bits & VHDX_REGION_ENTRY_REQUIRED) { + /* cannot read vhdx file - required region table entry that + * we do not understand. per spec, we must fail to open */ + ret = -ENOTSUP; + goto fail; + } + } + + if (!bat_rt_found || !metadata_rt_found) { + ret = -EINVAL; + goto fail; + } + + ret = 0; + +fail: + qemu_vfree(buffer); + return ret; +} + + + +/* Metadata initial parser + * + * This loads all the metadata entry fields. This may cause additional + * fields to be processed (e.g. parent locator, etc..). + * + * There are 5 Metadata items that are always required: + * - File Parameters (block size, has a parent) + * - Virtual Disk Size (size, in bytes, of the virtual drive) + * - Page 83 Data (scsi page 83 guid) + * - Logical Sector Size (logical sector size in bytes, either 512 or + * 4096. We only support 512 currently) + * - Physical Sector Size (512 or 4096) + * + * Also, if the File Parameters indicate this is a differencing file, + * we must also look for the Parent Locator metadata item. + */ +static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) +{ + int ret = 0; + uint8_t *buffer; + int offset = 0; + uint32_t i = 0; + VHDXMetadataTableEntry md_entry; + + buffer = qemu_blockalign(bs, VHDX_METADATA_TABLE_MAX_SIZE); + + ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, buffer, + VHDX_METADATA_TABLE_MAX_SIZE); + if (ret < 0) { + goto exit; + } + memcpy(&s->metadata_hdr, buffer, sizeof(s->metadata_hdr)); + offset += sizeof(s->metadata_hdr); + + vhdx_metadata_header_le_import(&s->metadata_hdr); + + if (s->metadata_hdr.signature != VHDX_METADATA_SIGNATURE) { + ret = -EINVAL; + goto exit; + } + + s->metadata_entries.present = 0; + + if ((s->metadata_hdr.entry_count * sizeof(md_entry)) > + (VHDX_METADATA_TABLE_MAX_SIZE - offset)) { + ret = -EINVAL; + goto exit; + } + + for (i = 0; i < s->metadata_hdr.entry_count; i++) { + memcpy(&md_entry, buffer + offset, sizeof(md_entry)); + offset += sizeof(md_entry); + + vhdx_metadata_entry_le_import(&md_entry); + + if (guid_eq(md_entry.item_id, file_param_guid)) { + if (s->metadata_entries.present & META_FILE_PARAMETER_PRESENT) { + ret = -EINVAL; + goto exit; + } + s->metadata_entries.file_parameters_entry = md_entry; + s->metadata_entries.present |= META_FILE_PARAMETER_PRESENT; + continue; + } + + if (guid_eq(md_entry.item_id, virtual_size_guid)) { + if (s->metadata_entries.present & META_VIRTUAL_DISK_SIZE_PRESENT) { + ret = -EINVAL; + goto exit; + } + s->metadata_entries.virtual_disk_size_entry = md_entry; + s->metadata_entries.present |= META_VIRTUAL_DISK_SIZE_PRESENT; + continue; + } + + if (guid_eq(md_entry.item_id, page83_guid)) { + if (s->metadata_entries.present & META_PAGE_83_PRESENT) { + ret = -EINVAL; + goto exit; + } + s->metadata_entries.page83_data_entry = md_entry; + s->metadata_entries.present |= META_PAGE_83_PRESENT; + continue; + } + + if (guid_eq(md_entry.item_id, logical_sector_guid)) { + if (s->metadata_entries.present & + META_LOGICAL_SECTOR_SIZE_PRESENT) { + ret = -EINVAL; + goto exit; + } + s->metadata_entries.logical_sector_size_entry = md_entry; + s->metadata_entries.present |= META_LOGICAL_SECTOR_SIZE_PRESENT; + continue; + } + + if (guid_eq(md_entry.item_id, phys_sector_guid)) { + if (s->metadata_entries.present & META_PHYS_SECTOR_SIZE_PRESENT) { + ret = -EINVAL; + goto exit; + } + s->metadata_entries.phys_sector_size_entry = md_entry; + s->metadata_entries.present |= META_PHYS_SECTOR_SIZE_PRESENT; + continue; + } + + if (guid_eq(md_entry.item_id, parent_locator_guid)) { + if (s->metadata_entries.present & META_PARENT_LOCATOR_PRESENT) { + ret = -EINVAL; + goto exit; + } + s->metadata_entries.parent_locator_entry = md_entry; + s->metadata_entries.present |= META_PARENT_LOCATOR_PRESENT; + continue; + } + + if (md_entry.data_bits & VHDX_META_FLAGS_IS_REQUIRED) { + /* cannot read vhdx file - required region table entry that + * we do not understand. per spec, we must fail to open */ + ret = -ENOTSUP; + goto exit; + } + } + + if (s->metadata_entries.present != META_ALL_PRESENT) { + ret = -ENOTSUP; + goto exit; + } + + ret = bdrv_pread(bs->file, + s->metadata_entries.file_parameters_entry.offset + + s->metadata_rt.file_offset, + &s->params, + sizeof(s->params)); + + if (ret < 0) { + goto exit; + } + + le32_to_cpus(&s->params.block_size); + le32_to_cpus(&s->params.data_bits); + + + /* We now have the file parameters, so we can tell if this is a + * differencing file (i.e.. has_parent), is dynamic or fixed + * sized (leave_blocks_allocated), and the block size */ + + /* The parent locator required iff the file parameters has_parent set */ + if (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) { + if (s->metadata_entries.present & META_PARENT_LOCATOR_PRESENT) { + /* TODO: parse parent locator fields */ + ret = -ENOTSUP; /* temp, until differencing files are supported */ + goto exit; + } else { + /* if has_parent is set, but there is not parent locator present, + * then that is an invalid combination */ + ret = -EINVAL; + goto exit; + } + } + + /* determine virtual disk size, logical sector size, + * and phys sector size */ + + ret = bdrv_pread(bs->file, + s->metadata_entries.virtual_disk_size_entry.offset + + s->metadata_rt.file_offset, + &s->virtual_disk_size, + sizeof(uint64_t)); + if (ret < 0) { + goto exit; + } + ret = bdrv_pread(bs->file, + s->metadata_entries.logical_sector_size_entry.offset + + s->metadata_rt.file_offset, + &s->logical_sector_size, + sizeof(uint32_t)); + if (ret < 0) { + goto exit; + } + ret = bdrv_pread(bs->file, + s->metadata_entries.phys_sector_size_entry.offset + + s->metadata_rt.file_offset, + &s->physical_sector_size, + sizeof(uint32_t)); + if (ret < 0) { + goto exit; + } + + le64_to_cpus(&s->virtual_disk_size); + le32_to_cpus(&s->logical_sector_size); + le32_to_cpus(&s->physical_sector_size); + + if (s->params.block_size < VHDX_BLOCK_SIZE_MIN || + s->params.block_size > VHDX_BLOCK_SIZE_MAX) { + ret = -EINVAL; + goto exit; + } + + /* only 2 supported sector sizes */ + if (s->logical_sector_size != 512 && s->logical_sector_size != 4096) { + ret = -EINVAL; + goto exit; + } + + /* Both block_size and sector_size are guaranteed powers of 2, below. + Due to range checks above, s->sectors_per_block can never be < 256 */ + s->sectors_per_block = s->params.block_size / s->logical_sector_size; + s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) * + (uint64_t)s->logical_sector_size / + (uint64_t)s->params.block_size; + + /* These values are ones we will want to use for division / multiplication + * later on, and they are all guaranteed (per the spec) to be powers of 2, + * so we can take advantage of that for shift operations during + * reads/writes */ + if (s->logical_sector_size & (s->logical_sector_size - 1)) { + ret = -EINVAL; + goto exit; + } + if (s->sectors_per_block & (s->sectors_per_block - 1)) { + ret = -EINVAL; + goto exit; + } + if (s->chunk_ratio & (s->chunk_ratio - 1)) { + ret = -EINVAL; + goto exit; + } + s->block_size = s->params.block_size; + if (s->block_size & (s->block_size - 1)) { + ret = -EINVAL; + goto exit; + } + + vhdx_set_shift_bits(s); + + ret = 0; + +exit: + qemu_vfree(buffer); + return ret; +} + +/* + * Calculate the number of BAT entries, including sector + * bitmap entries. + */ +static void vhdx_calc_bat_entries(BDRVVHDXState *s) +{ + uint32_t data_blocks_cnt, bitmap_blocks_cnt; + + data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits; + if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) { + data_blocks_cnt++; + } + bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits; + if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) { + bitmap_blocks_cnt++; + } + + if (s->parent_entries) { + s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1); + } else { + s->bat_entries = data_blocks_cnt + + ((data_blocks_cnt - 1) >> s->chunk_ratio_bits); + } + +} + +static void vhdx_close(BlockDriverState *bs) +{ + BDRVVHDXState *s = bs->opaque; + qemu_vfree(s->headers[0]); + s->headers[0] = NULL; + qemu_vfree(s->headers[1]); + s->headers[1] = NULL; + qemu_vfree(s->bat); + s->bat = NULL; + qemu_vfree(s->parent_entries); + s->parent_entries = NULL; + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); + qemu_vfree(s->log.hdr); + s->log.hdr = NULL; + vhdx_region_unregister_all(s); +} + +static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVVHDXState *s = bs->opaque; + int ret = 0; + uint32_t i; + uint64_t signature; + Error *local_err = NULL; + + s->bat = NULL; + s->first_visible_write = true; + + qemu_co_mutex_init(&s->lock); + QLIST_INIT(&s->regions); + + /* validate the file signature */ + ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + if (memcmp(&signature, "vhdxfile", 8)) { + ret = -EINVAL; + goto fail; + } + + /* This is used for any header updates, for the file_write_guid. + * The spec dictates that a new value should be used for the first + * header update */ + vhdx_guid_generate(&s->session_guid); + + vhdx_parse_header(bs, s, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + ret = vhdx_parse_log(bs, s, &s->log_replayed_on_open, errp); + if (ret < 0) { + goto fail; + } + + ret = vhdx_open_region_tables(bs, s); + if (ret < 0) { + goto fail; + } + + ret = vhdx_parse_metadata(bs, s); + if (ret < 0) { + goto fail; + } + + s->block_size = s->params.block_size; + + /* the VHDX spec dictates that virtual_disk_size is always a multiple of + * logical_sector_size */ + bs->total_sectors = s->virtual_disk_size >> s->logical_sector_size_bits; + + vhdx_calc_bat_entries(s); + + s->bat_offset = s->bat_rt.file_offset; + + if (s->bat_entries > s->bat_rt.length / sizeof(VHDXBatEntry)) { + /* BAT allocation is not large enough for all entries */ + ret = -EINVAL; + goto fail; + } + + /* s->bat is freed in vhdx_close() */ + s->bat = qemu_try_blockalign(bs->file, s->bat_rt.length); + if (s->bat == NULL) { + ret = -ENOMEM; + goto fail; + } + + ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length); + if (ret < 0) { + goto fail; + } + + uint64_t payblocks = s->chunk_ratio; + /* endian convert, and verify populated BAT field file offsets against + * region table and log entries */ + for (i = 0; i < s->bat_entries; i++) { + le64_to_cpus(&s->bat[i]); + if (payblocks--) { + /* payload bat entries */ + if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) == + PAYLOAD_BLOCK_FULLY_PRESENT) { + ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK, + s->block_size); + if (ret < 0) { + goto fail; + } + } + } else { + payblocks = s->chunk_ratio; + /* Once differencing files are supported, verify sector bitmap + * blocks here */ + } + } + + if (flags & BDRV_O_RDWR) { + ret = vhdx_update_headers(bs, s, false, NULL); + if (ret < 0) { + goto fail; + } + } + + /* TODO: differencing files */ + + /* Disable migration when VHDX images are used */ + error_setg(&s->migration_blocker, "The vhdx format used by node '%s' " + "does not support live migration", + bdrv_get_device_or_node_name(bs)); + migrate_add_blocker(s->migration_blocker); + + return 0; +fail: + vhdx_close(bs); + return ret; +} + +static int vhdx_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + return 0; +} + + +/* + * Perform sector to block offset translations, to get various + * sector and file offsets into the image. See VHDXSectorInfo + */ +static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num, + int nb_sectors, VHDXSectorInfo *sinfo) +{ + uint32_t block_offset; + + sinfo->bat_idx = sector_num >> s->sectors_per_block_bits; + /* effectively a modulo - this gives us the offset into the block + * (in sector sizes) for our sector number */ + block_offset = sector_num - (sinfo->bat_idx << s->sectors_per_block_bits); + /* the chunk ratio gives us the interleaving of the sector + * bitmaps, so we need to advance our page block index by the + * sector bitmaps entry number */ + sinfo->bat_idx += sinfo->bat_idx >> s->chunk_ratio_bits; + + /* the number of sectors we can read/write in this cycle */ + sinfo->sectors_avail = s->sectors_per_block - block_offset; + + sinfo->bytes_left = sinfo->sectors_avail << s->logical_sector_size_bits; + + if (sinfo->sectors_avail > nb_sectors) { + sinfo->sectors_avail = nb_sectors; + } + + sinfo->bytes_avail = sinfo->sectors_avail << s->logical_sector_size_bits; + + sinfo->file_offset = s->bat[sinfo->bat_idx] & VHDX_BAT_FILE_OFF_MASK; + + sinfo->block_offset = block_offset << s->logical_sector_size_bits; + + /* The file offset must be past the header section, so must be > 0 */ + if (sinfo->file_offset == 0) { + return; + } + + /* block offset is the offset in vhdx logical sectors, in + * the payload data block. Convert that to a byte offset + * in the block, and add in the payload data block offset + * in the file, in bytes, to get the final read address */ + + sinfo->file_offset += sinfo->block_offset; +} + + +static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVVHDXState *s = bs->opaque; + + bdi->cluster_size = s->block_size; + + bdi->unallocated_blocks_are_zero = + (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) == 0; + + return 0; +} + + +static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVVHDXState *s = bs->opaque; + int ret = 0; + VHDXSectorInfo sinfo; + uint64_t bytes_done = 0; + QEMUIOVector hd_qiov; + + qemu_iovec_init(&hd_qiov, qiov->niov); + + qemu_co_mutex_lock(&s->lock); + + while (nb_sectors > 0) { + /* We are a differencing file, so we need to inspect the sector bitmap + * to see if we have the data or not */ + if (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) { + /* not supported yet */ + ret = -ENOTSUP; + goto exit; + } else { + vhdx_block_translate(s, sector_num, nb_sectors, &sinfo); + + qemu_iovec_reset(&hd_qiov); + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, sinfo.bytes_avail); + + /* check the payload block state */ + switch (s->bat[sinfo.bat_idx] & VHDX_BAT_STATE_BIT_MASK) { + case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ + case PAYLOAD_BLOCK_UNDEFINED: + case PAYLOAD_BLOCK_UNMAPPED: + case PAYLOAD_BLOCK_UNMAPPED_v095: + case PAYLOAD_BLOCK_ZERO: + /* return zero */ + qemu_iovec_memset(&hd_qiov, 0, 0, sinfo.bytes_avail); + break; + case PAYLOAD_BLOCK_FULLY_PRESENT: + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->file, + sinfo.file_offset >> BDRV_SECTOR_BITS, + sinfo.sectors_avail, &hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto exit; + } + break; + case PAYLOAD_BLOCK_PARTIALLY_PRESENT: + /* we don't yet support difference files, fall through + * to error */ + default: + ret = -EIO; + goto exit; + break; + } + nb_sectors -= sinfo.sectors_avail; + sector_num += sinfo.sectors_avail; + bytes_done += sinfo.bytes_avail; + } + } + ret = 0; +exit: + qemu_co_mutex_unlock(&s->lock); + qemu_iovec_destroy(&hd_qiov); + return ret; +} + +/* + * Allocate a new payload block at the end of the file. + * + * Allocation will happen at 1MB alignment inside the file + * + * Returns the file offset start of the new payload block + */ +static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, + uint64_t *new_offset) +{ + *new_offset = bdrv_getlength(bs->file); + + /* per the spec, the address for a block is in units of 1MB */ + *new_offset = ROUND_UP(*new_offset, 1024 * 1024); + + return bdrv_truncate(bs->file, *new_offset + s->block_size); +} + +/* + * Update the BAT table entry with the new file offset, and the new entry + * state */ +static void vhdx_update_bat_table_entry(BlockDriverState *bs, BDRVVHDXState *s, + VHDXSectorInfo *sinfo, + uint64_t *bat_entry_le, + uint64_t *bat_offset, int state) +{ + /* The BAT entry is a uint64, with 44 bits for the file offset in units of + * 1MB, and 3 bits for the block state. */ + if ((state == PAYLOAD_BLOCK_ZERO) || + (state == PAYLOAD_BLOCK_UNDEFINED) || + (state == PAYLOAD_BLOCK_NOT_PRESENT) || + (state == PAYLOAD_BLOCK_UNMAPPED)) { + s->bat[sinfo->bat_idx] = 0; /* For PAYLOAD_BLOCK_ZERO, the + FileOffsetMB field is denoted as + 'reserved' in the v1.0 spec. If it is + non-zero, MS Hyper-V will fail to read + the disk image */ + } else { + s->bat[sinfo->bat_idx] = sinfo->file_offset; + } + + s->bat[sinfo->bat_idx] |= state & VHDX_BAT_STATE_BIT_MASK; + + *bat_entry_le = cpu_to_le64(s->bat[sinfo->bat_idx]); + *bat_offset = s->bat_offset + sinfo->bat_idx * sizeof(VHDXBatEntry); + +} + +/* Per the spec, on the first write of guest-visible data to the file the + * data write guid must be updated in the header */ +int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s) +{ + int ret = 0; + if (s->first_visible_write) { + s->first_visible_write = false; + ret = vhdx_update_headers(bs, s, true, NULL); + } + return ret; +} + +static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + int ret = -ENOTSUP; + BDRVVHDXState *s = bs->opaque; + VHDXSectorInfo sinfo; + uint64_t bytes_done = 0; + uint64_t bat_entry = 0; + uint64_t bat_entry_offset = 0; + QEMUIOVector hd_qiov; + struct iovec iov1 = { 0 }; + struct iovec iov2 = { 0 }; + int sectors_to_write; + int bat_state; + uint64_t bat_prior_offset = 0; + bool bat_update = false; + + qemu_iovec_init(&hd_qiov, qiov->niov); + + qemu_co_mutex_lock(&s->lock); + + ret = vhdx_user_visible_write(bs, s); + if (ret < 0) { + goto exit; + } + + while (nb_sectors > 0) { + bool use_zero_buffers = false; + bat_update = false; + if (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) { + /* not supported yet */ + ret = -ENOTSUP; + goto exit; + } else { + vhdx_block_translate(s, sector_num, nb_sectors, &sinfo); + sectors_to_write = sinfo.sectors_avail; + + qemu_iovec_reset(&hd_qiov); + /* check the payload block state */ + bat_state = s->bat[sinfo.bat_idx] & VHDX_BAT_STATE_BIT_MASK; + switch (bat_state) { + case PAYLOAD_BLOCK_ZERO: + /* in this case, we need to preserve zero writes for + * data that is not part of this write, so we must pad + * the rest of the buffer to zeroes */ + + /* if we are on a posix system with ftruncate() that extends + * a file, then it is zero-filled for us. On Win32, the raw + * layer uses SetFilePointer and SetFileEnd, which does not + * zero fill AFAIK */ + + /* Queue another write of zero buffers if the underlying file + * does not zero-fill on file extension */ + + if (bdrv_has_zero_init(bs->file) == 0) { + use_zero_buffers = true; + + /* zero fill the front, if any */ + if (sinfo.block_offset) { + iov1.iov_len = sinfo.block_offset; + iov1.iov_base = qemu_blockalign(bs, iov1.iov_len); + memset(iov1.iov_base, 0, iov1.iov_len); + qemu_iovec_concat_iov(&hd_qiov, &iov1, 1, 0, + iov1.iov_len); + sectors_to_write += iov1.iov_len >> BDRV_SECTOR_BITS; + } + + /* our actual data */ + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, + sinfo.bytes_avail); + + /* zero fill the back, if any */ + if ((sinfo.bytes_avail - sinfo.block_offset) < + s->block_size) { + iov2.iov_len = s->block_size - + (sinfo.bytes_avail + sinfo.block_offset); + iov2.iov_base = qemu_blockalign(bs, iov2.iov_len); + memset(iov2.iov_base, 0, iov2.iov_len); + qemu_iovec_concat_iov(&hd_qiov, &iov2, 1, 0, + iov2.iov_len); + sectors_to_write += iov2.iov_len >> BDRV_SECTOR_BITS; + } + } + /* fall through */ + case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ + case PAYLOAD_BLOCK_UNMAPPED: + case PAYLOAD_BLOCK_UNMAPPED_v095: + case PAYLOAD_BLOCK_UNDEFINED: + bat_prior_offset = sinfo.file_offset; + ret = vhdx_allocate_block(bs, s, &sinfo.file_offset); + if (ret < 0) { + goto exit; + } + /* once we support differencing files, this may also be + * partially present */ + /* update block state to the newly specified state */ + vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry, + &bat_entry_offset, + PAYLOAD_BLOCK_FULLY_PRESENT); + bat_update = true; + /* since we just allocated a block, file_offset is the + * beginning of the payload block. It needs to be the + * write address, which includes the offset into the block */ + if (!use_zero_buffers) { + sinfo.file_offset += sinfo.block_offset; + } + /* fall through */ + case PAYLOAD_BLOCK_FULLY_PRESENT: + /* if the file offset address is in the header zone, + * there is a problem */ + if (sinfo.file_offset < (1024 * 1024)) { + ret = -EFAULT; + goto error_bat_restore; + } + + if (!use_zero_buffers) { + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, + sinfo.bytes_avail); + } + /* block exists, so we can just overwrite it */ + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_writev(bs->file, + sinfo.file_offset >> BDRV_SECTOR_BITS, + sectors_to_write, &hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto error_bat_restore; + } + break; + case PAYLOAD_BLOCK_PARTIALLY_PRESENT: + /* we don't yet support difference files, fall through + * to error */ + default: + ret = -EIO; + goto exit; + break; + } + + if (bat_update) { + /* this will update the BAT entry into the log journal, and + * then flush the log journal out to disk */ + ret = vhdx_log_write_and_flush(bs, s, &bat_entry, + sizeof(VHDXBatEntry), + bat_entry_offset); + if (ret < 0) { + goto exit; + } + } + + nb_sectors -= sinfo.sectors_avail; + sector_num += sinfo.sectors_avail; + bytes_done += sinfo.bytes_avail; + + } + } + + goto exit; + +error_bat_restore: + if (bat_update) { + /* keep metadata in sync, and restore the bat entry state + * if error. */ + sinfo.file_offset = bat_prior_offset; + vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry, + &bat_entry_offset, bat_state); + } +exit: + qemu_vfree(iov1.iov_base); + qemu_vfree(iov2.iov_base); + qemu_co_mutex_unlock(&s->lock); + qemu_iovec_destroy(&hd_qiov); + return ret; +} + + + +/* + * Create VHDX Headers + * + * There are 2 headers, and the highest sequence number will represent + * the active header + */ +static int vhdx_create_new_headers(BlockDriverState *bs, uint64_t image_size, + uint32_t log_size) +{ + int ret = 0; + VHDXHeader *hdr = NULL; + + hdr = g_new0(VHDXHeader, 1); + + hdr->signature = VHDX_HEADER_SIGNATURE; + hdr->sequence_number = g_random_int(); + hdr->log_version = 0; + hdr->version = 1; + hdr->log_length = log_size; + hdr->log_offset = VHDX_HEADER_SECTION_END; + vhdx_guid_generate(&hdr->file_write_guid); + vhdx_guid_generate(&hdr->data_write_guid); + + ret = vhdx_write_header(bs, hdr, VHDX_HEADER1_OFFSET, false); + if (ret < 0) { + goto exit; + } + hdr->sequence_number++; + ret = vhdx_write_header(bs, hdr, VHDX_HEADER2_OFFSET, false); + if (ret < 0) { + goto exit; + } + +exit: + g_free(hdr); + return ret; +} + +#define VHDX_METADATA_ENTRY_BUFFER_SIZE \ + (sizeof(VHDXFileParameters) +\ + sizeof(VHDXVirtualDiskSize) +\ + sizeof(VHDXPage83Data) +\ + sizeof(VHDXVirtualDiskLogicalSectorSize) +\ + sizeof(VHDXVirtualDiskPhysicalSectorSize)) + +/* + * Create the Metadata entries. + * + * For more details on the entries, see section 3.5 (pg 29) in the + * VHDX 1.00 specification. + * + * We support 5 metadata entries (all required by spec): + * File Parameters, + * Virtual Disk Size, + * Page 83 Data, + * Logical Sector Size, + * Physical Sector Size + * + * The first 64KB of the Metadata section is reserved for the metadata + * header and entries; beyond that, the metadata items themselves reside. + */ +static int vhdx_create_new_metadata(BlockDriverState *bs, + uint64_t image_size, + uint32_t block_size, + uint32_t sector_size, + uint64_t metadata_offset, + VHDXImageType type) +{ + int ret = 0; + uint32_t offset = 0; + void *buffer = NULL; + void *entry_buffer; + VHDXMetadataTableHeader *md_table;; + VHDXMetadataTableEntry *md_table_entry; + + /* Metadata entries */ + VHDXFileParameters *mt_file_params; + VHDXVirtualDiskSize *mt_virtual_size; + VHDXPage83Data *mt_page83; + VHDXVirtualDiskLogicalSectorSize *mt_log_sector_size; + VHDXVirtualDiskPhysicalSectorSize *mt_phys_sector_size; + + entry_buffer = g_malloc0(VHDX_METADATA_ENTRY_BUFFER_SIZE); + + mt_file_params = entry_buffer; + offset += sizeof(VHDXFileParameters); + mt_virtual_size = entry_buffer + offset; + offset += sizeof(VHDXVirtualDiskSize); + mt_page83 = entry_buffer + offset; + offset += sizeof(VHDXPage83Data); + mt_log_sector_size = entry_buffer + offset; + offset += sizeof(VHDXVirtualDiskLogicalSectorSize); + mt_phys_sector_size = entry_buffer + offset; + + mt_file_params->block_size = cpu_to_le32(block_size); + if (type == VHDX_TYPE_FIXED) { + mt_file_params->data_bits |= VHDX_PARAMS_LEAVE_BLOCKS_ALLOCED; + cpu_to_le32s(&mt_file_params->data_bits); + } + + vhdx_guid_generate(&mt_page83->page_83_data); + cpu_to_leguids(&mt_page83->page_83_data); + mt_virtual_size->virtual_disk_size = cpu_to_le64(image_size); + mt_log_sector_size->logical_sector_size = cpu_to_le32(sector_size); + mt_phys_sector_size->physical_sector_size = cpu_to_le32(sector_size); + + buffer = g_malloc0(VHDX_HEADER_BLOCK_SIZE); + md_table = buffer; + + md_table->signature = VHDX_METADATA_SIGNATURE; + md_table->entry_count = 5; + vhdx_metadata_header_le_export(md_table); + + + /* This will reference beyond the reserved table portion */ + offset = 64 * KiB; + + md_table_entry = buffer + sizeof(VHDXMetadataTableHeader); + + md_table_entry[0].item_id = file_param_guid; + md_table_entry[0].offset = offset; + md_table_entry[0].length = sizeof(VHDXFileParameters); + md_table_entry[0].data_bits |= VHDX_META_FLAGS_IS_REQUIRED; + offset += md_table_entry[0].length; + vhdx_metadata_entry_le_export(&md_table_entry[0]); + + md_table_entry[1].item_id = virtual_size_guid; + md_table_entry[1].offset = offset; + md_table_entry[1].length = sizeof(VHDXVirtualDiskSize); + md_table_entry[1].data_bits |= VHDX_META_FLAGS_IS_REQUIRED | + VHDX_META_FLAGS_IS_VIRTUAL_DISK; + offset += md_table_entry[1].length; + vhdx_metadata_entry_le_export(&md_table_entry[1]); + + md_table_entry[2].item_id = page83_guid; + md_table_entry[2].offset = offset; + md_table_entry[2].length = sizeof(VHDXPage83Data); + md_table_entry[2].data_bits |= VHDX_META_FLAGS_IS_REQUIRED | + VHDX_META_FLAGS_IS_VIRTUAL_DISK; + offset += md_table_entry[2].length; + vhdx_metadata_entry_le_export(&md_table_entry[2]); + + md_table_entry[3].item_id = logical_sector_guid; + md_table_entry[3].offset = offset; + md_table_entry[3].length = sizeof(VHDXVirtualDiskLogicalSectorSize); + md_table_entry[3].data_bits |= VHDX_META_FLAGS_IS_REQUIRED | + VHDX_META_FLAGS_IS_VIRTUAL_DISK; + offset += md_table_entry[3].length; + vhdx_metadata_entry_le_export(&md_table_entry[3]); + + md_table_entry[4].item_id = phys_sector_guid; + md_table_entry[4].offset = offset; + md_table_entry[4].length = sizeof(VHDXVirtualDiskPhysicalSectorSize); + md_table_entry[4].data_bits |= VHDX_META_FLAGS_IS_REQUIRED | + VHDX_META_FLAGS_IS_VIRTUAL_DISK; + vhdx_metadata_entry_le_export(&md_table_entry[4]); + + ret = bdrv_pwrite(bs, metadata_offset, buffer, VHDX_HEADER_BLOCK_SIZE); + if (ret < 0) { + goto exit; + } + + ret = bdrv_pwrite(bs, metadata_offset + (64 * KiB), entry_buffer, + VHDX_METADATA_ENTRY_BUFFER_SIZE); + if (ret < 0) { + goto exit; + } + + +exit: + g_free(buffer); + g_free(entry_buffer); + return ret; +} + +/* This create the actual BAT itself. We currently only support + * 'Dynamic' and 'Fixed' image types. + * + * Dynamic images: default state of the BAT is all zeroes. + * + * Fixed images: default state of the BAT is fully populated, with + * file offsets and state PAYLOAD_BLOCK_FULLY_PRESENT. + */ +static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s, + uint64_t image_size, VHDXImageType type, + bool use_zero_blocks, uint64_t file_offset, + uint32_t length) +{ + int ret = 0; + uint64_t data_file_offset; + uint64_t total_sectors = 0; + uint64_t sector_num = 0; + uint64_t unused; + int block_state; + VHDXSectorInfo sinfo; + + assert(s->bat == NULL); + + /* this gives a data start after BAT/bitmap entries, and well + * past any metadata entries (with a 4 MB buffer for future + * expansion */ + data_file_offset = file_offset + length + 5 * MiB; + total_sectors = image_size >> s->logical_sector_size_bits; + + if (type == VHDX_TYPE_DYNAMIC) { + /* All zeroes, so we can just extend the file - the end of the BAT + * is the furthest thing we have written yet */ + ret = bdrv_truncate(bs, data_file_offset); + if (ret < 0) { + goto exit; + } + } else if (type == VHDX_TYPE_FIXED) { + ret = bdrv_truncate(bs, data_file_offset + image_size); + if (ret < 0) { + goto exit; + } + } else { + ret = -ENOTSUP; + goto exit; + } + + if (type == VHDX_TYPE_FIXED || + use_zero_blocks || + bdrv_has_zero_init(bs) == 0) { + /* for a fixed file, the default BAT entry is not zero */ + s->bat = g_try_malloc0(length); + if (length && s->bat == NULL) { + ret = -ENOMEM; + goto exit; + } + block_state = type == VHDX_TYPE_FIXED ? PAYLOAD_BLOCK_FULLY_PRESENT : + PAYLOAD_BLOCK_NOT_PRESENT; + block_state = use_zero_blocks ? PAYLOAD_BLOCK_ZERO : block_state; + /* fill the BAT by emulating sector writes of sectors_per_block size */ + while (sector_num < total_sectors) { + vhdx_block_translate(s, sector_num, s->sectors_per_block, &sinfo); + sinfo.file_offset = data_file_offset + + (sector_num << s->logical_sector_size_bits); + sinfo.file_offset = ROUND_UP(sinfo.file_offset, MiB); + vhdx_update_bat_table_entry(bs, s, &sinfo, &unused, &unused, + block_state); + cpu_to_le64s(&s->bat[sinfo.bat_idx]); + sector_num += s->sectors_per_block; + } + ret = bdrv_pwrite(bs, file_offset, s->bat, length); + if (ret < 0) { + goto exit; + } + } + + + +exit: + g_free(s->bat); + return ret; +} + +/* Creates the region table header, and region table entries. + * There are 2 supported region table entries: BAT, and Metadata/ + * + * As the calculations for the BAT region table are also needed + * to create the BAT itself, we will also cause the BAT to be + * created. + */ +static int vhdx_create_new_region_table(BlockDriverState *bs, + uint64_t image_size, + uint32_t block_size, + uint32_t sector_size, + uint32_t log_size, + bool use_zero_blocks, + VHDXImageType type, + uint64_t *metadata_offset) +{ + int ret = 0; + uint32_t offset = 0; + void *buffer = NULL; + uint64_t bat_file_offset; + uint32_t bat_length; + BDRVVHDXState *s = NULL; + VHDXRegionTableHeader *region_table; + VHDXRegionTableEntry *rt_bat; + VHDXRegionTableEntry *rt_metadata; + + assert(metadata_offset != NULL); + + /* Populate enough of the BDRVVHDXState to be able to use the + * pre-existing BAT calculation, translation, and update functions */ + s = g_new0(BDRVVHDXState, 1); + + s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) * + (uint64_t) sector_size / (uint64_t) block_size; + + s->sectors_per_block = block_size / sector_size; + s->virtual_disk_size = image_size; + s->block_size = block_size; + s->logical_sector_size = sector_size; + + vhdx_set_shift_bits(s); + + vhdx_calc_bat_entries(s); + + /* At this point the VHDX state is populated enough for creation */ + + /* a single buffer is used so we can calculate the checksum over the + * entire 64KB block */ + buffer = g_malloc0(VHDX_HEADER_BLOCK_SIZE); + region_table = buffer; + offset += sizeof(VHDXRegionTableHeader); + rt_bat = buffer + offset; + offset += sizeof(VHDXRegionTableEntry); + rt_metadata = buffer + offset; + + region_table->signature = VHDX_REGION_SIGNATURE; + region_table->entry_count = 2; /* BAT and Metadata */ + + rt_bat->guid = bat_guid; + rt_bat->length = ROUND_UP(s->bat_entries * sizeof(VHDXBatEntry), MiB); + rt_bat->file_offset = ROUND_UP(VHDX_HEADER_SECTION_END + log_size, MiB); + s->bat_offset = rt_bat->file_offset; + + rt_metadata->guid = metadata_guid; + rt_metadata->file_offset = ROUND_UP(rt_bat->file_offset + rt_bat->length, + MiB); + rt_metadata->length = 1 * MiB; /* min size, and more than enough */ + *metadata_offset = rt_metadata->file_offset; + + bat_file_offset = rt_bat->file_offset; + bat_length = rt_bat->length; + + vhdx_region_header_le_export(region_table); + vhdx_region_entry_le_export(rt_bat); + vhdx_region_entry_le_export(rt_metadata); + + vhdx_update_checksum(buffer, VHDX_HEADER_BLOCK_SIZE, + offsetof(VHDXRegionTableHeader, checksum)); + + + /* The region table gives us the data we need to create the BAT, + * so do that now */ + ret = vhdx_create_bat(bs, s, image_size, type, use_zero_blocks, + bat_file_offset, bat_length); + if (ret < 0) { + goto exit; + } + + /* Now write out the region headers to disk */ + ret = bdrv_pwrite(bs, VHDX_REGION_TABLE_OFFSET, buffer, + VHDX_HEADER_BLOCK_SIZE); + if (ret < 0) { + goto exit; + } + + ret = bdrv_pwrite(bs, VHDX_REGION_TABLE2_OFFSET, buffer, + VHDX_HEADER_BLOCK_SIZE); + if (ret < 0) { + goto exit; + } + +exit: + g_free(s); + g_free(buffer); + return ret; +} + +/* We need to create the following elements: + * + * .-----------------------------------------------------------------. + * | (A) | (B) | (C) | (D) | (E) | + * | File ID | Header1 | Header 2 | Region Tbl 1 | Region Tbl 2 | + * | | | | | | + * .-----------------------------------------------------------------. + * 0 64KB 128KB 192KB 256KB 320KB + * + * + * .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------. + * | (F) | (G) | (H) | | + * | Journal Log | BAT / Bitmap | Metadata | .... data ...... | + * | | | | | + * .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------. + * 1MB + */ +static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int ret = 0; + uint64_t image_size = (uint64_t) 2 * GiB; + uint32_t log_size = 1 * MiB; + uint32_t block_size = 0; + uint64_t signature; + uint64_t metadata_offset; + bool use_zero_blocks = false; + + gunichar2 *creator = NULL; + glong creator_items; + BlockDriverState *bs; + char *type = NULL; + VHDXImageType image_type; + Error *local_err = NULL; + + image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0); + block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0); + type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); + use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, true); + + if (image_size > VHDX_MAX_IMAGE_SIZE) { + error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB"); + ret = -EINVAL; + goto exit; + } + + if (type == NULL) { + type = g_strdup("dynamic"); + } + + if (!strcmp(type, "dynamic")) { + image_type = VHDX_TYPE_DYNAMIC; + } else if (!strcmp(type, "fixed")) { + image_type = VHDX_TYPE_FIXED; + } else if (!strcmp(type, "differencing")) { + error_setg_errno(errp, ENOTSUP, + "Differencing files not yet supported"); + ret = -ENOTSUP; + goto exit; + } else { + ret = -EINVAL; + goto exit; + } + + /* These are pretty arbitrary, and mainly designed to keep the BAT + * size reasonable to load into RAM */ + if (block_size == 0) { + if (image_size > 32 * TiB) { + block_size = 64 * MiB; + } else if (image_size > (uint64_t) 100 * GiB) { + block_size = 32 * MiB; + } else if (image_size > 1 * GiB) { + block_size = 16 * MiB; + } else { + block_size = 8 * MiB; + } + } + + + /* make the log size close to what was specified, but must be + * min 1MB, and multiple of 1MB */ + log_size = ROUND_UP(log_size, MiB); + + block_size = ROUND_UP(block_size, MiB); + block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX : + block_size; + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + + bs = NULL; + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + + /* Create (A) */ + + /* The creator field is optional, but may be useful for + * debugging / diagnostics */ + creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL, + &creator_items, NULL); + signature = cpu_to_le64(VHDX_FILE_SIGNATURE); + ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature)); + if (ret < 0) { + goto delete_and_exit; + } + if (creator) { + ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature), + creator, creator_items * sizeof(gunichar2)); + if (ret < 0) { + goto delete_and_exit; + } + } + + + /* Creates (B),(C) */ + ret = vhdx_create_new_headers(bs, image_size, log_size); + if (ret < 0) { + goto delete_and_exit; + } + + /* Creates (D),(E),(G) explicitly. (F) created as by-product */ + ret = vhdx_create_new_region_table(bs, image_size, block_size, 512, + log_size, use_zero_blocks, image_type, + &metadata_offset); + if (ret < 0) { + goto delete_and_exit; + } + + /* Creates (H) */ + ret = vhdx_create_new_metadata(bs, image_size, block_size, 512, + metadata_offset, image_type); + if (ret < 0) { + goto delete_and_exit; + } + + +delete_and_exit: + bdrv_unref(bs); +exit: + g_free(type); + g_free(creator); + return ret; +} + +/* If opened r/w, the VHDX driver will automatically replay the log, + * if one is present, inside the vhdx_open() call. + * + * If qemu-img check -r all is called, the image is automatically opened + * r/w and any log has already been replayed, so there is nothing (currently) + * for us to do here + */ +static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) +{ + BDRVVHDXState *s = bs->opaque; + + if (s->log_replayed_on_open) { + result->corruptions_fixed++; + } + return 0; +} + +static QemuOptsList vhdx_create_opts = { + .name = "vhdx-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(vhdx_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size; max of 64TB." + }, + { + .name = VHDX_BLOCK_OPT_LOG_SIZE, + .type = QEMU_OPT_SIZE, + .def_value_str = stringify(DEFAULT_LOG_SIZE), + .help = "Log size; min 1MB." + }, + { + .name = VHDX_BLOCK_OPT_BLOCK_SIZE, + .type = QEMU_OPT_SIZE, + .def_value_str = stringify(0), + .help = "Block Size; min 1MB, max 256MB. " \ + "0 means auto-calculate based on image size." + }, + { + .name = BLOCK_OPT_SUBFMT, + .type = QEMU_OPT_STRING, + .help = "VHDX format type, can be either 'dynamic' or 'fixed'. "\ + "Default is 'dynamic'." + }, + { + .name = VHDX_BLOCK_OPT_ZERO, + .type = QEMU_OPT_BOOL, + .help = "Force use of payload blocks of type 'ZERO'. "\ + "Non-standard, but default. Do not set to 'off' when "\ + "using 'qemu-img convert' with subformat=dynamic." + }, + { NULL } + } +}; + +static BlockDriver bdrv_vhdx = { + .format_name = "vhdx", + .instance_size = sizeof(BDRVVHDXState), + .bdrv_probe = vhdx_probe, + .bdrv_open = vhdx_open, + .bdrv_close = vhdx_close, + .bdrv_reopen_prepare = vhdx_reopen_prepare, + .bdrv_co_readv = vhdx_co_readv, + .bdrv_co_writev = vhdx_co_writev, + .bdrv_create = vhdx_create, + .bdrv_get_info = vhdx_get_info, + .bdrv_check = vhdx_check, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + + .create_opts = &vhdx_create_opts, +}; + +static void bdrv_vhdx_init(void) +{ + bdrv_register(&bdrv_vhdx); +} + +block_init(bdrv_vhdx_init); diff --git a/block/vhdx.h b/block/vhdx.h new file mode 100644 index 0000000..7003ab7 --- /dev/null +++ b/block/vhdx.h @@ -0,0 +1,453 @@ +/* + * Block driver for Hyper-V VHDX Images + * + * Copyright (c) 2013 Red Hat, Inc., + * + * Authors: + * Jeff Cody + * + * This is based on the "VHDX Format Specification v1.00", published 8/25/2012 + * by Microsoft: + * https://www.microsoft.com/en-us/download/details.aspx?id=34750 + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef BLOCK_VHDX_H +#define BLOCK_VHDX_H + +#define KiB (1 * 1024) +#define MiB (KiB * 1024) +#define GiB (MiB * 1024) +#define TiB ((uint64_t) GiB * 1024) + +#define DEFAULT_LOG_SIZE 1048576 /* 1MiB */ +/* Structures and fields present in the VHDX file */ + +/* The header section has the following blocks, + * each block is 64KB: + * + * _____________________________________________________________________________ + * | File Id. | Header 1 | Header 2 | Region Table | Reserved (768KB) | + * |----------|---------------|------------|--------------|--------------------| + * | | | | | | + * 0.........64KB...........128KB........192KB..........256KB................1MB + */ + +#define VHDX_HEADER_BLOCK_SIZE (64 * 1024) + +#define VHDX_FILE_ID_OFFSET 0 +#define VHDX_HEADER1_OFFSET (VHDX_HEADER_BLOCK_SIZE * 1) +#define VHDX_HEADER2_OFFSET (VHDX_HEADER_BLOCK_SIZE * 2) +#define VHDX_REGION_TABLE_OFFSET (VHDX_HEADER_BLOCK_SIZE * 3) +#define VHDX_REGION_TABLE2_OFFSET (VHDX_HEADER_BLOCK_SIZE * 4) + +#define VHDX_HEADER_SECTION_END (1 * MiB) +/* + * A note on the use of MS-GUID fields. For more details on the GUID, + * please see: https://en.wikipedia.org/wiki/Globally_unique_identifier. + * + * The VHDX specification only states that these are MS GUIDs, and which + * bytes are data1-data4. It makes no mention of what algorithm should be used + * to generate the GUID, nor what standard. However, looking at the specified + * known GUID fields, it appears the GUIDs are: + * Standard/DCE GUID type (noted by 10b in the MSB of byte 0 of .data4) + * Random algorithm (noted by 0x4XXX for .data3) + */ + +/* ---- HEADER SECTION STRUCTURES ---- */ + +/* These structures are ones that are defined in the VHDX specification + * document */ + +#define VHDX_FILE_SIGNATURE 0x656C696678646876ULL /* "vhdxfile" in ASCII */ +typedef struct VHDXFileIdentifier { + uint64_t signature; /* "vhdxfile" in ASCII */ + uint16_t creator[256]; /* optional; utf-16 string to identify + the vhdx file creator. Diagnostic + only */ +} VHDXFileIdentifier; + + +/* the guid is a 16 byte unique ID - the definition for this used by + * Microsoft is not just 16 bytes though - it is a structure that is defined, + * so we need to follow it here so that endianness does not trip us up */ + +typedef struct QEMU_PACKED MSGUID { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; +} MSGUID; + +#define guid_eq(a, b) \ + (memcmp(&(a), &(b), sizeof(MSGUID)) == 0) + +#define VHDX_HEADER_SIZE (4 * 1024) /* although the vhdx_header struct in disk + is only 582 bytes, for purposes of crc + the header is the first 4KB of the 64KB + block */ + +/* The full header is 4KB, although the actual header data is much smaller. + * But for the checksum calculation, it is over the entire 4KB structure, + * not just the defined portion of it */ +#define VHDX_HEADER_SIGNATURE 0x64616568 +typedef struct QEMU_PACKED VHDXHeader { + uint32_t signature; /* "head" in ASCII */ + uint32_t checksum; /* CRC-32C hash of the whole header */ + uint64_t sequence_number; /* Seq number of this header. Each + VHDX file has 2 of these headers, + and only the header with the highest + sequence number is valid */ + MSGUID file_write_guid; /* 128 bit unique identifier. Must be + updated to new, unique value before + the first modification is made to + file */ + MSGUID data_write_guid; /* 128 bit unique identifier. Must be + updated to new, unique value before + the first modification is made to + visible data. Visbile data is + defined as: + - system & user metadata + - raw block data + - disk size + - any change that will + cause the virtual disk + sector read to differ + + This does not need to change if + blocks are re-arranged */ + MSGUID log_guid; /* 128 bit unique identifier. If zero, + there is no valid log. If non-zero, + log entries with this guid are + valid. */ + uint16_t log_version; /* version of the log format. Must be + set to zero */ + uint16_t version; /* version of the vhdx file. Currently, + only supported version is "1" */ + uint32_t log_length; /* length of the log. Must be multiple + of 1MB */ + uint64_t log_offset; /* byte offset in the file of the log. + Must also be a multiple of 1MB */ +} VHDXHeader; + +/* Header for the region table block */ +#define VHDX_REGION_SIGNATURE 0x69676572 /* "regi" in ASCII */ +typedef struct QEMU_PACKED VHDXRegionTableHeader { + uint32_t signature; /* "regi" in ASCII */ + uint32_t checksum; /* CRC-32C hash of the 64KB table */ + uint32_t entry_count; /* number of valid entries */ + uint32_t reserved; +} VHDXRegionTableHeader; + +/* Individual region table entry. There may be a maximum of 2047 of these + * + * There are two known region table properties. Both are required. + * BAT (block allocation table): 2DC27766F62342009D64115E9BFD4A08 + * Metadata: 8B7CA20647904B9AB8FE575F050F886E + */ +#define VHDX_REGION_ENTRY_REQUIRED 0x01 /* if set, parser must understand + this entry in order to open + file */ +typedef struct QEMU_PACKED VHDXRegionTableEntry { + MSGUID guid; /* 128-bit unique identifier */ + uint64_t file_offset; /* offset of the object in the file. + Must be multiple of 1MB */ + uint32_t length; /* length, in bytes, of the object */ + uint32_t data_bits; +} VHDXRegionTableEntry; + + +/* ---- LOG ENTRY STRUCTURES ---- */ +#define VHDX_LOG_MIN_SIZE (1024 * 1024) +#define VHDX_LOG_SECTOR_SIZE 4096 +#define VHDX_LOG_HDR_SIZE 64 +#define VHDX_LOG_SIGNATURE 0x65676f6c +typedef struct QEMU_PACKED VHDXLogEntryHeader { + uint32_t signature; /* "loge" in ASCII */ + uint32_t checksum; /* CRC-32C hash of the 64KB table */ + uint32_t entry_length; /* length in bytes, multiple of 1MB */ + uint32_t tail; /* byte offset of first log entry of a + seq, where this entry is the last + entry */ + uint64_t sequence_number; /* incremented with each log entry. + May not be zero. */ + uint32_t descriptor_count; /* number of descriptors in this log + entry, must be >= 0 */ + uint32_t reserved; + MSGUID log_guid; /* value of the log_guid from + vhdx_header. If not found in + vhdx_header, it is invalid */ + uint64_t flushed_file_offset; /* see spec for full details - this + should be vhdx file size in bytes */ + uint64_t last_file_offset; /* size in bytes that all allocated + file structures fit into */ +} VHDXLogEntryHeader; + +#define VHDX_LOG_DESC_SIZE 32 +#define VHDX_LOG_DESC_SIGNATURE 0x63736564 +#define VHDX_LOG_ZERO_SIGNATURE 0x6f72657a +typedef struct QEMU_PACKED VHDXLogDescriptor { + uint32_t signature; /* "zero" or "desc" in ASCII */ + union { + uint32_t reserved; /* zero desc */ + uint32_t trailing_bytes; /* data desc: bytes 4092-4096 of the + data sector */ + }; + union { + uint64_t zero_length; /* zero desc: length of the section to + zero */ + uint64_t leading_bytes; /* data desc: bytes 0-7 of the data + sector */ + }; + uint64_t file_offset; /* file offset to write zeros - multiple + of 4kB */ + uint64_t sequence_number; /* must match same field in + vhdx_log_entry_header */ +} VHDXLogDescriptor; + +#define VHDX_LOG_DATA_SIGNATURE 0x61746164 +typedef struct QEMU_PACKED VHDXLogDataSector { + uint32_t data_signature; /* "data" in ASCII */ + uint32_t sequence_high; /* 4 MSB of 8 byte sequence_number */ + uint8_t data[4084]; /* raw data, bytes 8-4091 (inclusive). + see the data descriptor field for the + other mising bytes */ + uint32_t sequence_low; /* 4 LSB of 8 byte sequence_number */ +} VHDXLogDataSector; + + + +/* block states - different state values depending on whether it is a + * payload block, or a sector block. */ + +#define PAYLOAD_BLOCK_NOT_PRESENT 0 +#define PAYLOAD_BLOCK_UNDEFINED 1 +#define PAYLOAD_BLOCK_ZERO 2 +#define PAYLOAD_BLOCK_UNMAPPED 3 +#define PAYLOAD_BLOCK_UNMAPPED_v095 5 +#define PAYLOAD_BLOCK_FULLY_PRESENT 6 +#define PAYLOAD_BLOCK_PARTIALLY_PRESENT 7 + +#define SB_BLOCK_NOT_PRESENT 0 +#define SB_BLOCK_PRESENT 6 + +/* per the spec */ +#define VHDX_MAX_SECTORS_PER_BLOCK (1 << 23) + +/* upper 44 bits are the file offset in 1MB units lower 3 bits are the state + other bits are reserved */ +#define VHDX_BAT_STATE_BIT_MASK 0x07 +#define VHDX_BAT_FILE_OFF_MASK 0xFFFFFFFFFFF00000ULL /* upper 44 bits */ +typedef uint64_t VHDXBatEntry; + +/* ---- METADATA REGION STRUCTURES ---- */ + +#define VHDX_METADATA_ENTRY_SIZE 32 +#define VHDX_METADATA_MAX_ENTRIES 2047 /* not including the header */ +#define VHDX_METADATA_TABLE_MAX_SIZE \ + (VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1)) +#define VHDX_METADATA_SIGNATURE 0x617461646174656DULL /* "metadata" in ASCII */ +typedef struct QEMU_PACKED VHDXMetadataTableHeader { + uint64_t signature; /* "metadata" in ASCII */ + uint16_t reserved; + uint16_t entry_count; /* number table entries. <= 2047 */ + uint32_t reserved2[5]; +} VHDXMetadataTableHeader; + +#define VHDX_META_FLAGS_IS_USER 0x01 /* max 1024 entries */ +#define VHDX_META_FLAGS_IS_VIRTUAL_DISK 0x02 /* virtual disk metadata if set, + otherwise file metdata */ +#define VHDX_META_FLAGS_IS_REQUIRED 0x04 /* parse must understand this + entry to open the file */ +typedef struct QEMU_PACKED VHDXMetadataTableEntry { + MSGUID item_id; /* 128-bit identifier for metadata */ + uint32_t offset; /* byte offset of the metadata. At + least 64kB. Relative to start of + metadata region */ + /* note: if length = 0, so is offset */ + uint32_t length; /* length of metadata. <= 1MB. */ + uint32_t data_bits; /* least-significant 3 bits are flags, + the rest are reserved (see above) */ + uint32_t reserved2; +} VHDXMetadataTableEntry; + +#define VHDX_PARAMS_LEAVE_BLOCKS_ALLOCED 0x01 /* Do not change any blocks to + be BLOCK_NOT_PRESENT. + If set indicates a fixed + size VHDX file */ +#define VHDX_PARAMS_HAS_PARENT 0x02 /* has parent / backing file */ +#define VHDX_BLOCK_SIZE_MIN (1 * MiB) +#define VHDX_BLOCK_SIZE_MAX (256 * MiB) +typedef struct QEMU_PACKED VHDXFileParameters { + uint32_t block_size; /* size of each payload block, always + power of 2, <= 256MB and >= 1MB. */ + uint32_t data_bits; /* least-significant 2 bits are flags, + the rest are reserved (see above) */ +} VHDXFileParameters; + +#define VHDX_MAX_IMAGE_SIZE ((uint64_t) 64 * TiB) +typedef struct QEMU_PACKED VHDXVirtualDiskSize { + uint64_t virtual_disk_size; /* Size of the virtual disk, in bytes. + Must be multiple of the sector size, + max of 64TB */ +} VHDXVirtualDiskSize; + +typedef struct QEMU_PACKED VHDXPage83Data { + MSGUID page_83_data; /* unique id for scsi devices that + support page 0x83 */ +} VHDXPage83Data; + +typedef struct QEMU_PACKED VHDXVirtualDiskLogicalSectorSize { + uint32_t logical_sector_size; /* virtual disk sector size (in bytes). + Can only be 512 or 4096 bytes */ +} VHDXVirtualDiskLogicalSectorSize; + +typedef struct QEMU_PACKED VHDXVirtualDiskPhysicalSectorSize { + uint32_t physical_sector_size; /* physical sector size (in bytes). + Can only be 512 or 4096 bytes */ +} VHDXVirtualDiskPhysicalSectorSize; + +typedef struct QEMU_PACKED VHDXParentLocatorHeader { + MSGUID locator_type; /* type of the parent virtual disk. */ + uint16_t reserved; + uint16_t key_value_count; /* number of key/value pairs for this + locator */ +} VHDXParentLocatorHeader; + +/* key and value strings are UNICODE strings, UTF-16 LE encoding, no NULs */ +typedef struct QEMU_PACKED VHDXParentLocatorEntry { + uint32_t key_offset; /* offset in metadata for key, > 0 */ + uint32_t value_offset; /* offset in metadata for value, >0 */ + uint16_t key_length; /* length of entry key, > 0 */ + uint16_t value_length; /* length of entry value, > 0 */ +} VHDXParentLocatorEntry; + + +/* ----- END VHDX SPECIFICATION STRUCTURES ---- */ + +typedef struct VHDXMetadataEntries { + VHDXMetadataTableEntry file_parameters_entry; + VHDXMetadataTableEntry virtual_disk_size_entry; + VHDXMetadataTableEntry page83_data_entry; + VHDXMetadataTableEntry logical_sector_size_entry; + VHDXMetadataTableEntry phys_sector_size_entry; + VHDXMetadataTableEntry parent_locator_entry; + uint16_t present; +} VHDXMetadataEntries; + +typedef struct VHDXLogEntries { + uint64_t offset; + uint64_t length; + uint32_t write; + uint32_t read; + VHDXLogEntryHeader *hdr; + void *desc_buffer; + uint64_t sequence; + uint32_t tail; +} VHDXLogEntries; + +typedef struct VHDXRegionEntry { + uint64_t start; + uint64_t end; + QLIST_ENTRY(VHDXRegionEntry) entries; +} VHDXRegionEntry; + +typedef struct BDRVVHDXState { + CoMutex lock; + + int curr_header; + VHDXHeader *headers[2]; + + VHDXRegionTableHeader rt; + VHDXRegionTableEntry bat_rt; /* region table for the BAT */ + VHDXRegionTableEntry metadata_rt; /* region table for the metadata */ + + VHDXMetadataTableHeader metadata_hdr; + VHDXMetadataEntries metadata_entries; + + VHDXFileParameters params; + uint32_t block_size; + uint32_t block_size_bits; + uint32_t sectors_per_block; + uint32_t sectors_per_block_bits; + + uint64_t virtual_disk_size; + uint32_t logical_sector_size; + uint32_t physical_sector_size; + + uint64_t chunk_ratio; + uint32_t chunk_ratio_bits; + uint32_t logical_sector_size_bits; + + uint32_t bat_entries; + VHDXBatEntry *bat; + uint64_t bat_offset; + + bool first_visible_write; + MSGUID session_guid; + + VHDXLogEntries log; + + VHDXParentLocatorHeader parent_header; + VHDXParentLocatorEntry *parent_entries; + + Error *migration_blocker; + + bool log_replayed_on_open; + + QLIST_HEAD(VHDXRegionHead, VHDXRegionEntry) regions; +} BDRVVHDXState; + +void vhdx_guid_generate(MSGUID *guid); + +int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, bool rw, + MSGUID *log_guid); + +uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset); +uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, + int crc_offset); + +bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset); + +int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed, + Error **errp); + +int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset); + +static inline void leguid_to_cpus(MSGUID *guid) +{ + le32_to_cpus(&guid->data1); + le16_to_cpus(&guid->data2); + le16_to_cpus(&guid->data3); +} + +static inline void cpu_to_leguids(MSGUID *guid) +{ + cpu_to_le32s(&guid->data1); + cpu_to_le16s(&guid->data2); + cpu_to_le16s(&guid->data3); +} + +void vhdx_header_le_import(VHDXHeader *h); +void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h); +void vhdx_log_desc_le_import(VHDXLogDescriptor *d); +void vhdx_log_desc_le_export(VHDXLogDescriptor *d); +void vhdx_log_data_le_import(VHDXLogDataSector *d); +void vhdx_log_data_le_export(VHDXLogDataSector *d); +void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr); +void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr); +void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr); +void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr); +void vhdx_region_entry_le_import(VHDXRegionTableEntry *e); +void vhdx_region_entry_le_export(VHDXRegionTableEntry *e); +void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr); +void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr); +void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e); +void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e); +int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s); + +#endif diff --git a/block/vmdk.c b/block/vmdk.c new file mode 100644 index 0000000..b66745d --- /dev/null +++ b/block/vmdk.c @@ -0,0 +1,2311 @@ +/* + * Block driver for the VMDK format + * + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2005 Filip Navara + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "migration/migration.h" +#include +#include + +#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') +#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') +#define VMDK4_COMPRESSION_DEFLATE 1 +#define VMDK4_FLAG_NL_DETECT (1 << 0) +#define VMDK4_FLAG_RGD (1 << 1) +/* Zeroed-grain enable bit */ +#define VMDK4_FLAG_ZERO_GRAIN (1 << 2) +#define VMDK4_FLAG_COMPRESS (1 << 16) +#define VMDK4_FLAG_MARKER (1 << 17) +#define VMDK4_GD_AT_END 0xffffffffffffffffULL + +#define VMDK_GTE_ZEROED 0x1 + +/* VMDK internal error codes */ +#define VMDK_OK 0 +#define VMDK_ERROR (-1) +/* Cluster not allocated */ +#define VMDK_UNALLOC (-2) +#define VMDK_ZEROED (-3) + +#define BLOCK_OPT_ZEROED_GRAIN "zeroed_grain" + +typedef struct { + uint32_t version; + uint32_t flags; + uint32_t disk_sectors; + uint32_t granularity; + uint32_t l1dir_offset; + uint32_t l1dir_size; + uint32_t file_sectors; + uint32_t cylinders; + uint32_t heads; + uint32_t sectors_per_track; +} QEMU_PACKED VMDK3Header; + +typedef struct { + uint32_t version; + uint32_t flags; + uint64_t capacity; + uint64_t granularity; + uint64_t desc_offset; + uint64_t desc_size; + /* Number of GrainTableEntries per GrainTable */ + uint32_t num_gtes_per_gt; + uint64_t rgd_offset; + uint64_t gd_offset; + uint64_t grain_offset; + char filler[1]; + char check_bytes[4]; + uint16_t compressAlgorithm; +} QEMU_PACKED VMDK4Header; + +#define L2_CACHE_SIZE 16 + +typedef struct VmdkExtent { + BlockDriverState *file; + bool flat; + bool compressed; + bool has_marker; + bool has_zero_grain; + int version; + int64_t sectors; + int64_t end_sector; + int64_t flat_start_offset; + int64_t l1_table_offset; + int64_t l1_backup_table_offset; + uint32_t *l1_table; + uint32_t *l1_backup_table; + unsigned int l1_size; + uint32_t l1_entry_sectors; + + unsigned int l2_size; + uint32_t *l2_cache; + uint32_t l2_cache_offsets[L2_CACHE_SIZE]; + uint32_t l2_cache_counts[L2_CACHE_SIZE]; + + int64_t cluster_sectors; + int64_t next_cluster_sector; + char *type; +} VmdkExtent; + +typedef struct BDRVVmdkState { + CoMutex lock; + uint64_t desc_offset; + bool cid_updated; + bool cid_checked; + uint32_t cid; + uint32_t parent_cid; + int num_extents; + /* Extent array with num_extents entries, ascend ordered by address */ + VmdkExtent *extents; + Error *migration_blocker; + char *create_type; +} BDRVVmdkState; + +typedef struct VmdkMetaData { + unsigned int l1_index; + unsigned int l2_index; + unsigned int l2_offset; + int valid; + uint32_t *l2_cache_entry; +} VmdkMetaData; + +typedef struct VmdkGrainMarker { + uint64_t lba; + uint32_t size; + uint8_t data[0]; +} QEMU_PACKED VmdkGrainMarker; + +enum { + MARKER_END_OF_STREAM = 0, + MARKER_GRAIN_TABLE = 1, + MARKER_GRAIN_DIRECTORY = 2, + MARKER_FOOTER = 3, +}; + +static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + uint32_t magic; + + if (buf_size < 4) { + return 0; + } + magic = be32_to_cpu(*(uint32_t *)buf); + if (magic == VMDK3_MAGIC || + magic == VMDK4_MAGIC) { + return 100; + } else { + const char *p = (const char *)buf; + const char *end = p + buf_size; + while (p < end) { + if (*p == '#') { + /* skip comment line */ + while (p < end && *p != '\n') { + p++; + } + p++; + continue; + } + if (*p == ' ') { + while (p < end && *p == ' ') { + p++; + } + /* skip '\r' if windows line endings used. */ + if (p < end && *p == '\r') { + p++; + } + /* only accept blank lines before 'version=' line */ + if (p == end || *p != '\n') { + return 0; + } + p++; + continue; + } + if (end - p >= strlen("version=X\n")) { + if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 || + strncmp("version=2\n", p, strlen("version=2\n")) == 0) { + return 100; + } + } + if (end - p >= strlen("version=X\r\n")) { + if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 || + strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) { + return 100; + } + } + return 0; + } + return 0; + } +} + +#define SECTOR_SIZE 512 +#define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */ +#define BUF_SIZE 4096 +#define HEADER_SIZE 512 /* first sector of 512 bytes */ + +static void vmdk_free_extents(BlockDriverState *bs) +{ + int i; + BDRVVmdkState *s = bs->opaque; + VmdkExtent *e; + + for (i = 0; i < s->num_extents; i++) { + e = &s->extents[i]; + g_free(e->l1_table); + g_free(e->l2_cache); + g_free(e->l1_backup_table); + g_free(e->type); + if (e->file != bs->file) { + bdrv_unref(e->file); + } + } + g_free(s->extents); +} + +static void vmdk_free_last_extent(BlockDriverState *bs) +{ + BDRVVmdkState *s = bs->opaque; + + if (s->num_extents == 0) { + return; + } + s->num_extents--; + s->extents = g_renew(VmdkExtent, s->extents, s->num_extents); +} + +static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) +{ + char desc[DESC_SIZE]; + uint32_t cid = 0xffffffff; + const char *p_name, *cid_str; + size_t cid_str_size; + BDRVVmdkState *s = bs->opaque; + int ret; + + ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + if (ret < 0) { + return 0; + } + + if (parent) { + cid_str = "parentCID"; + cid_str_size = sizeof("parentCID"); + } else { + cid_str = "CID"; + cid_str_size = sizeof("CID"); + } + + desc[DESC_SIZE - 1] = '\0'; + p_name = strstr(desc, cid_str); + if (p_name != NULL) { + p_name += cid_str_size; + sscanf(p_name, "%" SCNx32, &cid); + } + + return cid; +} + +static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) +{ + char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; + char *p_name, *tmp_str; + BDRVVmdkState *s = bs->opaque; + int ret; + + ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + if (ret < 0) { + return ret; + } + + desc[DESC_SIZE - 1] = '\0'; + tmp_str = strstr(desc, "parentCID"); + if (tmp_str == NULL) { + return -EINVAL; + } + + pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str); + p_name = strstr(desc, "CID"); + if (p_name != NULL) { + p_name += sizeof("CID"); + snprintf(p_name, sizeof(desc) - (p_name - desc), "%" PRIx32 "\n", cid); + pstrcat(desc, sizeof(desc), tmp_desc); + } + + ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int vmdk_is_cid_valid(BlockDriverState *bs) +{ + BDRVVmdkState *s = bs->opaque; + BlockDriverState *p_bs = bs->backing_hd; + uint32_t cur_pcid; + + if (!s->cid_checked && p_bs) { + cur_pcid = vmdk_read_cid(p_bs, 0); + if (s->parent_cid != cur_pcid) { + /* CID not valid */ + return 0; + } + } + s->cid_checked = true; + /* CID valid */ + return 1; +} + +/* Queue extents, if any, for reopen() */ +static int vmdk_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + BDRVVmdkState *s; + int ret = -1; + int i; + VmdkExtent *e; + + assert(state != NULL); + assert(state->bs != NULL); + + if (queue == NULL) { + error_setg(errp, "No reopen queue for VMDK extents"); + goto exit; + } + + s = state->bs->opaque; + + assert(s != NULL); + + for (i = 0; i < s->num_extents; i++) { + e = &s->extents[i]; + if (e->file != state->bs->file) { + bdrv_reopen_queue(queue, e->file, state->flags); + } + } + ret = 0; + +exit: + return ret; +} + +static int vmdk_parent_open(BlockDriverState *bs) +{ + char *p_name; + char desc[DESC_SIZE + 1]; + BDRVVmdkState *s = bs->opaque; + int ret; + + desc[DESC_SIZE] = '\0'; + ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + if (ret < 0) { + return ret; + } + + p_name = strstr(desc, "parentFileNameHint"); + if (p_name != NULL) { + char *end_name; + + p_name += sizeof("parentFileNameHint") + 1; + end_name = strchr(p_name, '\"'); + if (end_name == NULL) { + return -EINVAL; + } + if ((end_name - p_name) > sizeof(bs->backing_file) - 1) { + return -EINVAL; + } + + pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); + } + + return 0; +} + +/* Create and append extent to the extent array. Return the added VmdkExtent + * address. return NULL if allocation failed. */ +static int vmdk_add_extent(BlockDriverState *bs, + BlockDriverState *file, bool flat, int64_t sectors, + int64_t l1_offset, int64_t l1_backup_offset, + uint32_t l1_size, + int l2_size, uint64_t cluster_sectors, + VmdkExtent **new_extent, + Error **errp) +{ + VmdkExtent *extent; + BDRVVmdkState *s = bs->opaque; + int64_t nb_sectors; + + if (cluster_sectors > 0x200000) { + /* 0x200000 * 512Bytes = 1GB for one cluster is unrealistic */ + error_setg(errp, "Invalid granularity, image may be corrupt"); + return -EFBIG; + } + if (l1_size > 512 * 1024 * 1024) { + /* Although with big capacity and small l1_entry_sectors, we can get a + * big l1_size, we don't want unbounded value to allocate the table. + * Limit it to 512M, which is 16PB for default cluster and L2 table + * size */ + error_setg(errp, "L1 size too big"); + return -EFBIG; + } + + nb_sectors = bdrv_nb_sectors(file); + if (nb_sectors < 0) { + return nb_sectors; + } + + s->extents = g_renew(VmdkExtent, s->extents, s->num_extents + 1); + extent = &s->extents[s->num_extents]; + s->num_extents++; + + memset(extent, 0, sizeof(VmdkExtent)); + extent->file = file; + extent->flat = flat; + extent->sectors = sectors; + extent->l1_table_offset = l1_offset; + extent->l1_backup_table_offset = l1_backup_offset; + extent->l1_size = l1_size; + extent->l1_entry_sectors = l2_size * cluster_sectors; + extent->l2_size = l2_size; + extent->cluster_sectors = flat ? sectors : cluster_sectors; + extent->next_cluster_sector = ROUND_UP(nb_sectors, cluster_sectors); + + if (s->num_extents > 1) { + extent->end_sector = (*(extent - 1)).end_sector + extent->sectors; + } else { + extent->end_sector = extent->sectors; + } + bs->total_sectors = extent->end_sector; + if (new_extent) { + *new_extent = extent; + } + return 0; +} + +static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, + Error **errp) +{ + int ret; + size_t l1_size; + int i; + + /* read the L1 table */ + l1_size = extent->l1_size * sizeof(uint32_t); + extent->l1_table = g_try_malloc(l1_size); + if (l1_size && extent->l1_table == NULL) { + return -ENOMEM; + } + + ret = bdrv_pread(extent->file, + extent->l1_table_offset, + extent->l1_table, + l1_size); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Could not read l1 table from extent '%s'", + extent->file->filename); + goto fail_l1; + } + for (i = 0; i < extent->l1_size; i++) { + le32_to_cpus(&extent->l1_table[i]); + } + + if (extent->l1_backup_table_offset) { + extent->l1_backup_table = g_try_malloc(l1_size); + if (l1_size && extent->l1_backup_table == NULL) { + ret = -ENOMEM; + goto fail_l1; + } + ret = bdrv_pread(extent->file, + extent->l1_backup_table_offset, + extent->l1_backup_table, + l1_size); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Could not read l1 backup table from extent '%s'", + extent->file->filename); + goto fail_l1b; + } + for (i = 0; i < extent->l1_size; i++) { + le32_to_cpus(&extent->l1_backup_table[i]); + } + } + + extent->l2_cache = + g_new(uint32_t, extent->l2_size * L2_CACHE_SIZE); + return 0; + fail_l1b: + g_free(extent->l1_backup_table); + fail_l1: + g_free(extent->l1_table); + return ret; +} + +static int vmdk_open_vmfs_sparse(BlockDriverState *bs, + BlockDriverState *file, + int flags, Error **errp) +{ + int ret; + uint32_t magic; + VMDK3Header header; + VmdkExtent *extent; + + ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Could not read header from file '%s'", + file->filename); + return ret; + } + ret = vmdk_add_extent(bs, file, false, + le32_to_cpu(header.disk_sectors), + (int64_t)le32_to_cpu(header.l1dir_offset) << 9, + 0, + le32_to_cpu(header.l1dir_size), + 4096, + le32_to_cpu(header.granularity), + &extent, + errp); + if (ret < 0) { + return ret; + } + ret = vmdk_init_tables(bs, extent, errp); + if (ret) { + /* free extent allocated by vmdk_add_extent */ + vmdk_free_last_extent(bs); + } + return ret; +} + +static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, + Error **errp); + +static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset, + Error **errp) +{ + int64_t size; + char *buf; + int ret; + + size = bdrv_getlength(file); + if (size < 0) { + error_setg_errno(errp, -size, "Could not access file"); + return NULL; + } + + if (size < 4) { + /* Both descriptor file and sparse image must be much larger than 4 + * bytes, also callers of vmdk_read_desc want to compare the first 4 + * bytes with VMDK4_MAGIC, let's error out if less is read. */ + error_setg(errp, "File is too small, not a valid image"); + return NULL; + } + + size = MIN(size, (1 << 20) - 1); /* avoid unbounded allocation */ + buf = g_malloc(size + 1); + + ret = bdrv_pread(file, desc_offset, buf, size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read from file"); + g_free(buf); + return NULL; + } + buf[ret] = 0; + + return buf; +} + +static int vmdk_open_vmdk4(BlockDriverState *bs, + BlockDriverState *file, + int flags, Error **errp) +{ + int ret; + uint32_t magic; + uint32_t l1_size, l1_entry_sectors; + VMDK4Header header; + VmdkExtent *extent; + BDRVVmdkState *s = bs->opaque; + int64_t l1_backup_offset = 0; + + ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Could not read header from file '%s'", + file->filename); + return -EINVAL; + } + if (header.capacity == 0) { + uint64_t desc_offset = le64_to_cpu(header.desc_offset); + if (desc_offset) { + char *buf = vmdk_read_desc(file, desc_offset << 9, errp); + if (!buf) { + return -EINVAL; + } + ret = vmdk_open_desc_file(bs, flags, buf, errp); + g_free(buf); + return ret; + } + } + + if (!s->create_type) { + s->create_type = g_strdup("monolithicSparse"); + } + + if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) { + /* + * The footer takes precedence over the header, so read it in. The + * footer starts at offset -1024 from the end: One sector for the + * footer, and another one for the end-of-stream marker. + */ + struct { + struct { + uint64_t val; + uint32_t size; + uint32_t type; + uint8_t pad[512 - 16]; + } QEMU_PACKED footer_marker; + + uint32_t magic; + VMDK4Header header; + uint8_t pad[512 - 4 - sizeof(VMDK4Header)]; + + struct { + uint64_t val; + uint32_t size; + uint32_t type; + uint8_t pad[512 - 16]; + } QEMU_PACKED eos_marker; + } QEMU_PACKED footer; + + ret = bdrv_pread(file, + bs->file->total_sectors * 512 - 1536, + &footer, sizeof(footer)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to read footer"); + return ret; + } + + /* Some sanity checks for the footer */ + if (be32_to_cpu(footer.magic) != VMDK4_MAGIC || + le32_to_cpu(footer.footer_marker.size) != 0 || + le32_to_cpu(footer.footer_marker.type) != MARKER_FOOTER || + le64_to_cpu(footer.eos_marker.val) != 0 || + le32_to_cpu(footer.eos_marker.size) != 0 || + le32_to_cpu(footer.eos_marker.type) != MARKER_END_OF_STREAM) + { + error_setg(errp, "Invalid footer"); + return -EINVAL; + } + + header = footer.header; + } + + if (le32_to_cpu(header.version) > 3) { + char buf[64]; + snprintf(buf, sizeof(buf), "VMDK version %" PRId32, + le32_to_cpu(header.version)); + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bdrv_get_device_or_node_name(bs), "vmdk", buf); + return -ENOTSUP; + } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) { + /* VMware KB 2064959 explains that version 3 added support for + * persistent changed block tracking (CBT), and backup software can + * read it as version=1 if it doesn't care about the changed area + * information. So we are safe to enable read only. */ + error_setg(errp, "VMDK version 3 must be read only"); + return -EINVAL; + } + + if (le32_to_cpu(header.num_gtes_per_gt) > 512) { + error_setg(errp, "L2 table size too big"); + return -EINVAL; + } + + l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gt) + * le64_to_cpu(header.granularity); + if (l1_entry_sectors == 0) { + error_setg(errp, "L1 entry size is invalid"); + return -EINVAL; + } + l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) + / l1_entry_sectors; + if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) { + l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9; + } + if (bdrv_nb_sectors(file) < le64_to_cpu(header.grain_offset)) { + error_setg(errp, "File truncated, expecting at least %" PRId64 " bytes", + (int64_t)(le64_to_cpu(header.grain_offset) + * BDRV_SECTOR_SIZE)); + return -EINVAL; + } + + ret = vmdk_add_extent(bs, file, false, + le64_to_cpu(header.capacity), + le64_to_cpu(header.gd_offset) << 9, + l1_backup_offset, + l1_size, + le32_to_cpu(header.num_gtes_per_gt), + le64_to_cpu(header.granularity), + &extent, + errp); + if (ret < 0) { + return ret; + } + extent->compressed = + le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE; + if (extent->compressed) { + g_free(s->create_type); + s->create_type = g_strdup("streamOptimized"); + } + extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER; + extent->version = le32_to_cpu(header.version); + extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN; + ret = vmdk_init_tables(bs, extent, errp); + if (ret) { + /* free extent allocated by vmdk_add_extent */ + vmdk_free_last_extent(bs); + } + return ret; +} + +/* find an option value out of descriptor file */ +static int vmdk_parse_description(const char *desc, const char *opt_name, + char *buf, int buf_size) +{ + char *opt_pos, *opt_end; + const char *end = desc + strlen(desc); + + opt_pos = strstr(desc, opt_name); + if (!opt_pos) { + return VMDK_ERROR; + } + /* Skip "=\"" following opt_name */ + opt_pos += strlen(opt_name) + 2; + if (opt_pos >= end) { + return VMDK_ERROR; + } + opt_end = opt_pos; + while (opt_end < end && *opt_end != '"') { + opt_end++; + } + if (opt_end == end || buf_size < opt_end - opt_pos + 1) { + return VMDK_ERROR; + } + pstrcpy(buf, opt_end - opt_pos + 1, opt_pos); + return VMDK_OK; +} + +/* Open an extent file and append to bs array */ +static int vmdk_open_sparse(BlockDriverState *bs, + BlockDriverState *file, int flags, + char *buf, Error **errp) +{ + uint32_t magic; + + magic = ldl_be_p(buf); + switch (magic) { + case VMDK3_MAGIC: + return vmdk_open_vmfs_sparse(bs, file, flags, errp); + break; + case VMDK4_MAGIC: + return vmdk_open_vmdk4(bs, file, flags, errp); + break; + default: + error_setg(errp, "Image not in VMDK format"); + return -EINVAL; + break; + } +} + +static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, + const char *desc_file_path, Error **errp) +{ + int ret; + int matches; + char access[11]; + char type[11]; + char fname[512]; + const char *p = desc; + int64_t sectors = 0; + int64_t flat_offset; + char *extent_path; + BlockDriverState *extent_file; + BDRVVmdkState *s = bs->opaque; + VmdkExtent *extent; + + while (*p) { + /* parse extent line in one of below formats: + * + * RW [size in sectors] FLAT "file-name.vmdk" OFFSET + * RW [size in sectors] SPARSE "file-name.vmdk" + * RW [size in sectors] VMFS "file-name.vmdk" + * RW [size in sectors] VMFSSPARSE "file-name.vmdk" + */ + flat_offset = -1; + matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64, + access, §ors, type, fname, &flat_offset); + if (matches < 4 || strcmp(access, "RW")) { + goto next_line; + } else if (!strcmp(type, "FLAT")) { + if (matches != 5 || flat_offset < 0) { + error_setg(errp, "Invalid extent lines: \n%s", p); + return -EINVAL; + } + } else if (!strcmp(type, "VMFS")) { + if (matches == 4) { + flat_offset = 0; + } else { + error_setg(errp, "Invalid extent lines:\n%s", p); + return -EINVAL; + } + } else if (matches != 4) { + error_setg(errp, "Invalid extent lines:\n%s", p); + return -EINVAL; + } + + if (sectors <= 0 || + (strcmp(type, "FLAT") && strcmp(type, "SPARSE") && + strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE")) || + (strcmp(access, "RW"))) { + goto next_line; + } + + if (!path_is_absolute(fname) && !path_has_protocol(fname) && + !desc_file_path[0]) + { + error_setg(errp, "Cannot use relative extent paths with VMDK " + "descriptor file '%s'", bs->file->filename); + return -EINVAL; + } + + extent_path = g_malloc0(PATH_MAX); + path_combine(extent_path, PATH_MAX, desc_file_path, fname); + extent_file = NULL; + ret = bdrv_open(&extent_file, extent_path, NULL, NULL, + bs->open_flags | BDRV_O_PROTOCOL, NULL, errp); + g_free(extent_path); + if (ret) { + return ret; + } + + /* save to extents array */ + if (!strcmp(type, "FLAT") || !strcmp(type, "VMFS")) { + /* FLAT extent */ + + ret = vmdk_add_extent(bs, extent_file, true, sectors, + 0, 0, 0, 0, 0, &extent, errp); + if (ret < 0) { + bdrv_unref(extent_file); + return ret; + } + extent->flat_start_offset = flat_offset << 9; + } else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) { + /* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/ + char *buf = vmdk_read_desc(extent_file, 0, errp); + if (!buf) { + ret = -EINVAL; + } else { + ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp); + } + g_free(buf); + if (ret) { + bdrv_unref(extent_file); + return ret; + } + extent = &s->extents[s->num_extents - 1]; + } else { + error_setg(errp, "Unsupported extent type '%s'", type); + bdrv_unref(extent_file); + return -ENOTSUP; + } + extent->type = g_strdup(type); +next_line: + /* move to next line */ + while (*p) { + if (*p == '\n') { + p++; + break; + } + p++; + } + } + return 0; +} + +static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, + Error **errp) +{ + int ret; + char ct[128]; + BDRVVmdkState *s = bs->opaque; + + if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) { + error_setg(errp, "invalid VMDK image descriptor"); + ret = -EINVAL; + goto exit; + } + if (strcmp(ct, "monolithicFlat") && + strcmp(ct, "vmfs") && + strcmp(ct, "vmfsSparse") && + strcmp(ct, "twoGbMaxExtentSparse") && + strcmp(ct, "twoGbMaxExtentFlat")) { + error_setg(errp, "Unsupported image type '%s'", ct); + ret = -ENOTSUP; + goto exit; + } + s->create_type = g_strdup(ct); + s->desc_offset = 0; + ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, errp); +exit: + return ret; +} + +static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + char *buf; + int ret; + BDRVVmdkState *s = bs->opaque; + uint32_t magic; + + buf = vmdk_read_desc(bs->file, 0, errp); + if (!buf) { + return -EINVAL; + } + + magic = ldl_be_p(buf); + switch (magic) { + case VMDK3_MAGIC: + case VMDK4_MAGIC: + ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp); + s->desc_offset = 0x200; + break; + default: + ret = vmdk_open_desc_file(bs, flags, buf, errp); + break; + } + if (ret) { + goto fail; + } + + /* try to open parent images, if exist */ + ret = vmdk_parent_open(bs); + if (ret) { + goto fail; + } + s->cid = vmdk_read_cid(bs, 0); + s->parent_cid = vmdk_read_cid(bs, 1); + qemu_co_mutex_init(&s->lock); + + /* Disable migration when VMDK images are used */ + error_setg(&s->migration_blocker, "The vmdk format used by node '%s' " + "does not support live migration", + bdrv_get_device_or_node_name(bs)); + migrate_add_blocker(s->migration_blocker); + g_free(buf); + return 0; + +fail: + g_free(buf); + g_free(s->create_type); + s->create_type = NULL; + vmdk_free_extents(bs); + return ret; +} + + +static void vmdk_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BDRVVmdkState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_extents; i++) { + if (!s->extents[i].flat) { + bs->bl.write_zeroes_alignment = + MAX(bs->bl.write_zeroes_alignment, + s->extents[i].cluster_sectors); + } + } +} + +/** + * get_whole_cluster + * + * Copy backing file's cluster that covers @sector_num, otherwise write zero, + * to the cluster at @cluster_sector_num. + * + * If @skip_start_sector < @skip_end_sector, the relative range + * [@skip_start_sector, @skip_end_sector) is not copied or written, and leave + * it for call to write user data in the request. + */ +static int get_whole_cluster(BlockDriverState *bs, + VmdkExtent *extent, + uint64_t cluster_sector_num, + uint64_t sector_num, + uint64_t skip_start_sector, + uint64_t skip_end_sector) +{ + int ret = VMDK_OK; + int64_t cluster_bytes; + uint8_t *whole_grain; + + /* For COW, align request sector_num to cluster start */ + sector_num = QEMU_ALIGN_DOWN(sector_num, extent->cluster_sectors); + cluster_bytes = extent->cluster_sectors << BDRV_SECTOR_BITS; + whole_grain = qemu_blockalign(bs, cluster_bytes); + + if (!bs->backing_hd) { + memset(whole_grain, 0, skip_start_sector << BDRV_SECTOR_BITS); + memset(whole_grain + (skip_end_sector << BDRV_SECTOR_BITS), 0, + cluster_bytes - (skip_end_sector << BDRV_SECTOR_BITS)); + } + + assert(skip_end_sector <= extent->cluster_sectors); + /* we will be here if it's first write on non-exist grain(cluster). + * try to read from parent image, if exist */ + if (bs->backing_hd && !vmdk_is_cid_valid(bs)) { + ret = VMDK_ERROR; + goto exit; + } + + /* Read backing data before skip range */ + if (skip_start_sector > 0) { + if (bs->backing_hd) { + ret = bdrv_read(bs->backing_hd, sector_num, + whole_grain, skip_start_sector); + if (ret < 0) { + ret = VMDK_ERROR; + goto exit; + } + } + ret = bdrv_write(extent->file, cluster_sector_num, whole_grain, + skip_start_sector); + if (ret < 0) { + ret = VMDK_ERROR; + goto exit; + } + } + /* Read backing data after skip range */ + if (skip_end_sector < extent->cluster_sectors) { + if (bs->backing_hd) { + ret = bdrv_read(bs->backing_hd, sector_num + skip_end_sector, + whole_grain + (skip_end_sector << BDRV_SECTOR_BITS), + extent->cluster_sectors - skip_end_sector); + if (ret < 0) { + ret = VMDK_ERROR; + goto exit; + } + } + ret = bdrv_write(extent->file, cluster_sector_num + skip_end_sector, + whole_grain + (skip_end_sector << BDRV_SECTOR_BITS), + extent->cluster_sectors - skip_end_sector); + if (ret < 0) { + ret = VMDK_ERROR; + goto exit; + } + } + +exit: + qemu_vfree(whole_grain); + return ret; +} + +static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, + uint32_t offset) +{ + offset = cpu_to_le32(offset); + /* update L2 table */ + if (bdrv_pwrite_sync( + extent->file, + ((int64_t)m_data->l2_offset * 512) + + (m_data->l2_index * sizeof(offset)), + &offset, sizeof(offset)) < 0) { + return VMDK_ERROR; + } + /* update backup L2 table */ + if (extent->l1_backup_table_offset != 0) { + m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; + if (bdrv_pwrite_sync( + extent->file, + ((int64_t)m_data->l2_offset * 512) + + (m_data->l2_index * sizeof(offset)), + &offset, sizeof(offset)) < 0) { + return VMDK_ERROR; + } + } + if (m_data->l2_cache_entry) { + *m_data->l2_cache_entry = offset; + } + + return VMDK_OK; +} + +/** + * get_cluster_offset + * + * Look up cluster offset in extent file by sector number, and store in + * @cluster_offset. + * + * For flat extents, the start offset as parsed from the description file is + * returned. + * + * For sparse extents, look up in L1, L2 table. If allocate is true, return an + * offset for a new cluster and update L2 cache. If there is a backing file, + * COW is done before returning; otherwise, zeroes are written to the allocated + * cluster. Both COW and zero writing skips the sector range + * [@skip_start_sector, @skip_end_sector) passed in by caller, because caller + * has new data to write there. + * + * Returns: VMDK_OK if cluster exists and mapped in the image. + * VMDK_UNALLOC if cluster is not mapped and @allocate is false. + * VMDK_ERROR if failed. + */ +static int get_cluster_offset(BlockDriverState *bs, + VmdkExtent *extent, + VmdkMetaData *m_data, + uint64_t offset, + bool allocate, + uint64_t *cluster_offset, + uint64_t skip_start_sector, + uint64_t skip_end_sector) +{ + unsigned int l1_index, l2_offset, l2_index; + int min_index, i, j; + uint32_t min_count, *l2_table; + bool zeroed = false; + int64_t ret; + int64_t cluster_sector; + + if (m_data) { + m_data->valid = 0; + } + if (extent->flat) { + *cluster_offset = extent->flat_start_offset; + return VMDK_OK; + } + + offset -= (extent->end_sector - extent->sectors) * SECTOR_SIZE; + l1_index = (offset >> 9) / extent->l1_entry_sectors; + if (l1_index >= extent->l1_size) { + return VMDK_ERROR; + } + l2_offset = extent->l1_table[l1_index]; + if (!l2_offset) { + return VMDK_UNALLOC; + } + for (i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == extent->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++extent->l2_cache_counts[i] == 0xffffffff) { + for (j = 0; j < L2_CACHE_SIZE; j++) { + extent->l2_cache_counts[j] >>= 1; + } + } + l2_table = extent->l2_cache + (i * extent->l2_size); + goto found; + } + } + /* not found: load a new entry in the least used one */ + min_index = 0; + min_count = 0xffffffff; + for (i = 0; i < L2_CACHE_SIZE; i++) { + if (extent->l2_cache_counts[i] < min_count) { + min_count = extent->l2_cache_counts[i]; + min_index = i; + } + } + l2_table = extent->l2_cache + (min_index * extent->l2_size); + if (bdrv_pread( + extent->file, + (int64_t)l2_offset * 512, + l2_table, + extent->l2_size * sizeof(uint32_t) + ) != extent->l2_size * sizeof(uint32_t)) { + return VMDK_ERROR; + } + + extent->l2_cache_offsets[min_index] = l2_offset; + extent->l2_cache_counts[min_index] = 1; + found: + l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; + cluster_sector = le32_to_cpu(l2_table[l2_index]); + + if (m_data) { + m_data->valid = 1; + m_data->l1_index = l1_index; + m_data->l2_index = l2_index; + m_data->l2_offset = l2_offset; + m_data->l2_cache_entry = &l2_table[l2_index]; + } + if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) { + zeroed = true; + } + + if (!cluster_sector || zeroed) { + if (!allocate) { + return zeroed ? VMDK_ZEROED : VMDK_UNALLOC; + } + + cluster_sector = extent->next_cluster_sector; + extent->next_cluster_sector += extent->cluster_sectors; + + /* First of all we write grain itself, to avoid race condition + * that may to corrupt the image. + * This problem may occur because of insufficient space on host disk + * or inappropriate VM shutdown. + */ + ret = get_whole_cluster(bs, extent, + cluster_sector, + offset >> BDRV_SECTOR_BITS, + skip_start_sector, skip_end_sector); + if (ret) { + return ret; + } + } + *cluster_offset = cluster_sector << BDRV_SECTOR_BITS; + return VMDK_OK; +} + +static VmdkExtent *find_extent(BDRVVmdkState *s, + int64_t sector_num, VmdkExtent *start_hint) +{ + VmdkExtent *extent = start_hint; + + if (!extent) { + extent = &s->extents[0]; + } + while (extent < &s->extents[s->num_extents]) { + if (sector_num < extent->end_sector) { + return extent; + } + extent++; + } + return NULL; +} + +static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + BDRVVmdkState *s = bs->opaque; + int64_t index_in_cluster, n, ret; + uint64_t offset; + VmdkExtent *extent; + + extent = find_extent(s, sector_num, NULL); + if (!extent) { + return 0; + } + qemu_co_mutex_lock(&s->lock); + ret = get_cluster_offset(bs, extent, NULL, + sector_num * 512, false, &offset, + 0, 0); + qemu_co_mutex_unlock(&s->lock); + + switch (ret) { + case VMDK_ERROR: + ret = -EIO; + break; + case VMDK_UNALLOC: + ret = 0; + break; + case VMDK_ZEROED: + ret = BDRV_BLOCK_ZERO; + break; + case VMDK_OK: + ret = BDRV_BLOCK_DATA; + if (extent->file == bs->file && !extent->compressed) { + ret |= BDRV_BLOCK_OFFSET_VALID | offset; + } + + break; + } + + index_in_cluster = sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { + n = nb_sectors; + } + *pnum = n; + return ret; +} + +static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, + int64_t offset_in_cluster, const uint8_t *buf, + int nb_sectors, int64_t sector_num) +{ + int ret; + VmdkGrainMarker *data = NULL; + uLongf buf_len; + const uint8_t *write_buf = buf; + int write_len = nb_sectors * 512; + int64_t write_offset; + int64_t write_end_sector; + + if (extent->compressed) { + if (!extent->has_marker) { + ret = -EINVAL; + goto out; + } + buf_len = (extent->cluster_sectors << 9) * 2; + data = g_malloc(buf_len + sizeof(VmdkGrainMarker)); + if (compress(data->data, &buf_len, buf, nb_sectors << 9) != Z_OK || + buf_len == 0) { + ret = -EINVAL; + goto out; + } + data->lba = sector_num; + data->size = buf_len; + write_buf = (uint8_t *)data; + write_len = buf_len + sizeof(VmdkGrainMarker); + } + write_offset = cluster_offset + offset_in_cluster, + ret = bdrv_pwrite(extent->file, write_offset, write_buf, write_len); + + write_end_sector = DIV_ROUND_UP(write_offset + write_len, BDRV_SECTOR_SIZE); + + extent->next_cluster_sector = MAX(extent->next_cluster_sector, + write_end_sector); + + if (ret != write_len) { + ret = ret < 0 ? ret : -EIO; + goto out; + } + ret = 0; + out: + g_free(data); + return ret; +} + +static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, + int64_t offset_in_cluster, uint8_t *buf, + int nb_sectors) +{ + int ret; + int cluster_bytes, buf_bytes; + uint8_t *cluster_buf, *compressed_data; + uint8_t *uncomp_buf; + uint32_t data_len; + VmdkGrainMarker *marker; + uLongf buf_len; + + + if (!extent->compressed) { + ret = bdrv_pread(extent->file, + cluster_offset + offset_in_cluster, + buf, nb_sectors * 512); + if (ret == nb_sectors * 512) { + return 0; + } else { + return -EIO; + } + } + cluster_bytes = extent->cluster_sectors * 512; + /* Read two clusters in case GrainMarker + compressed data > one cluster */ + buf_bytes = cluster_bytes * 2; + cluster_buf = g_malloc(buf_bytes); + uncomp_buf = g_malloc(cluster_bytes); + ret = bdrv_pread(extent->file, + cluster_offset, + cluster_buf, buf_bytes); + if (ret < 0) { + goto out; + } + compressed_data = cluster_buf; + buf_len = cluster_bytes; + data_len = cluster_bytes; + if (extent->has_marker) { + marker = (VmdkGrainMarker *)cluster_buf; + compressed_data = marker->data; + data_len = le32_to_cpu(marker->size); + } + if (!data_len || data_len > buf_bytes) { + ret = -EINVAL; + goto out; + } + ret = uncompress(uncomp_buf, &buf_len, compressed_data, data_len); + if (ret != Z_OK) { + ret = -EINVAL; + goto out; + + } + if (offset_in_cluster < 0 || + offset_in_cluster + nb_sectors * 512 > buf_len) { + ret = -EINVAL; + goto out; + } + memcpy(buf, uncomp_buf + offset_in_cluster, nb_sectors * 512); + ret = 0; + + out: + g_free(uncomp_buf); + g_free(cluster_buf); + return ret; +} + +static int vmdk_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVVmdkState *s = bs->opaque; + int ret; + uint64_t n, index_in_cluster; + uint64_t extent_begin_sector, extent_relative_sector_num; + VmdkExtent *extent = NULL; + uint64_t cluster_offset; + + while (nb_sectors > 0) { + extent = find_extent(s, sector_num, extent); + if (!extent) { + return -EIO; + } + ret = get_cluster_offset(bs, extent, NULL, + sector_num << 9, false, &cluster_offset, + 0, 0); + extent_begin_sector = extent->end_sector - extent->sectors; + extent_relative_sector_num = sector_num - extent_begin_sector; + index_in_cluster = extent_relative_sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { + n = nb_sectors; + } + if (ret != VMDK_OK) { + /* if not allocated, try to read from parent image, if exist */ + if (bs->backing_hd && ret != VMDK_ZEROED) { + if (!vmdk_is_cid_valid(bs)) { + return -EINVAL; + } + ret = bdrv_read(bs->backing_hd, sector_num, buf, n); + if (ret < 0) { + return ret; + } + } else { + memset(buf, 0, 512 * n); + } + } else { + ret = vmdk_read_extent(extent, + cluster_offset, index_in_cluster * 512, + buf, n); + if (ret) { + return ret; + } + } + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + return 0; +} + +static coroutine_fn int vmdk_co_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVVmdkState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = vmdk_read(bs, sector_num, buf, nb_sectors); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +/** + * vmdk_write: + * @zeroed: buf is ignored (data is zero), use zeroed_grain GTE feature + * if possible, otherwise return -ENOTSUP. + * @zero_dry_run: used for zeroed == true only, don't update L2 table, just try + * with each cluster. By dry run we can find if the zero write + * is possible without modifying image data. + * + * Returns: error code with 0 for success. + */ +static int vmdk_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors, + bool zeroed, bool zero_dry_run) +{ + BDRVVmdkState *s = bs->opaque; + VmdkExtent *extent = NULL; + int ret; + int64_t index_in_cluster, n; + uint64_t extent_begin_sector, extent_relative_sector_num; + uint64_t cluster_offset; + VmdkMetaData m_data; + + if (sector_num > bs->total_sectors) { + error_report("Wrong offset: sector_num=0x%" PRIx64 + " total_sectors=0x%" PRIx64 "\n", + sector_num, bs->total_sectors); + return -EIO; + } + + while (nb_sectors > 0) { + extent = find_extent(s, sector_num, extent); + if (!extent) { + return -EIO; + } + extent_begin_sector = extent->end_sector - extent->sectors; + extent_relative_sector_num = sector_num - extent_begin_sector; + index_in_cluster = extent_relative_sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { + n = nb_sectors; + } + ret = get_cluster_offset(bs, extent, &m_data, sector_num << 9, + !(extent->compressed || zeroed), + &cluster_offset, + index_in_cluster, index_in_cluster + n); + if (extent->compressed) { + if (ret == VMDK_OK) { + /* Refuse write to allocated cluster for streamOptimized */ + error_report("Could not write to allocated cluster" + " for streamOptimized"); + return -EIO; + } else { + /* allocate */ + ret = get_cluster_offset(bs, extent, &m_data, sector_num << 9, + true, &cluster_offset, 0, 0); + } + } + if (ret == VMDK_ERROR) { + return -EINVAL; + } + if (zeroed) { + /* Do zeroed write, buf is ignored */ + if (extent->has_zero_grain && + index_in_cluster == 0 && + n >= extent->cluster_sectors) { + n = extent->cluster_sectors; + if (!zero_dry_run) { + /* update L2 tables */ + if (vmdk_L2update(extent, &m_data, VMDK_GTE_ZEROED) + != VMDK_OK) { + return -EIO; + } + } + } else { + return -ENOTSUP; + } + } else { + ret = vmdk_write_extent(extent, + cluster_offset, index_in_cluster * 512, + buf, n, sector_num); + if (ret) { + return ret; + } + if (m_data.valid) { + /* update L2 tables */ + if (vmdk_L2update(extent, &m_data, + cluster_offset >> BDRV_SECTOR_BITS) + != VMDK_OK) { + return -EIO; + } + } + } + nb_sectors -= n; + sector_num += n; + buf += n * 512; + + /* update CID on the first write every time the virtual disk is + * opened */ + if (!s->cid_updated) { + ret = vmdk_write_cid(bs, g_random_int()); + if (ret < 0) { + return ret; + } + s->cid_updated = true; + } + } + return 0; +} + +static coroutine_fn int vmdk_co_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVVmdkState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = vmdk_write(bs, sector_num, buf, nb_sectors, false, false); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int vmdk_write_compressed(BlockDriverState *bs, + int64_t sector_num, + const uint8_t *buf, + int nb_sectors) +{ + BDRVVmdkState *s = bs->opaque; + if (s->num_extents == 1 && s->extents[0].compressed) { + return vmdk_write(bs, sector_num, buf, nb_sectors, false, false); + } else { + return -ENOTSUP; + } +} + +static int coroutine_fn vmdk_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, + BdrvRequestFlags flags) +{ + int ret; + BDRVVmdkState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + /* write zeroes could fail if sectors not aligned to cluster, test it with + * dry_run == true before really updating image */ + ret = vmdk_write(bs, sector_num, NULL, nb_sectors, true, true); + if (!ret) { + ret = vmdk_write(bs, sector_num, NULL, nb_sectors, true, false); + } + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int vmdk_create_extent(const char *filename, int64_t filesize, + bool flat, bool compress, bool zeroed_grain, + QemuOpts *opts, Error **errp) +{ + int ret, i; + BlockDriverState *bs = NULL; + VMDK4Header header; + Error *local_err = NULL; + uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count; + uint32_t *gd_buf = NULL; + int gd_buf_size; + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + + assert(bs == NULL); + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + + if (flat) { + ret = bdrv_truncate(bs, filesize); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not truncate file"); + } + goto exit; + } + magic = cpu_to_be32(VMDK4_MAGIC); + memset(&header, 0, sizeof(header)); + header.version = zeroed_grain ? 2 : 1; + header.flags = VMDK4_FLAG_RGD | VMDK4_FLAG_NL_DETECT + | (compress ? VMDK4_FLAG_COMPRESS | VMDK4_FLAG_MARKER : 0) + | (zeroed_grain ? VMDK4_FLAG_ZERO_GRAIN : 0); + header.compressAlgorithm = compress ? VMDK4_COMPRESSION_DEFLATE : 0; + header.capacity = filesize / BDRV_SECTOR_SIZE; + header.granularity = 128; + header.num_gtes_per_gt = BDRV_SECTOR_SIZE; + + grains = DIV_ROUND_UP(filesize / BDRV_SECTOR_SIZE, header.granularity); + gt_size = DIV_ROUND_UP(header.num_gtes_per_gt * sizeof(uint32_t), + BDRV_SECTOR_SIZE); + gt_count = DIV_ROUND_UP(grains, header.num_gtes_per_gt); + gd_sectors = DIV_ROUND_UP(gt_count * sizeof(uint32_t), BDRV_SECTOR_SIZE); + + header.desc_offset = 1; + header.desc_size = 20; + header.rgd_offset = header.desc_offset + header.desc_size; + header.gd_offset = header.rgd_offset + gd_sectors + (gt_size * gt_count); + header.grain_offset = + ROUND_UP(header.gd_offset + gd_sectors + (gt_size * gt_count), + header.granularity); + /* swap endianness for all header fields */ + header.version = cpu_to_le32(header.version); + header.flags = cpu_to_le32(header.flags); + header.capacity = cpu_to_le64(header.capacity); + header.granularity = cpu_to_le64(header.granularity); + header.num_gtes_per_gt = cpu_to_le32(header.num_gtes_per_gt); + header.desc_offset = cpu_to_le64(header.desc_offset); + header.desc_size = cpu_to_le64(header.desc_size); + header.rgd_offset = cpu_to_le64(header.rgd_offset); + header.gd_offset = cpu_to_le64(header.gd_offset); + header.grain_offset = cpu_to_le64(header.grain_offset); + header.compressAlgorithm = cpu_to_le16(header.compressAlgorithm); + + header.check_bytes[0] = 0xa; + header.check_bytes[1] = 0x20; + header.check_bytes[2] = 0xd; + header.check_bytes[3] = 0xa; + + /* write all the data */ + ret = bdrv_pwrite(bs, 0, &magic, sizeof(magic)); + if (ret < 0) { + error_set(errp, QERR_IO_ERROR); + goto exit; + } + ret = bdrv_pwrite(bs, sizeof(magic), &header, sizeof(header)); + if (ret < 0) { + error_set(errp, QERR_IO_ERROR); + goto exit; + } + + ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not truncate file"); + goto exit; + } + + /* write grain directory */ + gd_buf_size = gd_sectors * BDRV_SECTOR_SIZE; + gd_buf = g_malloc0(gd_buf_size); + for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_sectors; + i < gt_count; i++, tmp += gt_size) { + gd_buf[i] = cpu_to_le32(tmp); + } + ret = bdrv_pwrite(bs, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE, + gd_buf, gd_buf_size); + if (ret < 0) { + error_set(errp, QERR_IO_ERROR); + goto exit; + } + + /* write backup grain directory */ + for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_sectors; + i < gt_count; i++, tmp += gt_size) { + gd_buf[i] = cpu_to_le32(tmp); + } + ret = bdrv_pwrite(bs, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE, + gd_buf, gd_buf_size); + if (ret < 0) { + error_set(errp, QERR_IO_ERROR); + goto exit; + } + + ret = 0; +exit: + if (bs) { + bdrv_unref(bs); + } + g_free(gd_buf); + return ret; +} + +static int filename_decompose(const char *filename, char *path, char *prefix, + char *postfix, size_t buf_len, Error **errp) +{ + const char *p, *q; + + if (filename == NULL || !strlen(filename)) { + error_setg(errp, "No filename provided"); + return VMDK_ERROR; + } + p = strrchr(filename, '/'); + if (p == NULL) { + p = strrchr(filename, '\\'); + } + if (p == NULL) { + p = strrchr(filename, ':'); + } + if (p != NULL) { + p++; + if (p - filename >= buf_len) { + return VMDK_ERROR; + } + pstrcpy(path, p - filename + 1, filename); + } else { + p = filename; + path[0] = '\0'; + } + q = strrchr(p, '.'); + if (q == NULL) { + pstrcpy(prefix, buf_len, p); + postfix[0] = '\0'; + } else { + if (q - p >= buf_len) { + return VMDK_ERROR; + } + pstrcpy(prefix, q - p + 1, p); + pstrcpy(postfix, buf_len, q); + } + return VMDK_OK; +} + +static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int idx = 0; + BlockDriverState *new_bs = NULL; + Error *local_err = NULL; + char *desc = NULL; + int64_t total_size = 0, filesize; + char *adapter_type = NULL; + char *backing_file = NULL; + char *fmt = NULL; + int flags = 0; + int ret = 0; + bool flat, split, compress; + GString *ext_desc_lines; + char *path = g_malloc0(PATH_MAX); + char *prefix = g_malloc0(PATH_MAX); + char *postfix = g_malloc0(PATH_MAX); + char *desc_line = g_malloc0(BUF_SIZE); + char *ext_filename = g_malloc0(PATH_MAX); + char *desc_filename = g_malloc0(PATH_MAX); + const int64_t split_size = 0x80000000; /* VMDK has constant split size */ + const char *desc_extent_line; + char *parent_desc_line = g_malloc0(BUF_SIZE); + uint32_t parent_cid = 0xffffffff; + uint32_t number_heads = 16; + bool zeroed_grain = false; + uint32_t desc_offset = 0, desc_len; + const char desc_template[] = + "# Disk DescriptorFile\n" + "version=1\n" + "CID=%" PRIx32 "\n" + "parentCID=%" PRIx32 "\n" + "createType=\"%s\"\n" + "%s" + "\n" + "# Extent description\n" + "%s" + "\n" + "# The Disk Data Base\n" + "#DDB\n" + "\n" + "ddb.virtualHWVersion = \"%d\"\n" + "ddb.geometry.cylinders = \"%" PRId64 "\"\n" + "ddb.geometry.heads = \"%" PRIu32 "\"\n" + "ddb.geometry.sectors = \"63\"\n" + "ddb.adapterType = \"%s\"\n"; + + ext_desc_lines = g_string_new(NULL); + + if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) { + ret = -EINVAL; + goto exit; + } + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE); + backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); + if (qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false)) { + flags |= BLOCK_FLAG_COMPAT6; + } + fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); + if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false)) { + zeroed_grain = true; + } + + if (!adapter_type) { + adapter_type = g_strdup("ide"); + } else if (strcmp(adapter_type, "ide") && + strcmp(adapter_type, "buslogic") && + strcmp(adapter_type, "lsilogic") && + strcmp(adapter_type, "legacyESX")) { + error_setg(errp, "Unknown adapter type: '%s'", adapter_type); + ret = -EINVAL; + goto exit; + } + if (strcmp(adapter_type, "ide") != 0) { + /* that's the number of heads with which vmware operates when + creating, exporting, etc. vmdk files with a non-ide adapter type */ + number_heads = 255; + } + if (!fmt) { + /* Default format to monolithicSparse */ + fmt = g_strdup("monolithicSparse"); + } else if (strcmp(fmt, "monolithicFlat") && + strcmp(fmt, "monolithicSparse") && + strcmp(fmt, "twoGbMaxExtentSparse") && + strcmp(fmt, "twoGbMaxExtentFlat") && + strcmp(fmt, "streamOptimized")) { + error_setg(errp, "Unknown subformat: '%s'", fmt); + ret = -EINVAL; + goto exit; + } + split = !(strcmp(fmt, "twoGbMaxExtentFlat") && + strcmp(fmt, "twoGbMaxExtentSparse")); + flat = !(strcmp(fmt, "monolithicFlat") && + strcmp(fmt, "twoGbMaxExtentFlat")); + compress = !strcmp(fmt, "streamOptimized"); + if (flat) { + desc_extent_line = "RW %" PRId64 " FLAT \"%s\" 0\n"; + } else { + desc_extent_line = "RW %" PRId64 " SPARSE \"%s\"\n"; + } + if (flat && backing_file) { + error_setg(errp, "Flat image can't have backing file"); + ret = -ENOTSUP; + goto exit; + } + if (flat && zeroed_grain) { + error_setg(errp, "Flat image can't enable zeroed grain"); + ret = -ENOTSUP; + goto exit; + } + if (backing_file) { + BlockDriverState *bs = NULL; + char *full_backing = g_new0(char, PATH_MAX); + bdrv_get_full_backing_filename_from_filename(filename, backing_file, + full_backing, PATH_MAX, + &local_err); + if (local_err) { + g_free(full_backing); + error_propagate(errp, local_err); + ret = -ENOENT; + goto exit; + } + ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, NULL, + errp); + g_free(full_backing); + if (ret != 0) { + goto exit; + } + if (strcmp(bs->drv->format_name, "vmdk")) { + bdrv_unref(bs); + ret = -EINVAL; + goto exit; + } + parent_cid = vmdk_read_cid(bs, 0); + bdrv_unref(bs); + snprintf(parent_desc_line, BUF_SIZE, + "parentFileNameHint=\"%s\"", backing_file); + } + + /* Create extents */ + filesize = total_size; + while (filesize > 0) { + int64_t size = filesize; + + if (split && size > split_size) { + size = split_size; + } + if (split) { + snprintf(desc_filename, PATH_MAX, "%s-%c%03d%s", + prefix, flat ? 'f' : 's', ++idx, postfix); + } else if (flat) { + snprintf(desc_filename, PATH_MAX, "%s-flat%s", prefix, postfix); + } else { + snprintf(desc_filename, PATH_MAX, "%s%s", prefix, postfix); + } + snprintf(ext_filename, PATH_MAX, "%s%s", path, desc_filename); + + if (vmdk_create_extent(ext_filename, size, + flat, compress, zeroed_grain, opts, errp)) { + ret = -EINVAL; + goto exit; + } + filesize -= size; + + /* Format description line */ + snprintf(desc_line, BUF_SIZE, + desc_extent_line, size / BDRV_SECTOR_SIZE, desc_filename); + g_string_append(ext_desc_lines, desc_line); + } + /* generate descriptor file */ + desc = g_strdup_printf(desc_template, + g_random_int(), + parent_cid, + fmt, + parent_desc_line, + ext_desc_lines->str, + (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), + total_size / + (int64_t)(63 * number_heads * BDRV_SECTOR_SIZE), + number_heads, + adapter_type); + desc_len = strlen(desc); + /* the descriptor offset = 0x200 */ + if (!split && !flat) { + desc_offset = 0x200; + } else { + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + } + assert(new_bs == NULL); + ret = bdrv_open(&new_bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write description"); + goto exit; + } + /* bdrv_pwrite write padding zeros to align to sector, we don't need that + * for description file */ + if (desc_offset == 0) { + ret = bdrv_truncate(new_bs, desc_len); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not truncate file"); + } + } +exit: + if (new_bs) { + bdrv_unref(new_bs); + } + g_free(adapter_type); + g_free(backing_file); + g_free(fmt); + g_free(desc); + g_free(path); + g_free(prefix); + g_free(postfix); + g_free(desc_line); + g_free(ext_filename); + g_free(desc_filename); + g_free(parent_desc_line); + g_string_free(ext_desc_lines, true); + return ret; +} + +static void vmdk_close(BlockDriverState *bs) +{ + BDRVVmdkState *s = bs->opaque; + + vmdk_free_extents(bs); + g_free(s->create_type); + + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); +} + +static coroutine_fn int vmdk_co_flush(BlockDriverState *bs) +{ + BDRVVmdkState *s = bs->opaque; + int i, err; + int ret = 0; + + for (i = 0; i < s->num_extents; i++) { + err = bdrv_co_flush(s->extents[i].file); + if (err < 0) { + ret = err; + } + } + return ret; +} + +static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) +{ + int i; + int64_t ret = 0; + int64_t r; + BDRVVmdkState *s = bs->opaque; + + ret = bdrv_get_allocated_file_size(bs->file); + if (ret < 0) { + return ret; + } + for (i = 0; i < s->num_extents; i++) { + if (s->extents[i].file == bs->file) { + continue; + } + r = bdrv_get_allocated_file_size(s->extents[i].file); + if (r < 0) { + return r; + } + ret += r; + } + return ret; +} + +static int vmdk_has_zero_init(BlockDriverState *bs) +{ + int i; + BDRVVmdkState *s = bs->opaque; + + /* If has a flat extent and its underlying storage doesn't have zero init, + * return 0. */ + for (i = 0; i < s->num_extents; i++) { + if (s->extents[i].flat) { + if (!bdrv_has_zero_init(s->extents[i].file)) { + return 0; + } + } + } + return 1; +} + +static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent) +{ + ImageInfo *info = g_new0(ImageInfo, 1); + + *info = (ImageInfo){ + .filename = g_strdup(extent->file->filename), + .format = g_strdup(extent->type), + .virtual_size = extent->sectors * BDRV_SECTOR_SIZE, + .compressed = extent->compressed, + .has_compressed = extent->compressed, + .cluster_size = extent->cluster_sectors * BDRV_SECTOR_SIZE, + .has_cluster_size = !extent->flat, + }; + + return info; +} + +static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) +{ + BDRVVmdkState *s = bs->opaque; + VmdkExtent *extent = NULL; + int64_t sector_num = 0; + int64_t total_sectors = bdrv_nb_sectors(bs); + int ret; + uint64_t cluster_offset; + + if (fix) { + return -ENOTSUP; + } + + for (;;) { + if (sector_num >= total_sectors) { + return 0; + } + extent = find_extent(s, sector_num, extent); + if (!extent) { + fprintf(stderr, + "ERROR: could not find extent for sector %" PRId64 "\n", + sector_num); + break; + } + ret = get_cluster_offset(bs, extent, NULL, + sector_num << BDRV_SECTOR_BITS, + false, &cluster_offset, 0, 0); + if (ret == VMDK_ERROR) { + fprintf(stderr, + "ERROR: could not get cluster_offset for sector %" + PRId64 "\n", sector_num); + break; + } + if (ret == VMDK_OK && cluster_offset >= bdrv_getlength(extent->file)) { + fprintf(stderr, + "ERROR: cluster offset for sector %" + PRId64 " points after EOF\n", sector_num); + break; + } + sector_num += extent->cluster_sectors; + } + + result->corruptions++; + return 0; +} + +static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs) +{ + int i; + BDRVVmdkState *s = bs->opaque; + ImageInfoSpecific *spec_info = g_new0(ImageInfoSpecific, 1); + ImageInfoList **next; + + *spec_info = (ImageInfoSpecific){ + .kind = IMAGE_INFO_SPECIFIC_KIND_VMDK, + { + .vmdk = g_new0(ImageInfoSpecificVmdk, 1), + }, + }; + + *spec_info->vmdk = (ImageInfoSpecificVmdk) { + .create_type = g_strdup(s->create_type), + .cid = s->cid, + .parent_cid = s->parent_cid, + }; + + next = &spec_info->vmdk->extents; + for (i = 0; i < s->num_extents; i++) { + *next = g_new0(ImageInfoList, 1); + (*next)->value = vmdk_get_extent_info(&s->extents[i]); + (*next)->next = NULL; + next = &(*next)->next; + } + + return spec_info; +} + +static bool vmdk_extents_type_eq(const VmdkExtent *a, const VmdkExtent *b) +{ + return a->flat == b->flat && + a->compressed == b->compressed && + (a->flat || a->cluster_sectors == b->cluster_sectors); +} + +static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + int i; + BDRVVmdkState *s = bs->opaque; + assert(s->num_extents); + + /* See if we have multiple extents but they have different cases */ + for (i = 1; i < s->num_extents; i++) { + if (!vmdk_extents_type_eq(&s->extents[0], &s->extents[i])) { + return -ENOTSUP; + } + } + bdi->needs_compressed_writes = s->extents[0].compressed; + if (!s->extents[0].flat) { + bdi->cluster_size = s->extents[0].cluster_sectors << BDRV_SECTOR_BITS; + } + return 0; +} + +static void vmdk_detach_aio_context(BlockDriverState *bs) +{ + BDRVVmdkState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_extents; i++) { + bdrv_detach_aio_context(s->extents[i].file); + } +} + +static void vmdk_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVVmdkState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_extents; i++) { + bdrv_attach_aio_context(s->extents[i].file, new_context); + } +} + +static QemuOptsList vmdk_create_opts = { + .name = "vmdk-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(vmdk_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_ADAPTER_TYPE, + .type = QEMU_OPT_STRING, + .help = "Virtual adapter type, can be one of " + "ide (default), lsilogic, buslogic or legacyESX" + }, + { + .name = BLOCK_OPT_BACKING_FILE, + .type = QEMU_OPT_STRING, + .help = "File name of a base image" + }, + { + .name = BLOCK_OPT_COMPAT6, + .type = QEMU_OPT_BOOL, + .help = "VMDK version 6 image", + .def_value_str = "off" + }, + { + .name = BLOCK_OPT_SUBFMT, + .type = QEMU_OPT_STRING, + .help = + "VMDK flat extent format, can be one of " + "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat | streamOptimized} " + }, + { + .name = BLOCK_OPT_ZEROED_GRAIN, + .type = QEMU_OPT_BOOL, + .help = "Enable efficient zero writes " + "using the zeroed-grain GTE feature" + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_vmdk = { + .format_name = "vmdk", + .instance_size = sizeof(BDRVVmdkState), + .bdrv_probe = vmdk_probe, + .bdrv_open = vmdk_open, + .bdrv_check = vmdk_check, + .bdrv_reopen_prepare = vmdk_reopen_prepare, + .bdrv_read = vmdk_co_read, + .bdrv_write = vmdk_co_write, + .bdrv_write_compressed = vmdk_write_compressed, + .bdrv_co_write_zeroes = vmdk_co_write_zeroes, + .bdrv_close = vmdk_close, + .bdrv_create = vmdk_create, + .bdrv_co_flush_to_disk = vmdk_co_flush, + .bdrv_co_get_block_status = vmdk_co_get_block_status, + .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, + .bdrv_has_zero_init = vmdk_has_zero_init, + .bdrv_get_specific_info = vmdk_get_specific_info, + .bdrv_refresh_limits = vmdk_refresh_limits, + .bdrv_get_info = vmdk_get_info, + .bdrv_detach_aio_context = vmdk_detach_aio_context, + .bdrv_attach_aio_context = vmdk_attach_aio_context, + + .supports_backing = true, + .create_opts = &vmdk_create_opts, +}; + +static void bdrv_vmdk_init(void) +{ + bdrv_register(&bdrv_vmdk); +} + +block_init(bdrv_vmdk_init); diff --git a/block/vpc.c b/block/vpc.c new file mode 100644 index 0000000..37572ba --- /dev/null +++ b/block/vpc.c @@ -0,0 +1,934 @@ +/* + * Block driver for Connectix / Microsoft Virtual PC images + * + * Copyright (c) 2005 Alex Beregszaszi + * Copyright (c) 2009 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "migration/migration.h" +#if defined(CONFIG_UUID) +#include +#endif + +/**************************************************************/ + +#define HEADER_SIZE 512 + +//#define CACHE + +enum vhd_type { + VHD_FIXED = 2, + VHD_DYNAMIC = 3, + VHD_DIFFERENCING = 4, +}; + +// Seconds since Jan 1, 2000 0:00:00 (UTC) +#define VHD_TIMESTAMP_BASE 946684800 + +#define VHD_MAX_SECTORS (65535LL * 255 * 255) +#define VHD_MAX_GEOMETRY (65535LL * 16 * 255) + +// always big-endian +typedef struct vhd_footer { + char creator[8]; // "conectix" + uint32_t features; + uint32_t version; + + // Offset of next header structure, 0xFFFFFFFF if none + uint64_t data_offset; + + // Seconds since Jan 1, 2000 0:00:00 (UTC) + uint32_t timestamp; + + char creator_app[4]; // "vpc " + uint16_t major; + uint16_t minor; + char creator_os[4]; // "Wi2k" + + uint64_t orig_size; + uint64_t current_size; + + uint16_t cyls; + uint8_t heads; + uint8_t secs_per_cyl; + + uint32_t type; + + // Checksum of the Hard Disk Footer ("one's complement of the sum of all + // the bytes in the footer without the checksum field") + uint32_t checksum; + + // UUID used to identify a parent hard disk (backing file) + uint8_t uuid[16]; + + uint8_t in_saved_state; +} QEMU_PACKED VHDFooter; + +typedef struct vhd_dyndisk_header { + char magic[8]; // "cxsparse" + + // Offset of next header structure, 0xFFFFFFFF if none + uint64_t data_offset; + + // Offset of the Block Allocation Table (BAT) + uint64_t table_offset; + + uint32_t version; + uint32_t max_table_entries; // 32bit/entry + + // 2 MB by default, must be a power of two + uint32_t block_size; + + uint32_t checksum; + uint8_t parent_uuid[16]; + uint32_t parent_timestamp; + uint32_t reserved; + + // Backing file name (in UTF-16) + uint8_t parent_name[512]; + + struct { + uint32_t platform; + uint32_t data_space; + uint32_t data_length; + uint32_t reserved; + uint64_t data_offset; + } parent_locator[8]; +} QEMU_PACKED VHDDynDiskHeader; + +typedef struct BDRVVPCState { + CoMutex lock; + uint8_t footer_buf[HEADER_SIZE]; + uint64_t free_data_block_offset; + int max_table_entries; + uint32_t *pagetable; + uint64_t bat_offset; + uint64_t last_bitmap_offset; + + uint32_t block_size; + uint32_t bitmap_size; + +#ifdef CACHE + uint8_t *pageentry_u8; + uint32_t *pageentry_u32; + uint16_t *pageentry_u16; + + uint64_t last_bitmap; +#endif + + Error *migration_blocker; +} BDRVVPCState; + +static uint32_t vpc_checksum(uint8_t* buf, size_t size) +{ + uint32_t res = 0; + int i; + + for (i = 0; i < size; i++) + res += buf[i]; + + return ~res; +} + + +static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) + return 100; + return 0; +} + +static int vpc_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVVPCState *s = bs->opaque; + int i; + VHDFooter *footer; + VHDDynDiskHeader *dyndisk_header; + uint8_t buf[HEADER_SIZE]; + uint32_t checksum; + uint64_t computed_size; + int disk_type = VHD_DYNAMIC; + int ret; + + ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); + if (ret < 0) { + goto fail; + } + + footer = (VHDFooter *) s->footer_buf; + if (strncmp(footer->creator, "conectix", 8)) { + int64_t offset = bdrv_getlength(bs->file); + if (offset < 0) { + ret = offset; + goto fail; + } else if (offset < HEADER_SIZE) { + ret = -EINVAL; + goto fail; + } + + /* If a fixed disk, the footer is found only at the end of the file */ + ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, + HEADER_SIZE); + if (ret < 0) { + goto fail; + } + if (strncmp(footer->creator, "conectix", 8)) { + error_setg(errp, "invalid VPC image"); + ret = -EINVAL; + goto fail; + } + disk_type = VHD_FIXED; + } + + checksum = be32_to_cpu(footer->checksum); + footer->checksum = 0; + if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) + fprintf(stderr, "block-vpc: The header checksum of '%s' is " + "incorrect.\n", bs->filename); + + /* Write 'checksum' back to footer, or else will leave it with zero. */ + footer->checksum = cpu_to_be32(checksum); + + // The visible size of a image in Virtual PC depends on the geometry + // rather than on the size stored in the footer (the size in the footer + // is too large usually) + bs->total_sectors = (int64_t) + be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; + + /* Images that have exactly the maximum geometry are probably bigger and + * would be truncated if we adhered to the geometry for them. Rely on + * footer->current_size for them. */ + if (bs->total_sectors == VHD_MAX_GEOMETRY) { + bs->total_sectors = be64_to_cpu(footer->current_size) / + BDRV_SECTOR_SIZE; + } + + /* Allow a maximum disk size of approximately 2 TB */ + if (bs->total_sectors >= VHD_MAX_SECTORS) { + ret = -EFBIG; + goto fail; + } + + if (disk_type == VHD_DYNAMIC) { + ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, + HEADER_SIZE); + if (ret < 0) { + goto fail; + } + + dyndisk_header = (VHDDynDiskHeader *) buf; + + if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { + ret = -EINVAL; + goto fail; + } + + s->block_size = be32_to_cpu(dyndisk_header->block_size); + if (!is_power_of_2(s->block_size) || s->block_size < BDRV_SECTOR_SIZE) { + error_setg(errp, "Invalid block size %" PRIu32, s->block_size); + ret = -EINVAL; + goto fail; + } + s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; + + s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); + + if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) { + ret = -EINVAL; + goto fail; + } + if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) { + ret = -EINVAL; + goto fail; + } + + computed_size = (uint64_t) s->max_table_entries * s->block_size; + if (computed_size < bs->total_sectors * 512) { + ret = -EINVAL; + goto fail; + } + + s->pagetable = qemu_try_blockalign(bs->file, s->max_table_entries * 4); + if (s->pagetable == NULL) { + ret = -ENOMEM; + goto fail; + } + + s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); + + ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, + s->max_table_entries * 4); + if (ret < 0) { + goto fail; + } + + s->free_data_block_offset = + (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; + + for (i = 0; i < s->max_table_entries; i++) { + be32_to_cpus(&s->pagetable[i]); + if (s->pagetable[i] != 0xFFFFFFFF) { + int64_t next = (512 * (int64_t) s->pagetable[i]) + + s->bitmap_size + s->block_size; + + if (next > s->free_data_block_offset) { + s->free_data_block_offset = next; + } + } + } + + if (s->free_data_block_offset > bdrv_getlength(bs->file)) { + error_setg(errp, "block-vpc: free_data_block_offset points after " + "the end of file. The image has been truncated."); + ret = -EINVAL; + goto fail; + } + + s->last_bitmap_offset = (int64_t) -1; + +#ifdef CACHE + s->pageentry_u8 = g_malloc(512); + s->pageentry_u32 = s->pageentry_u8; + s->pageentry_u16 = s->pageentry_u8; + s->last_pagetable = -1; +#endif + } + + qemu_co_mutex_init(&s->lock); + + /* Disable migration when VHD images are used */ + error_setg(&s->migration_blocker, "The vpc format used by node '%s' " + "does not support live migration", + bdrv_get_device_or_node_name(bs)); + migrate_add_blocker(s->migration_blocker); + + return 0; + +fail: + qemu_vfree(s->pagetable); +#ifdef CACHE + g_free(s->pageentry_u8); +#endif + return ret; +} + +static int vpc_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + return 0; +} + +/* + * Returns the absolute byte offset of the given sector in the image file. + * If the sector is not allocated, -1 is returned instead. + * + * The parameter write must be 1 if the offset will be used for a write + * operation (the block bitmaps is updated then), 0 otherwise. + */ +static inline int64_t get_sector_offset(BlockDriverState *bs, + int64_t sector_num, int write) +{ + BDRVVPCState *s = bs->opaque; + uint64_t offset = sector_num * 512; + uint64_t bitmap_offset, block_offset; + uint32_t pagetable_index, pageentry_index; + + pagetable_index = offset / s->block_size; + pageentry_index = (offset % s->block_size) / 512; + + if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff) + return -1; // not allocated + + bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index]; + block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index); + + // We must ensure that we don't write to any sectors which are marked as + // unused in the bitmap. We get away with setting all bits in the block + // bitmap each time we write to a new block. This might cause Virtual PC to + // miss sparse read optimization, but it's not a problem in terms of + // correctness. + if (write && (s->last_bitmap_offset != bitmap_offset)) { + uint8_t bitmap[s->bitmap_size]; + + s->last_bitmap_offset = bitmap_offset; + memset(bitmap, 0xff, s->bitmap_size); + bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); + } + + return block_offset; +} + +/* + * Writes the footer to the end of the image file. This is needed when the + * file grows as it overwrites the old footer + * + * Returns 0 on success and < 0 on error + */ +static int rewrite_footer(BlockDriverState* bs) +{ + int ret; + BDRVVPCState *s = bs->opaque; + int64_t offset = s->free_data_block_offset; + + ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Allocates a new block. This involves writing a new footer and updating + * the Block Allocation Table to use the space at the old end of the image + * file (overwriting the old footer) + * + * Returns the sectors' offset in the image file on success and < 0 on error + */ +static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) +{ + BDRVVPCState *s = bs->opaque; + int64_t bat_offset; + uint32_t index, bat_value; + int ret; + uint8_t bitmap[s->bitmap_size]; + + // Check if sector_num is valid + if ((sector_num < 0) || (sector_num > bs->total_sectors)) + return -1; + + // Write entry into in-memory BAT + index = (sector_num * 512) / s->block_size; + if (s->pagetable[index] != 0xFFFFFFFF) + return -1; + + s->pagetable[index] = s->free_data_block_offset / 512; + + // Initialize the block's bitmap + memset(bitmap, 0xff, s->bitmap_size); + ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, + s->bitmap_size); + if (ret < 0) { + return ret; + } + + // Write new footer (the old one will be overwritten) + s->free_data_block_offset += s->block_size + s->bitmap_size; + ret = rewrite_footer(bs); + if (ret < 0) + goto fail; + + // Write BAT entry to disk + bat_offset = s->bat_offset + (4 * index); + bat_value = cpu_to_be32(s->pagetable[index]); + ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); + if (ret < 0) + goto fail; + + return get_sector_offset(bs, sector_num, 0); + +fail: + s->free_data_block_offset -= (s->block_size + s->bitmap_size); + return -1; +} + +static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVVPCState *s = (BDRVVPCState *)bs->opaque; + VHDFooter *footer = (VHDFooter *) s->footer_buf; + + if (be32_to_cpu(footer->type) != VHD_FIXED) { + bdi->cluster_size = s->block_size; + } + + bdi->unallocated_blocks_are_zero = true; + return 0; +} + +static int vpc_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVVPCState *s = bs->opaque; + int ret; + int64_t offset; + int64_t sectors, sectors_per_block; + VHDFooter *footer = (VHDFooter *) s->footer_buf; + + if (be32_to_cpu(footer->type) == VHD_FIXED) { + return bdrv_read(bs->file, sector_num, buf, nb_sectors); + } + while (nb_sectors > 0) { + offset = get_sector_offset(bs, sector_num, 0); + + sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; + sectors = sectors_per_block - (sector_num % sectors_per_block); + if (sectors > nb_sectors) { + sectors = nb_sectors; + } + + if (offset == -1) { + memset(buf, 0, sectors * BDRV_SECTOR_SIZE); + } else { + ret = bdrv_pread(bs->file, offset, buf, + sectors * BDRV_SECTOR_SIZE); + if (ret != sectors * BDRV_SECTOR_SIZE) { + return -1; + } + } + + nb_sectors -= sectors; + sector_num += sectors; + buf += sectors * BDRV_SECTOR_SIZE; + } + return 0; +} + +static coroutine_fn int vpc_co_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVVPCState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = vpc_read(bs, sector_num, buf, nb_sectors); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int vpc_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVVPCState *s = bs->opaque; + int64_t offset; + int64_t sectors, sectors_per_block; + int ret; + VHDFooter *footer = (VHDFooter *) s->footer_buf; + + if (be32_to_cpu(footer->type) == VHD_FIXED) { + return bdrv_write(bs->file, sector_num, buf, nb_sectors); + } + while (nb_sectors > 0) { + offset = get_sector_offset(bs, sector_num, 1); + + sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; + sectors = sectors_per_block - (sector_num % sectors_per_block); + if (sectors > nb_sectors) { + sectors = nb_sectors; + } + + if (offset == -1) { + offset = alloc_block(bs, sector_num); + if (offset < 0) + return -1; + } + + ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE); + if (ret != sectors * BDRV_SECTOR_SIZE) { + return -1; + } + + nb_sectors -= sectors; + sector_num += sectors; + buf += sectors * BDRV_SECTOR_SIZE; + } + + return 0; +} + +static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVVPCState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = vpc_write(bs, sector_num, buf, nb_sectors); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + BDRVVPCState *s = bs->opaque; + VHDFooter *footer = (VHDFooter*) s->footer_buf; + int64_t start, offset; + bool allocated; + int n; + + if (be32_to_cpu(footer->type) == VHD_FIXED) { + *pnum = nb_sectors; + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | + (sector_num << BDRV_SECTOR_BITS); + } + + offset = get_sector_offset(bs, sector_num, 0); + start = offset; + allocated = (offset != -1); + *pnum = 0; + + do { + /* All sectors in a block are contiguous (without using the bitmap) */ + n = ROUND_UP(sector_num + 1, s->block_size / BDRV_SECTOR_SIZE) + - sector_num; + n = MIN(n, nb_sectors); + + *pnum += n; + sector_num += n; + nb_sectors -= n; + /* *pnum can't be greater than one block for allocated + * sectors since there is always a bitmap in between. */ + if (allocated) { + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + } + if (nb_sectors == 0) { + break; + } + offset = get_sector_offset(bs, sector_num, 0); + } while (offset == -1); + + return 0; +} + +/* + * Calculates the number of cylinders, heads and sectors per cylinder + * based on a given number of sectors. This is the algorithm described + * in the VHD specification. + * + * Note that the geometry doesn't always exactly match total_sectors but + * may round it down. + * + * Returns 0 on success, -EFBIG if the size is larger than ~2 TB. Override + * the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB) + * and instead allow up to 255 heads. + */ +static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, + uint8_t* heads, uint8_t* secs_per_cyl) +{ + uint32_t cyls_times_heads; + + total_sectors = MIN(total_sectors, VHD_MAX_GEOMETRY); + + if (total_sectors >= 65535LL * 16 * 63) { + *secs_per_cyl = 255; + *heads = 16; + cyls_times_heads = total_sectors / *secs_per_cyl; + } else { + *secs_per_cyl = 17; + cyls_times_heads = total_sectors / *secs_per_cyl; + *heads = (cyls_times_heads + 1023) / 1024; + + if (*heads < 4) { + *heads = 4; + } + + if (cyls_times_heads >= (*heads * 1024) || *heads > 16) { + *secs_per_cyl = 31; + *heads = 16; + cyls_times_heads = total_sectors / *secs_per_cyl; + } + + if (cyls_times_heads >= (*heads * 1024)) { + *secs_per_cyl = 63; + *heads = 16; + cyls_times_heads = total_sectors / *secs_per_cyl; + } + } + + *cyls = cyls_times_heads / *heads; + + return 0; +} + +static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf, + int64_t total_sectors) +{ + VHDDynDiskHeader *dyndisk_header = + (VHDDynDiskHeader *) buf; + size_t block_size, num_bat_entries; + int i; + int ret; + int64_t offset = 0; + + // Write the footer (twice: at the beginning and at the end) + block_size = 0x200000; + num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); + + ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE); + if (ret) { + goto fail; + } + + offset = 1536 + ((num_bat_entries * 4 + 511) & ~511); + ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE); + if (ret < 0) { + goto fail; + } + + // Write the initial BAT + offset = 3 * 512; + + memset(buf, 0xFF, 512); + for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) { + ret = bdrv_pwrite_sync(bs, offset, buf, 512); + if (ret < 0) { + goto fail; + } + offset += 512; + } + + // Prepare the Dynamic Disk Header + memset(buf, 0, 1024); + + memcpy(dyndisk_header->magic, "cxsparse", 8); + + /* + * Note: The spec is actually wrong here for data_offset, it says + * 0xFFFFFFFF, but MS tools expect all 64 bits to be set. + */ + dyndisk_header->data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL); + dyndisk_header->table_offset = cpu_to_be64(3 * 512); + dyndisk_header->version = cpu_to_be32(0x00010000); + dyndisk_header->block_size = cpu_to_be32(block_size); + dyndisk_header->max_table_entries = cpu_to_be32(num_bat_entries); + + dyndisk_header->checksum = cpu_to_be32(vpc_checksum(buf, 1024)); + + // Write the header + offset = 512; + + ret = bdrv_pwrite_sync(bs, offset, buf, 1024); + if (ret < 0) { + goto fail; + } + + fail: + return ret; +} + +static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf, + int64_t total_size) +{ + int ret; + + /* Add footer to total size */ + total_size += HEADER_SIZE; + + ret = bdrv_truncate(bs, total_size); + if (ret < 0) { + return ret; + } + + ret = bdrv_pwrite_sync(bs, total_size - HEADER_SIZE, buf, HEADER_SIZE); + if (ret < 0) { + return ret; + } + + return ret; +} + +static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) +{ + uint8_t buf[1024]; + VHDFooter *footer = (VHDFooter *) buf; + char *disk_type_param; + int i; + uint16_t cyls = 0; + uint8_t heads = 0; + uint8_t secs_per_cyl = 0; + int64_t total_sectors; + int64_t total_size; + int disk_type; + int ret = -EIO; + Error *local_err = NULL; + BlockDriverState *bs = NULL; + + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); + if (disk_type_param) { + if (!strcmp(disk_type_param, "dynamic")) { + disk_type = VHD_DYNAMIC; + } else if (!strcmp(disk_type_param, "fixed")) { + disk_type = VHD_FIXED; + } else { + ret = -EINVAL; + goto out; + } + } else { + disk_type = VHD_DYNAMIC; + } + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto out; + } + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto out; + } + + /* + * Calculate matching total_size and geometry. Increase the number of + * sectors requested until we get enough (or fail). This ensures that + * qemu-img convert doesn't truncate images, but rather rounds up. + * + * If the image size can't be represented by a spec conform CHS geometry, + * we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use + * the image size from the VHD footer to calculate total_sectors. + */ + total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE); + for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { + calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl); + } + + if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) { + total_sectors = total_size / BDRV_SECTOR_SIZE; + /* Allow a maximum disk size of approximately 2 TB */ + if (total_sectors > VHD_MAX_SECTORS) { + ret = -EFBIG; + goto out; + } + } else { + total_sectors = (int64_t)cyls * heads * secs_per_cyl; + total_size = total_sectors * BDRV_SECTOR_SIZE; + } + + /* Prepare the Hard Disk Footer */ + memset(buf, 0, 1024); + + memcpy(footer->creator, "conectix", 8); + /* TODO Check if "qemu" creator_app is ok for VPC */ + memcpy(footer->creator_app, "qemu", 4); + memcpy(footer->creator_os, "Wi2k", 4); + + footer->features = cpu_to_be32(0x02); + footer->version = cpu_to_be32(0x00010000); + if (disk_type == VHD_DYNAMIC) { + footer->data_offset = cpu_to_be64(HEADER_SIZE); + } else { + footer->data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL); + } + footer->timestamp = cpu_to_be32(time(NULL) - VHD_TIMESTAMP_BASE); + + /* Version of Virtual PC 2007 */ + footer->major = cpu_to_be16(0x0005); + footer->minor = cpu_to_be16(0x0003); + footer->orig_size = cpu_to_be64(total_size); + footer->current_size = cpu_to_be64(total_size); + footer->cyls = cpu_to_be16(cyls); + footer->heads = heads; + footer->secs_per_cyl = secs_per_cyl; + + footer->type = cpu_to_be32(disk_type); + +#if defined(CONFIG_UUID) + uuid_generate(footer->uuid); +#endif + + footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE)); + + if (disk_type == VHD_DYNAMIC) { + ret = create_dynamic_disk(bs, buf, total_sectors); + } else { + ret = create_fixed_disk(bs, buf, total_size); + } + +out: + bdrv_unref(bs); + g_free(disk_type_param); + return ret; +} + +static int vpc_has_zero_init(BlockDriverState *bs) +{ + BDRVVPCState *s = bs->opaque; + VHDFooter *footer = (VHDFooter *) s->footer_buf; + + if (be32_to_cpu(footer->type) == VHD_FIXED) { + return bdrv_has_zero_init(bs->file); + } else { + return 1; + } +} + +static void vpc_close(BlockDriverState *bs) +{ + BDRVVPCState *s = bs->opaque; + qemu_vfree(s->pagetable); +#ifdef CACHE + g_free(s->pageentry_u8); +#endif + + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); +} + +static QemuOptsList vpc_create_opts = { + .name = "vpc-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(vpc_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_SUBFMT, + .type = QEMU_OPT_STRING, + .help = + "Type of virtual hard disk format. Supported formats are " + "{dynamic (default) | fixed} " + }, + { /* end of list */ } + } +}; + +static BlockDriver bdrv_vpc = { + .format_name = "vpc", + .instance_size = sizeof(BDRVVPCState), + + .bdrv_probe = vpc_probe, + .bdrv_open = vpc_open, + .bdrv_close = vpc_close, + .bdrv_reopen_prepare = vpc_reopen_prepare, + .bdrv_create = vpc_create, + + .bdrv_read = vpc_co_read, + .bdrv_write = vpc_co_write, + .bdrv_co_get_block_status = vpc_co_get_block_status, + + .bdrv_get_info = vpc_get_info, + + .create_opts = &vpc_create_opts, + .bdrv_has_zero_init = vpc_has_zero_init, +}; + +static void bdrv_vpc_init(void) +{ + bdrv_register(&bdrv_vpc); +} + +block_init(bdrv_vpc_init); diff --git a/block/vvfat.c b/block/vvfat.c new file mode 100644 index 0000000..e803589 --- /dev/null +++ b/block/vvfat.c @@ -0,0 +1,3022 @@ +/* vim:set shiftwidth=4 ts=4: */ +/* + * QEMU Block driver for virtual VFAT (shadows a local directory) + * + * Copyright (c) 2004,2005 Johannes E. Schindelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include "qemu-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "migration/migration.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qbool.h" + +#ifndef S_IWGRP +#define S_IWGRP 0 +#endif +#ifndef S_IWOTH +#define S_IWOTH 0 +#endif + +/* TODO: add ":bootsector=blabla.img:" */ +/* LATER TODO: add automatic boot sector generation from + BOOTEASY.ASM and Ranish Partition Manager + Note that DOS assumes the system files to be the first files in the + file system (test if the boot sector still relies on that fact)! */ +/* MAYBE TODO: write block-visofs.c */ +/* TODO: call try_commit() only after a timeout */ + +/* #define DEBUG */ + +#ifdef DEBUG + +#define DLOG(a) a + +static void checkpoint(void); + +#ifdef __MINGW32__ +void nonono(const char* file, int line, const char* msg) { + fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg); + exit(-5); +} +#undef assert +#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0) +#endif + +#else + +#define DLOG(a) + +#endif + +/* dynamic array functions */ +typedef struct array_t { + char* pointer; + unsigned int size,next,item_size; +} array_t; + +static inline void array_init(array_t* array,unsigned int item_size) +{ + array->pointer = NULL; + array->size=0; + array->next=0; + array->item_size=item_size; +} + +static inline void array_free(array_t* array) +{ + g_free(array->pointer); + array->size=array->next=0; +} + +/* does not automatically grow */ +static inline void* array_get(array_t* array,unsigned int index) { + assert(index < array->next); + return array->pointer + index * array->item_size; +} + +static inline int array_ensure_allocated(array_t* array, int index) +{ + if((index + 1) * array->item_size > array->size) { + int new_size = (index + 32) * array->item_size; + array->pointer = g_realloc(array->pointer, new_size); + if (!array->pointer) + return -1; + array->size = new_size; + array->next = index + 1; + } + + return 0; +} + +static inline void* array_get_next(array_t* array) { + unsigned int next = array->next; + void* result; + + if (array_ensure_allocated(array, next) < 0) + return NULL; + + array->next = next + 1; + result = array_get(array, next); + + return result; +} + +static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) { + if((array->next+count)*array->item_size>array->size) { + int increment=count*array->item_size; + array->pointer=g_realloc(array->pointer,array->size+increment); + if(!array->pointer) + return NULL; + array->size+=increment; + } + memmove(array->pointer+(index+count)*array->item_size, + array->pointer+index*array->item_size, + (array->next-index)*array->item_size); + array->next+=count; + return array->pointer+index*array->item_size; +} + +/* this performs a "roll", so that the element which was at index_from becomes + * index_to, but the order of all other elements is preserved. */ +static inline int array_roll(array_t* array,int index_to,int index_from,int count) +{ + char* buf; + char* from; + char* to; + int is; + + if(!array || + index_to<0 || index_to>=array->next || + index_from<0 || index_from>=array->next) + return -1; + + if(index_to==index_from) + return 0; + + is=array->item_size; + from=array->pointer+index_from*is; + to=array->pointer+index_to*is; + buf=g_malloc(is*count); + memcpy(buf,from,is*count); + + if(index_to=0); + assert(count > 0); + assert(index + count <= array->next); + if(array_roll(array,array->next-1,index,count)) + return -1; + array->next -= count; + return 0; +} + +static int array_remove(array_t* array,int index) +{ + return array_remove_slice(array, index, 1); +} + +/* return the index for a given member */ +static int array_index(array_t* array, void* pointer) +{ + size_t offset = (char*)pointer - array->pointer; + assert((offset % array->item_size) == 0); + assert(offset/array->item_size < array->next); + return offset/array->item_size; +} + +/* These structures are used to fake a disk and the VFAT filesystem. + * For this reason we need to use QEMU_PACKED. */ + +typedef struct bootsector_t { + uint8_t jump[3]; + uint8_t name[8]; + uint16_t sector_size; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t number_of_fats; + uint16_t root_entries; + uint16_t total_sectors16; + uint8_t media_type; + uint16_t sectors_per_fat; + uint16_t sectors_per_track; + uint16_t number_of_heads; + uint32_t hidden_sectors; + uint32_t total_sectors; + union { + struct { + uint8_t drive_number; + uint8_t current_head; + uint8_t signature; + uint32_t id; + uint8_t volume_label[11]; + } QEMU_PACKED fat16; + struct { + uint32_t sectors_per_fat; + uint16_t flags; + uint8_t major,minor; + uint32_t first_cluster_of_root_directory; + uint16_t info_sector; + uint16_t backup_boot_sector; + uint16_t ignored; + } QEMU_PACKED fat32; + } u; + uint8_t fat_type[8]; + uint8_t ignored[0x1c0]; + uint8_t magic[2]; +} QEMU_PACKED bootsector_t; + +typedef struct { + uint8_t head; + uint8_t sector; + uint8_t cylinder; +} mbr_chs_t; + +typedef struct partition_t { + uint8_t attributes; /* 0x80 = bootable */ + mbr_chs_t start_CHS; + uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */ + mbr_chs_t end_CHS; + uint32_t start_sector_long; + uint32_t length_sector_long; +} QEMU_PACKED partition_t; + +typedef struct mbr_t { + uint8_t ignored[0x1b8]; + uint32_t nt_id; + uint8_t ignored2[2]; + partition_t partition[4]; + uint8_t magic[2]; +} QEMU_PACKED mbr_t; + +typedef struct direntry_t { + uint8_t name[8 + 3]; + uint8_t attributes; + uint8_t reserved[2]; + uint16_t ctime; + uint16_t cdate; + uint16_t adate; + uint16_t begin_hi; + uint16_t mtime; + uint16_t mdate; + uint16_t begin; + uint32_t size; +} QEMU_PACKED direntry_t; + +/* this structure are used to transparently access the files */ + +typedef struct mapping_t { + /* begin is the first cluster, end is the last+1 */ + uint32_t begin,end; + /* as s->directory is growable, no pointer may be used here */ + unsigned int dir_index; + /* the clusters of a file may be in any order; this points to the first */ + int first_mapping_index; + union { + /* offset is + * - the offset in the file (in clusters) for a file, or + * - the next cluster of the directory for a directory, and + * - the address of the buffer for a faked entry + */ + struct { + uint32_t offset; + } file; + struct { + int parent_mapping_index; + int first_dir_index; + } dir; + } info; + /* path contains the full path, i.e. it always starts with s->path */ + char* path; + + enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2, + MODE_DIRECTORY = 4, MODE_FAKED = 8, + MODE_DELETED = 16, MODE_RENAMED = 32 } mode; + int read_only; +} mapping_t; + +#ifdef DEBUG +static void print_direntry(const struct direntry_t*); +static void print_mapping(const struct mapping_t* mapping); +#endif + +/* here begins the real VVFAT driver */ + +typedef struct BDRVVVFATState { + CoMutex lock; + BlockDriverState* bs; /* pointer to parent */ + unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */ + unsigned char first_sectors[0x40*0x200]; + + int fat_type; /* 16 or 32 */ + array_t fat,directory,mapping; + + unsigned int cluster_size; + unsigned int sectors_per_cluster; + unsigned int sectors_per_fat; + unsigned int sectors_of_root_directory; + uint32_t last_cluster_of_root_directory; + unsigned int faked_sectors; /* how many sectors are faked before file data */ + uint32_t sector_count; /* total number of sectors of the partition */ + uint32_t cluster_count; /* total number of clusters of this partition */ + uint32_t max_fat_value; + + int current_fd; + mapping_t* current_mapping; + unsigned char* cluster; /* points to current cluster */ + unsigned char* cluster_buffer; /* points to a buffer to hold temp data */ + unsigned int current_cluster; + + /* write support */ + BlockDriverState* write_target; + char* qcow_filename; + BlockDriverState* qcow; + void* fat2; + char* used_clusters; + array_t commits; + const char* path; + int downcase_short_names; + + Error *migration_blocker; +} BDRVVVFATState; + +/* take the sector position spos and convert it to Cylinder/Head/Sector position + * if the position is outside the specified geometry, fill maximum value for CHS + * and return 1 to signal overflow. + */ +static int sector2CHS(mbr_chs_t *chs, int spos, int cyls, int heads, int secs) +{ + int head,sector; + sector = spos % secs; spos /= secs; + head = spos % heads; spos /= heads; + if (spos >= cyls) { + /* Overflow, + it happens if 32bit sector positions are used, while CHS is only 24bit. + Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */ + chs->head = 0xFF; + chs->sector = 0xFF; + chs->cylinder = 0xFF; + return 1; + } + chs->head = (uint8_t)head; + chs->sector = (uint8_t)( (sector+1) | ((spos>>8)<<6) ); + chs->cylinder = (uint8_t)spos; + return 0; +} + +static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs) +{ + /* TODO: if the files mbr.img and bootsect.img exist, use them */ + mbr_t* real_mbr=(mbr_t*)s->first_sectors; + partition_t* partition = &(real_mbr->partition[0]); + int lba; + + memset(s->first_sectors,0,512); + + /* Win NT Disk Signature */ + real_mbr->nt_id= cpu_to_le32(0xbe1afdfa); + + partition->attributes=0x80; /* bootable */ + + /* LBA is used when partition is outside the CHS geometry */ + lba = sector2CHS(&partition->start_CHS, s->first_sectors_number - 1, + cyls, heads, secs); + lba |= sector2CHS(&partition->end_CHS, s->bs->total_sectors - 1, + cyls, heads, secs); + + /*LBA partitions are identified only by start/length_sector_long not by CHS*/ + partition->start_sector_long = cpu_to_le32(s->first_sectors_number - 1); + partition->length_sector_long = cpu_to_le32(s->bs->total_sectors + - s->first_sectors_number + 1); + + /* FAT12/FAT16/FAT32 */ + /* DOS uses different types when partition is LBA, + probably to prevent older versions from using CHS on them */ + partition->fs_type= s->fat_type==12 ? 0x1: + s->fat_type==16 ? (lba?0xe:0x06): + /*fat_tyoe==32*/ (lba?0xc:0x0b); + + real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa; +} + +/* direntry functions */ + +/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */ +static inline int short2long_name(char* dest,const char* src) +{ + int i; + int len; + for(i=0;i<129 && src[i];i++) { + dest[2*i]=src[i]; + dest[2*i+1]=0; + } + len=2*i; + dest[2*i]=dest[2*i+1]=0; + for(i=2*i+2;(i%26);i++) + dest[i]=0xff; + return len; +} + +static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename) +{ + char buffer[258]; + int length=short2long_name(buffer,filename), + number_of_entries=(length+25)/26,i; + direntry_t* entry; + + for(i=0;idirectory)); + entry->attributes=0xf; + entry->reserved[0]=0; + entry->begin=0; + entry->name[0]=(number_of_entries-i)|(i==0?0x40:0); + } + for(i=0;i<26*number_of_entries;i++) { + int offset=(i%26); + if(offset<10) offset=1+offset; + else if(offset<22) offset=14+offset-10; + else offset=28+offset-22; + entry=array_get(&(s->directory),s->directory.next-1-(i/26)); + entry->name[offset]=buffer[i]; + } + return array_get(&(s->directory),s->directory.next-number_of_entries); +} + +static char is_free(const direntry_t* direntry) +{ + return direntry->name[0]==0xe5 || direntry->name[0]==0x00; +} + +static char is_volume_label(const direntry_t* direntry) +{ + return direntry->attributes == 0x28; +} + +static char is_long_name(const direntry_t* direntry) +{ + return direntry->attributes == 0xf; +} + +static char is_short_name(const direntry_t* direntry) +{ + return !is_volume_label(direntry) && !is_long_name(direntry) + && !is_free(direntry); +} + +static char is_directory(const direntry_t* direntry) +{ + return direntry->attributes & 0x10 && direntry->name[0] != 0xe5; +} + +static inline char is_dot(const direntry_t* direntry) +{ + return is_short_name(direntry) && direntry->name[0] == '.'; +} + +static char is_file(const direntry_t* direntry) +{ + return is_short_name(direntry) && !is_directory(direntry); +} + +static inline uint32_t begin_of_direntry(const direntry_t* direntry) +{ + return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16); +} + +static inline uint32_t filesize_of_direntry(const direntry_t* direntry) +{ + return le32_to_cpu(direntry->size); +} + +static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin) +{ + direntry->begin = cpu_to_le16(begin & 0xffff); + direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff); +} + +/* fat functions */ + +static inline uint8_t fat_chksum(const direntry_t* entry) +{ + uint8_t chksum=0; + int i; + + for (i = 0; i < ARRAY_SIZE(entry->name); i++) { + chksum = (((chksum & 0xfe) >> 1) | + ((chksum & 0x01) ? 0x80 : 0)) + entry->name[i]; + } + + return chksum; +} + +/* if return_time==0, this returns the fat_date, else the fat_time */ +static uint16_t fat_datetime(time_t time,int return_time) { + struct tm* t; + struct tm t1; + t = &t1; + localtime_r(&time,t); + if(return_time) + return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11)); + return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9)); +} + +static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value) +{ + if(s->fat_type==32) { + uint32_t* entry=array_get(&(s->fat),cluster); + *entry=cpu_to_le32(value); + } else if(s->fat_type==16) { + uint16_t* entry=array_get(&(s->fat),cluster); + *entry=cpu_to_le16(value&0xffff); + } else { + int offset = (cluster*3/2); + unsigned char* p = array_get(&(s->fat), offset); + switch (cluster&1) { + case 0: + p[0] = value&0xff; + p[1] = (p[1]&0xf0) | ((value>>8)&0xf); + break; + case 1: + p[0] = (p[0]&0xf) | ((value&0xf)<<4); + p[1] = (value>>4); + break; + } + } +} + +static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster) +{ + if(s->fat_type==32) { + uint32_t* entry=array_get(&(s->fat),cluster); + return le32_to_cpu(*entry); + } else if(s->fat_type==16) { + uint16_t* entry=array_get(&(s->fat),cluster); + return le16_to_cpu(*entry); + } else { + const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2; + return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff; + } +} + +static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry) +{ + if(fat_entry>s->max_fat_value-8) + return -1; + return 0; +} + +static inline void init_fat(BDRVVVFATState* s) +{ + if (s->fat_type == 12) { + array_init(&(s->fat),1); + array_ensure_allocated(&(s->fat), + s->sectors_per_fat * 0x200 * 3 / 2 - 1); + } else { + array_init(&(s->fat),(s->fat_type==32?4:2)); + array_ensure_allocated(&(s->fat), + s->sectors_per_fat * 0x200 / s->fat.item_size - 1); + } + memset(s->fat.pointer,0,s->fat.size); + + switch(s->fat_type) { + case 12: s->max_fat_value=0xfff; break; + case 16: s->max_fat_value=0xffff; break; + case 32: s->max_fat_value=0x0fffffff; break; + default: s->max_fat_value=0; /* error... */ + } + +} + +/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */ +/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */ +static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s, + unsigned int directory_start, const char* filename, int is_dot) +{ + int i,j,long_index=s->directory.next; + direntry_t* entry = NULL; + direntry_t* entry_long = NULL; + + if(is_dot) { + entry=array_get_next(&(s->directory)); + memset(entry->name, 0x20, sizeof(entry->name)); + memcpy(entry->name,filename,strlen(filename)); + return entry; + } + + entry_long=create_long_filename(s,filename); + + i = strlen(filename); + for(j = i - 1; j>0 && filename[j]!='.';j--); + if (j > 0) + i = (j > 8 ? 8 : j); + else if (i > 8) + i = 8; + + entry=array_get_next(&(s->directory)); + memset(entry->name, 0x20, sizeof(entry->name)); + memcpy(entry->name, filename, i); + + if (j > 0) { + for (i = 0; i < 3 && filename[j + 1 + i]; i++) { + entry->name[8 + i] = filename[j + 1 + i]; + } + } + + /* upcase & remove unwanted characters */ + for(i=10;i>=0;i--) { + if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--); + if(entry->name[i]<=' ' || entry->name[i]>0x7f + || strchr(".*?<>|\":/\\[];,+='",entry->name[i])) + entry->name[i]='_'; + else if(entry->name[i]>='a' && entry->name[i]<='z') + entry->name[i]+='A'-'a'; + } + + /* mangle duplicates */ + while(1) { + direntry_t* entry1=array_get(&(s->directory),directory_start); + int j; + + for(;entry1name,entry->name,11)) + break; /* found dupe */ + if(entry1==entry) /* no dupe found */ + break; + + /* use all 8 characters of name */ + if(entry->name[7]==' ') { + int j; + for(j=6;j>0 && entry->name[j]==' ';j--) + entry->name[j]='~'; + } + + /* increment number */ + for(j=7;j>0 && entry->name[j]=='9';j--) + entry->name[j]='0'; + if(j>0) { + if(entry->name[j]<'0' || entry->name[j]>'9') + entry->name[j]='0'; + else + entry->name[j]++; + } + } + + /* calculate checksum; propagate to long name */ + if(entry_long) { + uint8_t chksum=fat_chksum(entry); + + /* calculate anew, because realloc could have taken place */ + entry_long=array_get(&(s->directory),long_index); + while(entry_longreserved[1]=chksum; + entry_long++; + } + } + + return entry; +} + +/* + * Read a directory. (the index of the corresponding mapping must be passed). + */ +static int read_directory(BDRVVVFATState* s, int mapping_index) +{ + mapping_t* mapping = array_get(&(s->mapping), mapping_index); + direntry_t* direntry; + const char* dirname = mapping->path; + int first_cluster = mapping->begin; + int parent_index = mapping->info.dir.parent_mapping_index; + mapping_t* parent_mapping = (mapping_t*) + (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : NULL); + int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1; + + DIR* dir=opendir(dirname); + struct dirent* entry; + int i; + + assert(mapping->mode & MODE_DIRECTORY); + + if(!dir) { + mapping->end = mapping->begin; + return -1; + } + + i = mapping->info.dir.first_dir_index = + first_cluster == 0 ? 0 : s->directory.next; + + /* actually read the directory, and allocate the mappings */ + while((entry=readdir(dir))) { + unsigned int length=strlen(dirname)+2+strlen(entry->d_name); + char* buffer; + direntry_t* direntry; + struct stat st; + int is_dot=!strcmp(entry->d_name,"."); + int is_dotdot=!strcmp(entry->d_name,".."); + + if(first_cluster == 0 && (is_dotdot || is_dot)) + continue; + + buffer = g_malloc(length); + snprintf(buffer,length,"%s/%s",dirname,entry->d_name); + + if(stat(buffer,&st)<0) { + g_free(buffer); + continue; + } + + /* create directory entry for this file */ + direntry=create_short_and_long_name(s, i, entry->d_name, + is_dot || is_dotdot); + direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20); + direntry->reserved[0]=direntry->reserved[1]=0; + direntry->ctime=fat_datetime(st.st_ctime,1); + direntry->cdate=fat_datetime(st.st_ctime,0); + direntry->adate=fat_datetime(st.st_atime,0); + direntry->begin_hi=0; + direntry->mtime=fat_datetime(st.st_mtime,1); + direntry->mdate=fat_datetime(st.st_mtime,0); + if(is_dotdot) + set_begin_of_direntry(direntry, first_cluster_of_parent); + else if(is_dot) + set_begin_of_direntry(direntry, first_cluster); + else + direntry->begin=0; /* do that later */ + if (st.st_size > 0x7fffffff) { + fprintf(stderr, "File %s is larger than 2GB\n", buffer); + g_free(buffer); + closedir(dir); + return -2; + } + direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size); + + /* create mapping for this file */ + if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) { + s->current_mapping = array_get_next(&(s->mapping)); + s->current_mapping->begin=0; + s->current_mapping->end=st.st_size; + /* + * we get the direntry of the most recent direntry, which + * contains the short name and all the relevant information. + */ + s->current_mapping->dir_index=s->directory.next-1; + s->current_mapping->first_mapping_index = -1; + if (S_ISDIR(st.st_mode)) { + s->current_mapping->mode = MODE_DIRECTORY; + s->current_mapping->info.dir.parent_mapping_index = + mapping_index; + } else { + s->current_mapping->mode = MODE_UNDEFINED; + s->current_mapping->info.file.offset = 0; + } + s->current_mapping->path=buffer; + s->current_mapping->read_only = + (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0; + } else { + g_free(buffer); + } + } + closedir(dir); + + /* fill with zeroes up to the end of the cluster */ + while(s->directory.next%(0x10*s->sectors_per_cluster)) { + direntry_t* direntry=array_get_next(&(s->directory)); + memset(direntry,0,sizeof(direntry_t)); + } + +/* TODO: if there are more entries, bootsector has to be adjusted! */ +#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster) + if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) { + /* root directory */ + int cur = s->directory.next; + array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1); + s->directory.next = ROOT_ENTRIES; + memset(array_get(&(s->directory), cur), 0, + (ROOT_ENTRIES - cur) * sizeof(direntry_t)); + } + + /* reget the mapping, since s->mapping was possibly realloc()ed */ + mapping = array_get(&(s->mapping), mapping_index); + first_cluster += (s->directory.next - mapping->info.dir.first_dir_index) + * 0x20 / s->cluster_size; + mapping->end = first_cluster; + + direntry = array_get(&(s->directory), mapping->dir_index); + set_begin_of_direntry(direntry, mapping->begin); + + return 0; +} + +static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num) +{ + return (sector_num-s->faked_sectors)/s->sectors_per_cluster; +} + +static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num) +{ + return s->faked_sectors + s->sectors_per_cluster * cluster_num; +} + +static int init_directories(BDRVVVFATState* s, + const char *dirname, int heads, int secs, + Error **errp) +{ + bootsector_t* bootsector; + mapping_t* mapping; + unsigned int i; + unsigned int cluster; + + memset(&(s->first_sectors[0]),0,0x40*0x200); + + s->cluster_size=s->sectors_per_cluster*0x200; + s->cluster_buffer=g_malloc(s->cluster_size); + + /* + * The formula: sc = spf+1+spf*spc*(512*8/fat_type), + * where sc is sector_count, + * spf is sectors_per_fat, + * spc is sectors_per_clusters, and + * fat_type = 12, 16 or 32. + */ + i = 1+s->sectors_per_cluster*0x200*8/s->fat_type; + s->sectors_per_fat=(s->sector_count+i)/i; /* round up */ + + array_init(&(s->mapping),sizeof(mapping_t)); + array_init(&(s->directory),sizeof(direntry_t)); + + /* add volume label */ + { + direntry_t* entry=array_get_next(&(s->directory)); + entry->attributes=0x28; /* archive | volume label */ + memcpy(entry->name, "QEMU VVFAT ", sizeof(entry->name)); + } + + /* Now build FAT, and write back information into directory */ + init_fat(s); + + s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2; + s->cluster_count=sector2cluster(s, s->sector_count); + + mapping = array_get_next(&(s->mapping)); + mapping->begin = 0; + mapping->dir_index = 0; + mapping->info.dir.parent_mapping_index = -1; + mapping->first_mapping_index = -1; + mapping->path = g_strdup(dirname); + i = strlen(mapping->path); + if (i > 0 && mapping->path[i - 1] == '/') + mapping->path[i - 1] = '\0'; + mapping->mode = MODE_DIRECTORY; + mapping->read_only = 0; + s->path = mapping->path; + + for (i = 0, cluster = 0; i < s->mapping.next; i++) { + /* MS-DOS expects the FAT to be 0 for the root directory + * (except for the media byte). */ + /* LATER TODO: still true for FAT32? */ + int fix_fat = (i != 0); + mapping = array_get(&(s->mapping), i); + + if (mapping->mode & MODE_DIRECTORY) { + mapping->begin = cluster; + if(read_directory(s, i)) { + error_setg(errp, "Could not read directory %s", + mapping->path); + return -1; + } + mapping = array_get(&(s->mapping), i); + } else { + assert(mapping->mode == MODE_UNDEFINED); + mapping->mode=MODE_NORMAL; + mapping->begin = cluster; + if (mapping->end > 0) { + direntry_t* direntry = array_get(&(s->directory), + mapping->dir_index); + + mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size; + set_begin_of_direntry(direntry, mapping->begin); + } else { + mapping->end = cluster + 1; + fix_fat = 0; + } + } + + assert(mapping->begin < mapping->end); + + /* next free cluster */ + cluster = mapping->end; + + if(cluster > s->cluster_count) { + error_setg(errp, + "Directory does not fit in FAT%d (capacity %.2f MB)", + s->fat_type, s->sector_count / 2000.0); + return -1; + } + + /* fix fat for entry */ + if (fix_fat) { + int j; + for(j = mapping->begin; j < mapping->end - 1; j++) + fat_set(s, j, j+1); + fat_set(s, mapping->end - 1, s->max_fat_value); + } + } + + mapping = array_get(&(s->mapping), 0); + s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster; + s->last_cluster_of_root_directory = mapping->end; + + /* the FAT signature */ + fat_set(s,0,s->max_fat_value); + fat_set(s,1,s->max_fat_value); + + s->current_mapping = NULL; + + bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200); + bootsector->jump[0]=0xeb; + bootsector->jump[1]=0x3e; + bootsector->jump[2]=0x90; + memcpy(bootsector->name,"QEMU ",8); + bootsector->sector_size=cpu_to_le16(0x200); + bootsector->sectors_per_cluster=s->sectors_per_cluster; + bootsector->reserved_sectors=cpu_to_le16(1); + bootsector->number_of_fats=0x2; /* number of FATs */ + bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10); + bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count); + bootsector->media_type=(s->first_sectors_number>1?0xf8:0xf0); /* media descriptor (f8=hd, f0=3.5 fd)*/ + s->fat.pointer[0] = bootsector->media_type; + bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat); + bootsector->sectors_per_track = cpu_to_le16(secs); + bootsector->number_of_heads = cpu_to_le16(heads); + bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f); + bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0); + + /* LATER TODO: if FAT32, this is wrong */ + bootsector->u.fat16.drive_number=s->first_sectors_number==1?0:0x80; /* fda=0, hda=0x80 */ + bootsector->u.fat16.current_head=0; + bootsector->u.fat16.signature=0x29; + bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd); + + memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11); + memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8); + bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa; + + return 0; +} + +#ifdef DEBUG +static BDRVVVFATState *vvv = NULL; +#endif + +static int enable_write_target(BDRVVVFATState *s, Error **errp); +static int is_consistent(BDRVVVFATState *s); + +static void vvfat_rebind(BlockDriverState *bs) +{ + BDRVVVFATState *s = bs->opaque; + s->bs = bs; +} + +static QemuOptsList runtime_opts = { + .name = "vvfat", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "dir", + .type = QEMU_OPT_STRING, + .help = "Host directory to map to the vvfat device", + }, + { + .name = "fat-type", + .type = QEMU_OPT_NUMBER, + .help = "FAT type (12, 16 or 32)", + }, + { + .name = "floppy", + .type = QEMU_OPT_BOOL, + .help = "Create a floppy rather than a hard disk image", + }, + { + .name = "rw", + .type = QEMU_OPT_BOOL, + .help = "Make the image writable", + }, + { /* end of list */ } + }, +}; + +static void vvfat_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + int fat_type = 0; + bool floppy = false; + bool rw = false; + int i; + + if (!strstart(filename, "fat:", NULL)) { + error_setg(errp, "File name string must start with 'fat:'"); + return; + } + + /* Parse options */ + if (strstr(filename, ":32:")) { + fat_type = 32; + } else if (strstr(filename, ":16:")) { + fat_type = 16; + } else if (strstr(filename, ":12:")) { + fat_type = 12; + } + + if (strstr(filename, ":floppy:")) { + floppy = true; + } + + if (strstr(filename, ":rw:")) { + rw = true; + } + + /* Get the directory name without options */ + i = strrchr(filename, ':') - filename; + assert(i >= 3); + if (filename[i - 2] == ':' && qemu_isalpha(filename[i - 1])) { + /* workaround for DOS drive names */ + filename += i - 1; + } else { + filename += i + 1; + } + + /* Fill in the options QDict */ + qdict_put(options, "dir", qstring_from_str(filename)); + qdict_put(options, "fat-type", qint_from_int(fat_type)); + qdict_put(options, "floppy", qbool_from_int(floppy)); + qdict_put(options, "rw", qbool_from_int(rw)); +} + +static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVVVFATState *s = bs->opaque; + int cyls, heads, secs; + bool floppy; + const char *dirname; + QemuOpts *opts; + Error *local_err = NULL; + int ret; + +#ifdef DEBUG + vvv = s; +#endif + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + dirname = qemu_opt_get(opts, "dir"); + if (!dirname) { + error_setg(errp, "vvfat block driver requires a 'dir' option"); + ret = -EINVAL; + goto fail; + } + + s->fat_type = qemu_opt_get_number(opts, "fat-type", 0); + floppy = qemu_opt_get_bool(opts, "floppy", false); + + if (floppy) { + /* 1.44MB or 2.88MB floppy. 2.88MB can be FAT12 (default) or FAT16. */ + if (!s->fat_type) { + s->fat_type = 12; + secs = 36; + s->sectors_per_cluster = 2; + } else { + secs = s->fat_type == 12 ? 18 : 36; + s->sectors_per_cluster = 1; + } + s->first_sectors_number = 1; + cyls = 80; + heads = 2; + } else { + /* 32MB or 504MB disk*/ + if (!s->fat_type) { + s->fat_type = 16; + } + s->first_sectors_number = 0x40; + cyls = s->fat_type == 12 ? 64 : 1024; + heads = 16; + secs = 63; + } + + switch (s->fat_type) { + case 32: + fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. " + "You are welcome to do so!\n"); + break; + case 16: + case 12: + break; + default: + error_setg(errp, "Valid FAT types are only 12, 16 and 32"); + ret = -EINVAL; + goto fail; + } + + + s->bs = bs; + + /* LATER TODO: if FAT32, adjust */ + s->sectors_per_cluster=0x10; + + s->current_cluster=0xffffffff; + + /* read only is the default for safety */ + bs->read_only = 1; + s->qcow = s->write_target = NULL; + s->qcow_filename = NULL; + s->fat2 = NULL; + s->downcase_short_names = 1; + + fprintf(stderr, "vvfat %s chs %d,%d,%d\n", + dirname, cyls, heads, secs); + + s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1); + + if (qemu_opt_get_bool(opts, "rw", false)) { + ret = enable_write_target(s, errp); + if (ret < 0) { + goto fail; + } + bs->read_only = 0; + } + + bs->total_sectors = cyls * heads * secs; + + if (init_directories(s, dirname, heads, secs, errp)) { + ret = -EIO; + goto fail; + } + + s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count; + + if (s->first_sectors_number == 0x40) { + init_mbr(s, cyls, heads, secs); + } + + // assert(is_consistent(s)); + qemu_co_mutex_init(&s->lock); + + /* Disable migration when vvfat is used rw */ + if (s->qcow) { + error_setg(&s->migration_blocker, + "The vvfat (rw) format used by node '%s' " + "does not support live migration", + bdrv_get_device_or_node_name(bs)); + migrate_add_blocker(s->migration_blocker); + } + + ret = 0; +fail: + qemu_opts_del(opts); + return ret; +} + +static inline void vvfat_close_current_file(BDRVVVFATState *s) +{ + if(s->current_mapping) { + s->current_mapping = NULL; + if (s->current_fd) { + qemu_close(s->current_fd); + s->current_fd = 0; + } + } + s->current_cluster = -1; +} + +/* mappings between index1 and index2-1 are supposed to be ordered + * return value is the index of the last mapping for which end>cluster_num + */ +static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) +{ + while(1) { + int index3; + mapping_t* mapping; + index3=(index1+index2)/2; + mapping=array_get(&(s->mapping),index3); + assert(mapping->begin < mapping->end); + if(mapping->begin>=cluster_num) { + assert(index2!=index3 || index2==0); + if(index2==index3) + return index1; + index2=index3; + } else { + if(index1==index3) + return mapping->end<=cluster_num ? index2 : index1; + index1=index3; + } + assert(index1<=index2); + DLOG(mapping=array_get(&(s->mapping),index1); + assert(mapping->begin<=cluster_num); + assert(index2 >= s->mapping.next || + ((mapping = array_get(&(s->mapping),index2)) && + mapping->end>cluster_num))); + } +} + +static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num) +{ + int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next); + mapping_t* mapping; + if(index>=s->mapping.next) + return NULL; + mapping=array_get(&(s->mapping),index); + if(mapping->begin>cluster_num) + return NULL; + assert(mapping->begin<=cluster_num && mapping->end>cluster_num); + return mapping; +} + +static int open_file(BDRVVVFATState* s,mapping_t* mapping) +{ + if(!mapping) + return -1; + if(!s->current_mapping || + strcmp(s->current_mapping->path,mapping->path)) { + /* open file */ + int fd = qemu_open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE); + if(fd<0) + return -1; + vvfat_close_current_file(s); + s->current_fd = fd; + s->current_mapping = mapping; + } + return 0; +} + +static inline int read_cluster(BDRVVVFATState *s,int cluster_num) +{ + if(s->current_cluster != cluster_num) { + int result=0; + off_t offset; + assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY)); + if(!s->current_mapping + || s->current_mapping->begin>cluster_num + || s->current_mapping->end<=cluster_num) { + /* binary search of mappings for file */ + mapping_t* mapping=find_mapping_for_cluster(s,cluster_num); + + assert(!mapping || (cluster_num>=mapping->begin && cluster_numend)); + + if (mapping && mapping->mode & MODE_DIRECTORY) { + vvfat_close_current_file(s); + s->current_mapping = mapping; +read_cluster_directory: + offset = s->cluster_size*(cluster_num-s->current_mapping->begin); + s->cluster = (unsigned char*)s->directory.pointer+offset + + 0x20*s->current_mapping->info.dir.first_dir_index; + assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0); + assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size); + s->current_cluster = cluster_num; + return 0; + } + + if(open_file(s,mapping)) + return -2; + } else if (s->current_mapping->mode & MODE_DIRECTORY) + goto read_cluster_directory; + + assert(s->current_fd); + + offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset; + if(lseek(s->current_fd, offset, SEEK_SET)!=offset) + return -3; + s->cluster=s->cluster_buffer; + result=read(s->current_fd,s->cluster,s->cluster_size); + if(result<0) { + s->current_cluster = -1; + return -1; + } + s->current_cluster = cluster_num; + } + return 0; +} + +#ifdef DEBUG +static void print_direntry(const direntry_t* direntry) +{ + int j = 0; + char buffer[1024]; + + fprintf(stderr, "direntry %p: ", direntry); + if(!direntry) + return; + if(is_long_name(direntry)) { + unsigned char* c=(unsigned char*)direntry; + int i; + for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2) +#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;} + ADD_CHAR(c[i]); + for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2) + ADD_CHAR(c[i]); + for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2) + ADD_CHAR(c[i]); + buffer[j] = 0; + fprintf(stderr, "%s\n", buffer); + } else { + int i; + for(i=0;i<11;i++) + ADD_CHAR(direntry->name[i]); + buffer[j] = 0; + fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n", + buffer, + direntry->attributes, + begin_of_direntry(direntry),le32_to_cpu(direntry->size)); + } +} + +static void print_mapping(const mapping_t* mapping) +{ + fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, " + "first_mapping_index = %d, name = %s, mode = 0x%x, " , + mapping, mapping->begin, mapping->end, mapping->dir_index, + mapping->first_mapping_index, mapping->path, mapping->mode); + + if (mapping->mode & MODE_DIRECTORY) + fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); + else + fprintf(stderr, "offset = %d\n", mapping->info.file.offset); +} +#endif + +static int vvfat_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVVVFATState *s = bs->opaque; + int i; + + for(i=0;i= bs->total_sectors) + return -1; + if (s->qcow) { + int n; + if (bdrv_is_allocated(s->qcow, sector_num, nb_sectors-i, &n)) { +DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n)); + if (bdrv_read(s->qcow, sector_num, buf + i*0x200, n)) { + return -1; + } + i += n - 1; + sector_num += n - 1; + continue; + } +DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num)); + } + if(sector_numfaked_sectors) { + if(sector_numfirst_sectors_number) + memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200); + else if(sector_num-s->first_sectors_numbersectors_per_fat) + memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200); + else if(sector_num-s->first_sectors_number-s->sectors_per_fatsectors_per_fat) + memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200); + } else { + uint32_t sector=sector_num-s->faked_sectors, + sector_offset_in_cluster=(sector%s->sectors_per_cluster), + cluster_num=sector/s->sectors_per_cluster; + if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) { + /* LATER TODO: strict: return -1; */ + memset(buf+i*0x200,0,0x200); + continue; + } + memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200); + } + } + return 0; +} + +static coroutine_fn int vvfat_co_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVVVFATState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = vvfat_read(bs, sector_num, buf, nb_sectors); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +/* LATER TODO: statify all functions */ + +/* + * Idea of the write support (use snapshot): + * + * 1. check if all data is consistent, recording renames, modifications, + * new files and directories (in s->commits). + * + * 2. if the data is not consistent, stop committing + * + * 3. handle renames, and create new files and directories (do not yet + * write their contents) + * + * 4. walk the directories, fixing the mapping and direntries, and marking + * the handled mappings as not deleted + * + * 5. commit the contents of the files + * + * 6. handle deleted files and directories + * + */ + +typedef struct commit_t { + char* path; + union { + struct { uint32_t cluster; } rename; + struct { int dir_index; uint32_t modified_offset; } writeout; + struct { uint32_t first_cluster; } new_file; + struct { uint32_t cluster; } mkdir; + } param; + /* DELETEs and RMDIRs are handled differently: see handle_deletes() */ + enum { + ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR + } action; +} commit_t; + +static void clear_commits(BDRVVVFATState* s) +{ + int i; +DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next)); + for (i = 0; i < s->commits.next; i++) { + commit_t* commit = array_get(&(s->commits), i); + assert(commit->path || commit->action == ACTION_WRITEOUT); + if (commit->action != ACTION_WRITEOUT) { + assert(commit->path); + g_free(commit->path); + } else + assert(commit->path == NULL); + } + s->commits.next = 0; +} + +static void schedule_rename(BDRVVVFATState* s, + uint32_t cluster, char* new_path) +{ + commit_t* commit = array_get_next(&(s->commits)); + commit->path = new_path; + commit->param.rename.cluster = cluster; + commit->action = ACTION_RENAME; +} + +static void schedule_writeout(BDRVVVFATState* s, + int dir_index, uint32_t modified_offset) +{ + commit_t* commit = array_get_next(&(s->commits)); + commit->path = NULL; + commit->param.writeout.dir_index = dir_index; + commit->param.writeout.modified_offset = modified_offset; + commit->action = ACTION_WRITEOUT; +} + +static void schedule_new_file(BDRVVVFATState* s, + char* path, uint32_t first_cluster) +{ + commit_t* commit = array_get_next(&(s->commits)); + commit->path = path; + commit->param.new_file.first_cluster = first_cluster; + commit->action = ACTION_NEW_FILE; +} + +static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path) +{ + commit_t* commit = array_get_next(&(s->commits)); + commit->path = path; + commit->param.mkdir.cluster = cluster; + commit->action = ACTION_MKDIR; +} + +typedef struct { + /* + * Since the sequence number is at most 0x3f, and the filename + * length is at most 13 times the sequence number, the maximal + * filename length is 0x3f * 13 bytes. + */ + unsigned char name[0x3f * 13 + 1]; + int checksum, len; + int sequence_number; +} long_file_name; + +static void lfn_init(long_file_name* lfn) +{ + lfn->sequence_number = lfn->len = 0; + lfn->checksum = 0x100; +} + +/* return 0 if parsed successfully, > 0 if no long name, < 0 if error */ +static int parse_long_name(long_file_name* lfn, + const direntry_t* direntry) +{ + int i, j, offset; + const unsigned char* pointer = (const unsigned char*)direntry; + + if (!is_long_name(direntry)) + return 1; + + if (pointer[0] & 0x40) { + lfn->sequence_number = pointer[0] & 0x3f; + lfn->checksum = pointer[13]; + lfn->name[0] = 0; + lfn->name[lfn->sequence_number * 13] = 0; + } else if ((pointer[0] & 0x3f) != --lfn->sequence_number) + return -1; + else if (pointer[13] != lfn->checksum) + return -2; + else if (pointer[12] || pointer[26] || pointer[27]) + return -3; + + offset = 13 * (lfn->sequence_number - 1); + for (i = 0, j = 1; i < 13; i++, j+=2) { + if (j == 11) + j = 14; + else if (j == 26) + j = 28; + + if (pointer[j+1] == 0) + lfn->name[offset + i] = pointer[j]; + else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0) + return -4; + else + lfn->name[offset + i] = 0; + } + + if (pointer[0] & 0x40) + lfn->len = offset + strlen((char*)lfn->name + offset); + + return 0; +} + +/* returns 0 if successful, >0 if no short_name, and <0 on error */ +static int parse_short_name(BDRVVVFATState* s, + long_file_name* lfn, direntry_t* direntry) +{ + int i, j; + + if (!is_short_name(direntry)) + return 1; + + for (j = 7; j >= 0 && direntry->name[j] == ' '; j--); + for (i = 0; i <= j; i++) { + if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f) + return -1; + else if (s->downcase_short_names) + lfn->name[i] = qemu_tolower(direntry->name[i]); + else + lfn->name[i] = direntry->name[i]; + } + + for (j = 2; j >= 0 && direntry->name[8 + j] == ' '; j--) { + } + if (j >= 0) { + lfn->name[i++] = '.'; + lfn->name[i + j + 1] = '\0'; + for (;j >= 0; j--) { + uint8_t c = direntry->name[8 + j]; + if (c <= ' ' || c > 0x7f) { + return -2; + } else if (s->downcase_short_names) { + lfn->name[i + j] = qemu_tolower(c); + } else { + lfn->name[i + j] = c; + } + } + } else + lfn->name[i + j + 1] = '\0'; + + lfn->len = strlen((char*)lfn->name); + + return 0; +} + +static inline uint32_t modified_fat_get(BDRVVVFATState* s, + unsigned int cluster) +{ + if (cluster < s->last_cluster_of_root_directory) { + if (cluster + 1 == s->last_cluster_of_root_directory) + return s->max_fat_value; + else + return cluster + 1; + } + + if (s->fat_type==32) { + uint32_t* entry=((uint32_t*)s->fat2)+cluster; + return le32_to_cpu(*entry); + } else if (s->fat_type==16) { + uint16_t* entry=((uint16_t*)s->fat2)+cluster; + return le16_to_cpu(*entry); + } else { + const uint8_t* x=s->fat2+cluster*3/2; + return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff; + } +} + +static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num) +{ + int was_modified = 0; + int i, dummy; + + if (s->qcow == NULL) + return 0; + + for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) + was_modified = bdrv_is_allocated(s->qcow, + cluster2sector(s, cluster_num) + i, 1, &dummy); + + return was_modified; +} + +static const char* get_basename(const char* path) +{ + char* basename = strrchr(path, '/'); + if (basename == NULL) + return path; + else + return basename + 1; /* strip '/' */ +} + +/* + * The array s->used_clusters holds the states of the clusters. If it is + * part of a file, it has bit 2 set, in case of a directory, bit 1. If it + * was modified, bit 3 is set. + * If any cluster is allocated, but not part of a file or directory, this + * driver refuses to commit. + */ +typedef enum { + USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4 +} used_t; + +/* + * get_cluster_count_for_direntry() not only determines how many clusters + * are occupied by direntry, but also if it was renamed or modified. + * + * A file is thought to be renamed *only* if there already was a file with + * exactly the same first cluster, but a different name. + * + * Further, the files/directories handled by this function are + * assumed to be *not* deleted (and *only* those). + */ +static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, + direntry_t* direntry, const char* path) +{ + /* + * This is a little bit tricky: + * IF the guest OS just inserts a cluster into the file chain, + * and leaves the rest alone, (i.e. the original file had clusters + * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens: + * + * - do_commit will write the cluster into the file at the given + * offset, but + * + * - the cluster which is overwritten should be moved to a later + * position in the file. + * + * I am not aware that any OS does something as braindead, but this + * situation could happen anyway when not committing for a long time. + * Just to be sure that this does not bite us, detect it, and copy the + * contents of the clusters to-be-overwritten into the qcow. + */ + int copy_it = 0; + int was_modified = 0; + int32_t ret = 0; + + uint32_t cluster_num = begin_of_direntry(direntry); + uint32_t offset = 0; + int first_mapping_index = -1; + mapping_t* mapping = NULL; + const char* basename2 = NULL; + + vvfat_close_current_file(s); + + /* the root directory */ + if (cluster_num == 0) + return 0; + + /* write support */ + if (s->qcow) { + basename2 = get_basename(path); + + mapping = find_mapping_for_cluster(s, cluster_num); + + if (mapping) { + const char* basename; + + assert(mapping->mode & MODE_DELETED); + mapping->mode &= ~MODE_DELETED; + + basename = get_basename(mapping->path); + + assert(mapping->mode & MODE_NORMAL); + + /* rename */ + if (strcmp(basename, basename2)) + schedule_rename(s, cluster_num, g_strdup(path)); + } else if (is_file(direntry)) + /* new file */ + schedule_new_file(s, g_strdup(path), cluster_num); + else { + abort(); + return 0; + } + } + + while(1) { + if (s->qcow) { + if (!copy_it && cluster_was_modified(s, cluster_num)) { + if (mapping == NULL || + mapping->begin > cluster_num || + mapping->end <= cluster_num) + mapping = find_mapping_for_cluster(s, cluster_num); + + + if (mapping && + (mapping->mode & MODE_DIRECTORY) == 0) { + + /* was modified in qcow */ + if (offset != mapping->info.file.offset + s->cluster_size + * (cluster_num - mapping->begin)) { + /* offset of this cluster in file chain has changed */ + abort(); + copy_it = 1; + } else if (offset == 0) { + const char* basename = get_basename(mapping->path); + + if (strcmp(basename, basename2)) + copy_it = 1; + first_mapping_index = array_index(&(s->mapping), mapping); + } + + if (mapping->first_mapping_index != first_mapping_index + && mapping->info.file.offset > 0) { + abort(); + copy_it = 1; + } + + /* need to write out? */ + if (!was_modified && is_file(direntry)) { + was_modified = 1; + schedule_writeout(s, mapping->dir_index, offset); + } + } + } + + if (copy_it) { + int i, dummy; + /* + * This is horribly inefficient, but that is okay, since + * it is rarely executed, if at all. + */ + int64_t offset = cluster2sector(s, cluster_num); + + vvfat_close_current_file(s); + for (i = 0; i < s->sectors_per_cluster; i++) { + if (!bdrv_is_allocated(s->qcow, offset + i, 1, &dummy)) { + if (vvfat_read(s->bs, offset, s->cluster_buffer, 1)) { + return -1; + } + if (bdrv_write(s->qcow, offset, s->cluster_buffer, 1)) { + return -2; + } + } + } + } + } + + ret++; + if (s->used_clusters[cluster_num] & USED_ANY) + return 0; + s->used_clusters[cluster_num] = USED_FILE; + + cluster_num = modified_fat_get(s, cluster_num); + + if (fat_eof(s, cluster_num)) + return ret; + else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16) + return -1; + + offset += s->cluster_size; + } +} + +/* + * This function looks at the modified data (qcow). + * It returns 0 upon inconsistency or error, and the number of clusters + * used by the directory, its subdirectories and their files. + */ +static int check_directory_consistency(BDRVVVFATState *s, + int cluster_num, const char* path) +{ + int ret = 0; + unsigned char* cluster = g_malloc(s->cluster_size); + direntry_t* direntries = (direntry_t*)cluster; + mapping_t* mapping = find_mapping_for_cluster(s, cluster_num); + + long_file_name lfn; + int path_len = strlen(path); + char path2[PATH_MAX + 1]; + + assert(path_len < PATH_MAX); /* len was tested before! */ + pstrcpy(path2, sizeof(path2), path); + path2[path_len] = '/'; + path2[path_len + 1] = '\0'; + + if (mapping) { + const char* basename = get_basename(mapping->path); + const char* basename2 = get_basename(path); + + assert(mapping->mode & MODE_DIRECTORY); + + assert(mapping->mode & MODE_DELETED); + mapping->mode &= ~MODE_DELETED; + + if (strcmp(basename, basename2)) + schedule_rename(s, cluster_num, g_strdup(path)); + } else + /* new directory */ + schedule_mkdir(s, cluster_num, g_strdup(path)); + + lfn_init(&lfn); + do { + int i; + int subret = 0; + + ret++; + + if (s->used_clusters[cluster_num] & USED_ANY) { + fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num); + goto fail; + } + s->used_clusters[cluster_num] = USED_DIRECTORY; + +DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num))); + subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster, + s->sectors_per_cluster); + if (subret) { + fprintf(stderr, "Error fetching direntries\n"); + fail: + g_free(cluster); + return 0; + } + + for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) { + int cluster_count = 0; + +DLOG(fprintf(stderr, "check direntry %d:\n", i); print_direntry(direntries + i)); + if (is_volume_label(direntries + i) || is_dot(direntries + i) || + is_free(direntries + i)) + continue; + + subret = parse_long_name(&lfn, direntries + i); + if (subret < 0) { + fprintf(stderr, "Error in long name\n"); + goto fail; + } + if (subret == 0 || is_free(direntries + i)) + continue; + + if (fat_chksum(direntries+i) != lfn.checksum) { + subret = parse_short_name(s, &lfn, direntries + i); + if (subret < 0) { + fprintf(stderr, "Error in short name (%d)\n", subret); + goto fail; + } + if (subret > 0 || !strcmp((char*)lfn.name, ".") + || !strcmp((char*)lfn.name, "..")) + continue; + } + lfn.checksum = 0x100; /* cannot use long name twice */ + + if (path_len + 1 + lfn.len >= PATH_MAX) { + fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name); + goto fail; + } + pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1, + (char*)lfn.name); + + if (is_directory(direntries + i)) { + if (begin_of_direntry(direntries + i) == 0) { + DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i)); + goto fail; + } + cluster_count = check_directory_consistency(s, + begin_of_direntry(direntries + i), path2); + if (cluster_count == 0) { + DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i)); + goto fail; + } + } else if (is_file(direntries + i)) { + /* check file size with FAT */ + cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2); + if (cluster_count != + (le32_to_cpu(direntries[i].size) + s->cluster_size + - 1) / s->cluster_size) { + DLOG(fprintf(stderr, "Cluster count mismatch\n")); + goto fail; + } + } else + abort(); /* cluster_count = 0; */ + + ret += cluster_count; + } + + cluster_num = modified_fat_get(s, cluster_num); + } while(!fat_eof(s, cluster_num)); + + g_free(cluster); + return ret; +} + +/* returns 1 on success */ +static int is_consistent(BDRVVVFATState* s) +{ + int i, check; + int used_clusters_count = 0; + +DLOG(checkpoint()); + /* + * - get modified FAT + * - compare the two FATs (TODO) + * - get buffer for marking used clusters + * - recurse direntries from root (using bs->bdrv_read to make + * sure to get the new data) + * - check that the FAT agrees with the size + * - count the number of clusters occupied by this directory and + * its files + * - check that the cumulative used cluster count agrees with the + * FAT + * - if all is fine, return number of used clusters + */ + if (s->fat2 == NULL) { + int size = 0x200 * s->sectors_per_fat; + s->fat2 = g_malloc(size); + memcpy(s->fat2, s->fat.pointer, size); + } + check = vvfat_read(s->bs, + s->first_sectors_number, s->fat2, s->sectors_per_fat); + if (check) { + fprintf(stderr, "Could not copy fat\n"); + return 0; + } + assert (s->used_clusters); + for (i = 0; i < sector2cluster(s, s->sector_count); i++) + s->used_clusters[i] &= ~USED_ANY; + + clear_commits(s); + + /* mark every mapped file/directory as deleted. + * (check_directory_consistency() will unmark those still present). */ + if (s->qcow) + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->first_mapping_index < 0) + mapping->mode |= MODE_DELETED; + } + + used_clusters_count = check_directory_consistency(s, 0, s->path); + if (used_clusters_count <= 0) { + DLOG(fprintf(stderr, "problem in directory\n")); + return 0; + } + + check = s->last_cluster_of_root_directory; + for (i = check; i < sector2cluster(s, s->sector_count); i++) { + if (modified_fat_get(s, i)) { + if(!s->used_clusters[i]) { + DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i)); + return 0; + } + check++; + } + + if (s->used_clusters[i] == USED_ALLOCATED) { + /* allocated, but not used... */ + DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i)); + return 0; + } + } + + if (check != used_clusters_count) + return 0; + + return used_clusters_count; +} + +static inline void adjust_mapping_indices(BDRVVVFATState* s, + int offset, int adjust) +{ + int i; + + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + +#define ADJUST_MAPPING_INDEX(name) \ + if (mapping->name >= offset) \ + mapping->name += adjust + + ADJUST_MAPPING_INDEX(first_mapping_index); + if (mapping->mode & MODE_DIRECTORY) + ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index); + } +} + +/* insert or update mapping */ +static mapping_t* insert_mapping(BDRVVVFATState* s, + uint32_t begin, uint32_t end) +{ + /* + * - find mapping where mapping->begin >= begin, + * - if mapping->begin > begin: insert + * - adjust all references to mappings! + * - else: adjust + * - replace name + */ + int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next); + mapping_t* mapping = NULL; + mapping_t* first_mapping = array_get(&(s->mapping), 0); + + if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index)) + && mapping->begin < begin) { + mapping->end = begin; + index++; + mapping = array_get(&(s->mapping), index); + } + if (index >= s->mapping.next || mapping->begin > begin) { + mapping = array_insert(&(s->mapping), index, 1); + mapping->path = NULL; + adjust_mapping_indices(s, index, +1); + } + + mapping->begin = begin; + mapping->end = end; + +DLOG(mapping_t* next_mapping; +assert(index + 1 >= s->mapping.next || +((next_mapping = array_get(&(s->mapping), index + 1)) && + next_mapping->begin >= end))); + + if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer) + s->current_mapping = array_get(&(s->mapping), + s->current_mapping - first_mapping); + + return mapping; +} + +static int remove_mapping(BDRVVVFATState* s, int mapping_index) +{ + mapping_t* mapping = array_get(&(s->mapping), mapping_index); + mapping_t* first_mapping = array_get(&(s->mapping), 0); + + /* free mapping */ + if (mapping->first_mapping_index < 0) { + g_free(mapping->path); + } + + /* remove from s->mapping */ + array_remove(&(s->mapping), mapping_index); + + /* adjust all references to mappings */ + adjust_mapping_indices(s, mapping_index, -1); + + if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer) + s->current_mapping = array_get(&(s->mapping), + s->current_mapping - first_mapping); + + return 0; +} + +static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust) +{ + int i; + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->dir_index >= offset) + mapping->dir_index += adjust; + if ((mapping->mode & MODE_DIRECTORY) && + mapping->info.dir.first_dir_index >= offset) + mapping->info.dir.first_dir_index += adjust; + } +} + +static direntry_t* insert_direntries(BDRVVVFATState* s, + int dir_index, int count) +{ + /* + * make room in s->directory, + * adjust_dirindices + */ + direntry_t* result = array_insert(&(s->directory), dir_index, count); + if (result == NULL) + return NULL; + adjust_dirindices(s, dir_index, count); + return result; +} + +static int remove_direntries(BDRVVVFATState* s, int dir_index, int count) +{ + int ret = array_remove_slice(&(s->directory), dir_index, count); + if (ret) + return ret; + adjust_dirindices(s, dir_index, -count); + return 0; +} + +/* + * Adapt the mappings of the cluster chain starting at first cluster + * (i.e. if a file starts at first_cluster, the chain is followed according + * to the modified fat, and the corresponding entries in s->mapping are + * adjusted) + */ +static int commit_mappings(BDRVVVFATState* s, + uint32_t first_cluster, int dir_index) +{ + mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); + direntry_t* direntry = array_get(&(s->directory), dir_index); + uint32_t cluster = first_cluster; + + vvfat_close_current_file(s); + + assert(mapping); + assert(mapping->begin == first_cluster); + mapping->first_mapping_index = -1; + mapping->dir_index = dir_index; + mapping->mode = (dir_index <= 0 || is_directory(direntry)) ? + MODE_DIRECTORY : MODE_NORMAL; + + while (!fat_eof(s, cluster)) { + uint32_t c, c1; + + for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1; + c = c1, c1 = modified_fat_get(s, c1)); + + c++; + if (c > mapping->end) { + int index = array_index(&(s->mapping), mapping); + int i, max_i = s->mapping.next - index; + for (i = 1; i < max_i && mapping[i].begin < c; i++); + while (--i > 0) + remove_mapping(s, index + 1); + } + assert(mapping == array_get(&(s->mapping), s->mapping.next - 1) + || mapping[1].begin >= c); + mapping->end = c; + + if (!fat_eof(s, c1)) { + int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next); + mapping_t* next_mapping = i >= s->mapping.next ? NULL : + array_get(&(s->mapping), i); + + if (next_mapping == NULL || next_mapping->begin > c1) { + int i1 = array_index(&(s->mapping), mapping); + + next_mapping = insert_mapping(s, c1, c1+1); + + if (c1 < c) + i1++; + mapping = array_get(&(s->mapping), i1); + } + + next_mapping->dir_index = mapping->dir_index; + next_mapping->first_mapping_index = + mapping->first_mapping_index < 0 ? + array_index(&(s->mapping), mapping) : + mapping->first_mapping_index; + next_mapping->path = mapping->path; + next_mapping->mode = mapping->mode; + next_mapping->read_only = mapping->read_only; + if (mapping->mode & MODE_DIRECTORY) { + next_mapping->info.dir.parent_mapping_index = + mapping->info.dir.parent_mapping_index; + next_mapping->info.dir.first_dir_index = + mapping->info.dir.first_dir_index + + 0x10 * s->sectors_per_cluster * + (mapping->end - mapping->begin); + } else + next_mapping->info.file.offset = mapping->info.file.offset + + mapping->end - mapping->begin; + + mapping = next_mapping; + } + + cluster = c1; + } + + return 0; +} + +static int commit_direntries(BDRVVVFATState* s, + int dir_index, int parent_mapping_index) +{ + direntry_t* direntry = array_get(&(s->directory), dir_index); + uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry); + mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); + + int factor = 0x10 * s->sectors_per_cluster; + int old_cluster_count, new_cluster_count; + int current_dir_index = mapping->info.dir.first_dir_index; + int first_dir_index = current_dir_index; + int ret, i; + uint32_t c; + +DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index)); + + assert(direntry); + assert(mapping); + assert(mapping->begin == first_cluster); + assert(mapping->info.dir.first_dir_index < s->directory.next); + assert(mapping->mode & MODE_DIRECTORY); + assert(dir_index == 0 || is_directory(direntry)); + + mapping->info.dir.parent_mapping_index = parent_mapping_index; + + if (first_cluster == 0) { + old_cluster_count = new_cluster_count = + s->last_cluster_of_root_directory; + } else { + for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c); + c = fat_get(s, c)) + old_cluster_count++; + + for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c); + c = modified_fat_get(s, c)) + new_cluster_count++; + } + + if (new_cluster_count > old_cluster_count) { + if (insert_direntries(s, + current_dir_index + factor * old_cluster_count, + factor * (new_cluster_count - old_cluster_count)) == NULL) + return -1; + } else if (new_cluster_count < old_cluster_count) + remove_direntries(s, + current_dir_index + factor * new_cluster_count, + factor * (old_cluster_count - new_cluster_count)); + + for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) { + void* direntry = array_get(&(s->directory), current_dir_index); + int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry, + s->sectors_per_cluster); + if (ret) + return ret; + assert(!strncmp(s->directory.pointer, "QEMU", 4)); + current_dir_index += factor; + } + + ret = commit_mappings(s, first_cluster, dir_index); + if (ret) + return ret; + + /* recurse */ + for (i = 0; i < factor * new_cluster_count; i++) { + direntry = array_get(&(s->directory), first_dir_index + i); + if (is_directory(direntry) && !is_dot(direntry)) { + mapping = find_mapping_for_cluster(s, first_cluster); + assert(mapping->mode & MODE_DIRECTORY); + ret = commit_direntries(s, first_dir_index + i, + array_index(&(s->mapping), mapping)); + if (ret) + return ret; + } + } + + return 0; +} + +/* commit one file (adjust contents, adjust mapping), + return first_mapping_index */ +static int commit_one_file(BDRVVVFATState* s, + int dir_index, uint32_t offset) +{ + direntry_t* direntry = array_get(&(s->directory), dir_index); + uint32_t c = begin_of_direntry(direntry); + uint32_t first_cluster = c; + mapping_t* mapping = find_mapping_for_cluster(s, c); + uint32_t size = filesize_of_direntry(direntry); + char* cluster = g_malloc(s->cluster_size); + uint32_t i; + int fd = 0; + + assert(offset < size); + assert((offset % s->cluster_size) == 0); + + for (i = s->cluster_size; i < offset; i += s->cluster_size) + c = modified_fat_get(s, c); + + fd = qemu_open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666); + if (fd < 0) { + fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path, + strerror(errno), errno); + g_free(cluster); + return fd; + } + if (offset > 0) { + if (lseek(fd, offset, SEEK_SET) != offset) { + qemu_close(fd); + g_free(cluster); + return -3; + } + } + + while (offset < size) { + uint32_t c1; + int rest_size = (size - offset > s->cluster_size ? + s->cluster_size : size - offset); + int ret; + + c1 = modified_fat_get(s, c); + + assert((size - offset == 0 && fat_eof(s, c)) || + (size > offset && c >=2 && !fat_eof(s, c))); + + ret = vvfat_read(s->bs, cluster2sector(s, c), + (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200); + + if (ret < 0) { + qemu_close(fd); + g_free(cluster); + return ret; + } + + if (write(fd, cluster, rest_size) < 0) { + qemu_close(fd); + g_free(cluster); + return -2; + } + + offset += rest_size; + c = c1; + } + + if (ftruncate(fd, size)) { + perror("ftruncate()"); + qemu_close(fd); + g_free(cluster); + return -4; + } + qemu_close(fd); + g_free(cluster); + + return commit_mappings(s, first_cluster, dir_index); +} + +#ifdef DEBUG +/* test, if all mappings point to valid direntries */ +static void check1(BDRVVVFATState* s) +{ + int i; + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->mode & MODE_DELETED) { + fprintf(stderr, "deleted\n"); + continue; + } + assert(mapping->dir_index < s->directory.next); + direntry_t* direntry = array_get(&(s->directory), mapping->dir_index); + assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0); + if (mapping->mode & MODE_DIRECTORY) { + assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next); + assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0); + } + } +} + +/* test, if all direntries have mappings */ +static void check2(BDRVVVFATState* s) +{ + int i; + int first_mapping = -1; + + for (i = 0; i < s->directory.next; i++) { + direntry_t* direntry = array_get(&(s->directory), i); + + if (is_short_name(direntry) && begin_of_direntry(direntry)) { + mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry)); + assert(mapping); + assert(mapping->dir_index == i || is_dot(direntry)); + assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry)); + } + + if ((i % (0x10 * s->sectors_per_cluster)) == 0) { + /* cluster start */ + int j, count = 0; + + for (j = 0; j < s->mapping.next; j++) { + mapping_t* mapping = array_get(&(s->mapping), j); + if (mapping->mode & MODE_DELETED) + continue; + if (mapping->mode & MODE_DIRECTORY) { + if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) { + assert(++count == 1); + if (mapping->first_mapping_index == -1) + first_mapping = array_index(&(s->mapping), mapping); + else + assert(first_mapping == mapping->first_mapping_index); + if (mapping->info.dir.parent_mapping_index < 0) + assert(j == 0); + else { + mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index); + assert(parent->mode & MODE_DIRECTORY); + assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index); + } + } + } + } + if (count == 0) + first_mapping = -1; + } + } +} +#endif + +static int handle_renames_and_mkdirs(BDRVVVFATState* s) +{ + int i; + +#ifdef DEBUG + fprintf(stderr, "handle_renames\n"); + for (i = 0; i < s->commits.next; i++) { + commit_t* commit = array_get(&(s->commits), i); + fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action); + } +#endif + + for (i = 0; i < s->commits.next;) { + commit_t* commit = array_get(&(s->commits), i); + if (commit->action == ACTION_RENAME) { + mapping_t* mapping = find_mapping_for_cluster(s, + commit->param.rename.cluster); + char* old_path = mapping->path; + + assert(commit->path); + mapping->path = commit->path; + if (rename(old_path, mapping->path)) + return -2; + + if (mapping->mode & MODE_DIRECTORY) { + int l1 = strlen(mapping->path); + int l2 = strlen(old_path); + int diff = l1 - l2; + direntry_t* direntry = array_get(&(s->directory), + mapping->info.dir.first_dir_index); + uint32_t c = mapping->begin; + int i = 0; + + /* recurse */ + while (!fat_eof(s, c)) { + do { + direntry_t* d = direntry + i; + + if (is_file(d) || (is_directory(d) && !is_dot(d))) { + mapping_t* m = find_mapping_for_cluster(s, + begin_of_direntry(d)); + int l = strlen(m->path); + char* new_path = g_malloc(l + diff + 1); + + assert(!strncmp(m->path, mapping->path, l2)); + + pstrcpy(new_path, l + diff + 1, mapping->path); + pstrcpy(new_path + l1, l + diff + 1 - l1, + m->path + l2); + + schedule_rename(s, m->begin, new_path); + } + i++; + } while((i % (0x10 * s->sectors_per_cluster)) != 0); + c = fat_get(s, c); + } + } + + g_free(old_path); + array_remove(&(s->commits), i); + continue; + } else if (commit->action == ACTION_MKDIR) { + mapping_t* mapping; + int j, parent_path_len; + +#ifdef __MINGW32__ + if (mkdir(commit->path)) + return -5; +#else + if (mkdir(commit->path, 0755)) + return -5; +#endif + + mapping = insert_mapping(s, commit->param.mkdir.cluster, + commit->param.mkdir.cluster + 1); + if (mapping == NULL) + return -6; + + mapping->mode = MODE_DIRECTORY; + mapping->read_only = 0; + mapping->path = commit->path; + j = s->directory.next; + assert(j); + insert_direntries(s, s->directory.next, + 0x10 * s->sectors_per_cluster); + mapping->info.dir.first_dir_index = j; + + parent_path_len = strlen(commit->path) + - strlen(get_basename(commit->path)) - 1; + for (j = 0; j < s->mapping.next; j++) { + mapping_t* m = array_get(&(s->mapping), j); + if (m->first_mapping_index < 0 && m != mapping && + !strncmp(m->path, mapping->path, parent_path_len) && + strlen(m->path) == parent_path_len) + break; + } + assert(j < s->mapping.next); + mapping->info.dir.parent_mapping_index = j; + + array_remove(&(s->commits), i); + continue; + } + + i++; + } + return 0; +} + +/* + * TODO: make sure that the short name is not matching *another* file + */ +static int handle_commits(BDRVVVFATState* s) +{ + int i, fail = 0; + + vvfat_close_current_file(s); + + for (i = 0; !fail && i < s->commits.next; i++) { + commit_t* commit = array_get(&(s->commits), i); + switch(commit->action) { + case ACTION_RENAME: case ACTION_MKDIR: + abort(); + fail = -2; + break; + case ACTION_WRITEOUT: { +#ifndef NDEBUG + /* these variables are only used by assert() below */ + direntry_t* entry = array_get(&(s->directory), + commit->param.writeout.dir_index); + uint32_t begin = begin_of_direntry(entry); + mapping_t* mapping = find_mapping_for_cluster(s, begin); +#endif + + assert(mapping); + assert(mapping->begin == begin); + assert(commit->path == NULL); + + if (commit_one_file(s, commit->param.writeout.dir_index, + commit->param.writeout.modified_offset)) + fail = -3; + + break; + } + case ACTION_NEW_FILE: { + int begin = commit->param.new_file.first_cluster; + mapping_t* mapping = find_mapping_for_cluster(s, begin); + direntry_t* entry; + int i; + + /* find direntry */ + for (i = 0; i < s->directory.next; i++) { + entry = array_get(&(s->directory), i); + if (is_file(entry) && begin_of_direntry(entry) == begin) + break; + } + + if (i >= s->directory.next) { + fail = -6; + continue; + } + + /* make sure there exists an initial mapping */ + if (mapping && mapping->begin != begin) { + mapping->end = begin; + mapping = NULL; + } + if (mapping == NULL) { + mapping = insert_mapping(s, begin, begin+1); + } + /* most members will be fixed in commit_mappings() */ + assert(commit->path); + mapping->path = commit->path; + mapping->read_only = 0; + mapping->mode = MODE_NORMAL; + mapping->info.file.offset = 0; + + if (commit_one_file(s, i, 0)) + fail = -7; + + break; + } + default: + abort(); + } + } + if (i > 0 && array_remove_slice(&(s->commits), 0, i)) + return -1; + return fail; +} + +static int handle_deletes(BDRVVVFATState* s) +{ + int i, deferred = 1, deleted = 1; + + /* delete files corresponding to mappings marked as deleted */ + /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */ + while (deferred && deleted) { + deferred = 0; + deleted = 0; + + for (i = 1; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->mode & MODE_DELETED) { + direntry_t* entry = array_get(&(s->directory), + mapping->dir_index); + + if (is_free(entry)) { + /* remove file/directory */ + if (mapping->mode & MODE_DIRECTORY) { + int j, next_dir_index = s->directory.next, + first_dir_index = mapping->info.dir.first_dir_index; + + if (rmdir(mapping->path) < 0) { + if (errno == ENOTEMPTY) { + deferred++; + continue; + } else + return -5; + } + + for (j = 1; j < s->mapping.next; j++) { + mapping_t* m = array_get(&(s->mapping), j); + if (m->mode & MODE_DIRECTORY && + m->info.dir.first_dir_index > + first_dir_index && + m->info.dir.first_dir_index < + next_dir_index) + next_dir_index = + m->info.dir.first_dir_index; + } + remove_direntries(s, first_dir_index, + next_dir_index - first_dir_index); + + deleted++; + } + } else { + if (unlink(mapping->path)) + return -4; + deleted++; + } + DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry)); + remove_mapping(s, i); + } + } + } + + return 0; +} + +/* + * synchronize mapping with new state: + * + * - copy FAT (with bdrv_read) + * - mark all filenames corresponding to mappings as deleted + * - recurse direntries from root (using bs->bdrv_read) + * - delete files corresponding to mappings marked as deleted + */ +static int do_commit(BDRVVVFATState* s) +{ + int ret = 0; + + /* the real meat are the commits. Nothing to do? Move along! */ + if (s->commits.next == 0) + return 0; + + vvfat_close_current_file(s); + + ret = handle_renames_and_mkdirs(s); + if (ret) { + fprintf(stderr, "Error handling renames (%d)\n", ret); + abort(); + return ret; + } + + /* copy FAT (with bdrv_read) */ + memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat); + + /* recurse direntries from root (using bs->bdrv_read) */ + ret = commit_direntries(s, 0, -1); + if (ret) { + fprintf(stderr, "Fatal: error while committing (%d)\n", ret); + abort(); + return ret; + } + + ret = handle_commits(s); + if (ret) { + fprintf(stderr, "Error handling commits (%d)\n", ret); + abort(); + return ret; + } + + ret = handle_deletes(s); + if (ret) { + fprintf(stderr, "Error deleting\n"); + abort(); + return ret; + } + + if (s->qcow->drv->bdrv_make_empty) { + s->qcow->drv->bdrv_make_empty(s->qcow); + } + + memset(s->used_clusters, 0, sector2cluster(s, s->sector_count)); + +DLOG(checkpoint()); + return 0; +} + +static int try_commit(BDRVVVFATState* s) +{ + vvfat_close_current_file(s); +DLOG(checkpoint()); + if(!is_consistent(s)) + return -1; + return do_commit(s); +} + +static int vvfat_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVVVFATState *s = bs->opaque; + int i, ret; + +DLOG(checkpoint()); + + /* Check if we're operating in read-only mode */ + if (s->qcow == NULL) { + return -EACCES; + } + + vvfat_close_current_file(s); + + /* + * Some sanity checks: + * - do not allow writing to the boot sector + * - do not allow to write non-ASCII filenames + */ + + if (sector_num < s->first_sectors_number) + return -1; + + for (i = sector2cluster(s, sector_num); + i <= sector2cluster(s, sector_num + nb_sectors - 1);) { + mapping_t* mapping = find_mapping_for_cluster(s, i); + if (mapping) { + if (mapping->read_only) { + fprintf(stderr, "Tried to write to write-protected file %s\n", + mapping->path); + return -1; + } + + if (mapping->mode & MODE_DIRECTORY) { + int begin = cluster2sector(s, i); + int end = begin + s->sectors_per_cluster, k; + int dir_index; + const direntry_t* direntries; + long_file_name lfn; + + lfn_init(&lfn); + + if (begin < sector_num) + begin = sector_num; + if (end > sector_num + nb_sectors) + end = sector_num + nb_sectors; + dir_index = mapping->dir_index + + 0x10 * (begin - mapping->begin * s->sectors_per_cluster); + direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num)); + + for (k = 0; k < (end - begin) * 0x10; k++) { + /* do not allow non-ASCII filenames */ + if (parse_long_name(&lfn, direntries + k) < 0) { + fprintf(stderr, "Warning: non-ASCII filename\n"); + return -1; + } + /* no access to the direntry of a read-only file */ + else if (is_short_name(direntries+k) && + (direntries[k].attributes & 1)) { + if (memcmp(direntries + k, + array_get(&(s->directory), dir_index + k), + sizeof(direntry_t))) { + fprintf(stderr, "Warning: tried to write to write-protected file\n"); + return -1; + } + } + } + } + i = mapping->end; + } else + i++; + } + + /* + * Use qcow backend. Commit later. + */ +DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors)); + ret = bdrv_write(s->qcow, sector_num, buf, nb_sectors); + if (ret < 0) { + fprintf(stderr, "Error writing to qcow backend\n"); + return ret; + } + + for (i = sector2cluster(s, sector_num); + i <= sector2cluster(s, sector_num + nb_sectors - 1); i++) + if (i >= 0) + s->used_clusters[i] |= USED_ALLOCATED; + +DLOG(checkpoint()); + /* TODO: add timeout */ + try_commit(s); + +DLOG(checkpoint()); + return 0; +} + +static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + int ret; + BDRVVVFATState *s = bs->opaque; + qemu_co_mutex_lock(&s->lock); + ret = vvfat_write(bs, sector_num, buf, nb_sectors); + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int* n) +{ + BDRVVVFATState* s = bs->opaque; + *n = s->sector_count - sector_num; + if (*n > nb_sectors) { + *n = nb_sectors; + } else if (*n < 0) { + return 0; + } + return BDRV_BLOCK_DATA; +} + +static int write_target_commit(BlockDriverState *bs, int64_t sector_num, + const uint8_t* buffer, int nb_sectors) { + BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); + return try_commit(s); +} + +static void write_target_close(BlockDriverState *bs) { + BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); + bdrv_unref(s->qcow); + g_free(s->qcow_filename); +} + +static BlockDriver vvfat_write_target = { + .format_name = "vvfat_write_target", + .bdrv_write = write_target_commit, + .bdrv_close = write_target_close, +}; + +static int enable_write_target(BDRVVVFATState *s, Error **errp) +{ + BlockDriver *bdrv_qcow = NULL; + QemuOpts *opts = NULL; + int ret; + int size = sector2cluster(s, s->sector_count); + s->used_clusters = calloc(size, 1); + + array_init(&(s->commits), sizeof(commit_t)); + + s->qcow_filename = g_malloc(PATH_MAX); + ret = get_tmp_filename(s->qcow_filename, PATH_MAX); + if (ret < 0) { + error_setg_errno(errp, -ret, "can't create temporary file"); + goto err; + } + + bdrv_qcow = bdrv_find_format("qcow"); + if (!bdrv_qcow) { + error_setg(errp, "Failed to locate qcow driver"); + ret = -ENOENT; + goto err; + } + + opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort); + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s->sector_count * 512, + &error_abort); + qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort); + + ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp); + qemu_opts_del(opts); + if (ret < 0) { + goto err; + } + + s->qcow = NULL; + ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, + bdrv_qcow, errp); + if (ret < 0) { + goto err; + } + +#ifndef _WIN32 + unlink(s->qcow_filename); +#endif + + bdrv_set_backing_hd(s->bs, bdrv_new()); + s->bs->backing_hd->drv = &vvfat_write_target; + s->bs->backing_hd->opaque = g_new(void *, 1); + *(void**)s->bs->backing_hd->opaque = s; + + return 0; + +err: + g_free(s->qcow_filename); + s->qcow_filename = NULL; + return ret; +} + +static void vvfat_close(BlockDriverState *bs) +{ + BDRVVVFATState *s = bs->opaque; + + vvfat_close_current_file(s); + array_free(&(s->fat)); + array_free(&(s->directory)); + array_free(&(s->mapping)); + g_free(s->cluster_buffer); + + if (s->qcow) { + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); + } +} + +static BlockDriver bdrv_vvfat = { + .format_name = "vvfat", + .protocol_name = "fat", + .instance_size = sizeof(BDRVVVFATState), + + .bdrv_parse_filename = vvfat_parse_filename, + .bdrv_file_open = vvfat_open, + .bdrv_close = vvfat_close, + .bdrv_rebind = vvfat_rebind, + + .bdrv_read = vvfat_co_read, + .bdrv_write = vvfat_co_write, + .bdrv_co_get_block_status = vvfat_co_get_block_status, +}; + +static void bdrv_vvfat_init(void) +{ + bdrv_register(&bdrv_vvfat); +} + +block_init(bdrv_vvfat_init); + +#ifdef DEBUG +static void checkpoint(void) { + assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2); + check1(vvv); + check2(vvv); + assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY)); +#if 0 + if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf) + fprintf(stderr, "Nonono!\n"); + mapping_t* mapping; + direntry_t* direntry; + assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next); + assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next); + if (vvv->mapping.next<47) + return; + assert((mapping = array_get(&(vvv->mapping), 47))); + assert(mapping->dir_index < vvv->directory.next); + direntry = array_get(&(vvv->directory), mapping->dir_index); + assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0); +#endif +} +#endif diff --git a/block/win32-aio.c b/block/win32-aio.c new file mode 100644 index 0000000..64e8682 --- /dev/null +++ b/block/win32-aio.c @@ -0,0 +1,217 @@ +/* + * Block driver for RAW files (win32) + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "qemu/timer.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "block/aio.h" +#include "raw-aio.h" +#include "qemu/event_notifier.h" +#include "qemu/iov.h" +#include +#include + +#define FTYPE_FILE 0 +#define FTYPE_CD 1 +#define FTYPE_HARDDISK 2 + +struct QEMUWin32AIOState { + HANDLE hIOCP; + EventNotifier e; + int count; + bool is_aio_context_attached; +}; + +typedef struct QEMUWin32AIOCB { + BlockAIOCB common; + struct QEMUWin32AIOState *ctx; + int nbytes; + OVERLAPPED ov; + QEMUIOVector *qiov; + void *buf; + bool is_read; + bool is_linear; +} QEMUWin32AIOCB; + +/* + * Completes an AIO request (calls the callback and frees the ACB). + */ +static void win32_aio_process_completion(QEMUWin32AIOState *s, + QEMUWin32AIOCB *waiocb, DWORD count) +{ + int ret; + s->count--; + + if (waiocb->ov.Internal != 0) { + ret = -EIO; + } else { + ret = 0; + if (count < waiocb->nbytes) { + /* Short reads mean EOF, pad with zeros. */ + if (waiocb->is_read) { + qemu_iovec_memset(waiocb->qiov, count, 0, + waiocb->qiov->size - count); + } else { + ret = -EINVAL; + } + } + } + + if (!waiocb->is_linear) { + if (ret == 0 && waiocb->is_read) { + QEMUIOVector *qiov = waiocb->qiov; + iov_from_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size); + } + qemu_vfree(waiocb->buf); + } + + + waiocb->common.cb(waiocb->common.opaque, ret); + qemu_aio_unref(waiocb); +} + +static void win32_aio_completion_cb(EventNotifier *e) +{ + QEMUWin32AIOState *s = container_of(e, QEMUWin32AIOState, e); + DWORD count; + ULONG_PTR key; + OVERLAPPED *ov; + + event_notifier_test_and_clear(&s->e); + while (GetQueuedCompletionStatus(s->hIOCP, &count, &key, &ov, 0)) { + QEMUWin32AIOCB *waiocb = container_of(ov, QEMUWin32AIOCB, ov); + + win32_aio_process_completion(s, waiocb, count); + } +} + +static const AIOCBInfo win32_aiocb_info = { + .aiocb_size = sizeof(QEMUWin32AIOCB), +}; + +BlockAIOCB *win32_aio_submit(BlockDriverState *bs, + QEMUWin32AIOState *aio, HANDLE hfile, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, int type) +{ + struct QEMUWin32AIOCB *waiocb; + uint64_t offset = sector_num * 512; + DWORD rc; + + waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque); + waiocb->nbytes = nb_sectors * 512; + waiocb->qiov = qiov; + waiocb->is_read = (type == QEMU_AIO_READ); + + if (qiov->niov > 1) { + waiocb->buf = qemu_try_blockalign(bs, qiov->size); + if (waiocb->buf == NULL) { + goto out; + } + if (type & QEMU_AIO_WRITE) { + iov_to_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size); + } + waiocb->is_linear = false; + } else { + waiocb->buf = qiov->iov[0].iov_base; + waiocb->is_linear = true; + } + + memset(&waiocb->ov, 0, sizeof(waiocb->ov)); + waiocb->ov.Offset = (DWORD)offset; + waiocb->ov.OffsetHigh = (DWORD)(offset >> 32); + waiocb->ov.hEvent = event_notifier_get_handle(&aio->e); + + aio->count++; + + if (type & QEMU_AIO_READ) { + rc = ReadFile(hfile, waiocb->buf, waiocb->nbytes, NULL, &waiocb->ov); + } else { + rc = WriteFile(hfile, waiocb->buf, waiocb->nbytes, NULL, &waiocb->ov); + } + if(rc == 0 && GetLastError() != ERROR_IO_PENDING) { + goto out_dec_count; + } + return &waiocb->common; + +out_dec_count: + aio->count--; +out: + qemu_aio_unref(waiocb); + return NULL; +} + +int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile) +{ + if (CreateIoCompletionPort(hfile, aio->hIOCP, (ULONG_PTR) 0, 0) == NULL) { + return -EINVAL; + } else { + return 0; + } +} + +void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, + AioContext *old_context) +{ + aio_set_event_notifier(old_context, &aio->e, NULL); + aio->is_aio_context_attached = false; +} + +void win32_aio_attach_aio_context(QEMUWin32AIOState *aio, + AioContext *new_context) +{ + aio->is_aio_context_attached = true; + aio_set_event_notifier(new_context, &aio->e, win32_aio_completion_cb); +} + +QEMUWin32AIOState *win32_aio_init(void) +{ + QEMUWin32AIOState *s; + + s = g_malloc0(sizeof(*s)); + if (event_notifier_init(&s->e, false) < 0) { + goto out_free_state; + } + + s->hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (s->hIOCP == NULL) { + goto out_close_efd; + } + + return s; + +out_close_efd: + event_notifier_cleanup(&s->e); +out_free_state: + g_free(s); + return NULL; +} + +void win32_aio_cleanup(QEMUWin32AIOState *aio) +{ + assert(!aio->is_aio_context_attached); + CloseHandle(aio->hIOCP); + event_notifier_cleanup(&aio->e); + g_free(aio); +} diff --git a/block/write-threshold.c b/block/write-threshold.c new file mode 100644 index 0000000..a53c1f5 --- /dev/null +++ b/block/write-threshold.c @@ -0,0 +1,125 @@ +/* + * QEMU System Emulator block write threshold notification + * + * Copyright Red Hat, Inc. 2014 + * + * Authors: + * Francesco Romani + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "block/block_int.h" +#include "block/coroutine.h" +#include "block/write-threshold.h" +#include "qemu/notify.h" +#include "qapi-event.h" +#include "qmp-commands.h" + + +uint64_t bdrv_write_threshold_get(const BlockDriverState *bs) +{ + return bs->write_threshold_offset; +} + +bool bdrv_write_threshold_is_set(const BlockDriverState *bs) +{ + return bs->write_threshold_offset > 0; +} + +static void write_threshold_disable(BlockDriverState *bs) +{ + if (bdrv_write_threshold_is_set(bs)) { + notifier_with_return_remove(&bs->write_threshold_notifier); + bs->write_threshold_offset = 0; + } +} + +uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, + const BdrvTrackedRequest *req) +{ + if (bdrv_write_threshold_is_set(bs)) { + if (req->offset > bs->write_threshold_offset) { + return (req->offset - bs->write_threshold_offset) + req->bytes; + } + if ((req->offset + req->bytes) > bs->write_threshold_offset) { + return (req->offset + req->bytes) - bs->write_threshold_offset; + } + } + return 0; +} + +static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, + void *opaque) +{ + BdrvTrackedRequest *req = opaque; + BlockDriverState *bs = req->bs; + uint64_t amount = 0; + + amount = bdrv_write_threshold_exceeded(bs, req); + if (amount > 0) { + qapi_event_send_block_write_threshold( + bs->node_name, + amount, + bs->write_threshold_offset, + &error_abort); + + /* autodisable to avoid flooding the monitor */ + write_threshold_disable(bs); + } + + return 0; /* should always let other notifiers run */ +} + +static void write_threshold_register_notifier(BlockDriverState *bs) +{ + bs->write_threshold_notifier.notify = before_write_notify; + notifier_with_return_list_add(&bs->before_write_notifiers, + &bs->write_threshold_notifier); +} + +static void write_threshold_update(BlockDriverState *bs, + int64_t threshold_bytes) +{ + bs->write_threshold_offset = threshold_bytes; +} + +void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_bytes) +{ + if (bdrv_write_threshold_is_set(bs)) { + if (threshold_bytes > 0) { + write_threshold_update(bs, threshold_bytes); + } else { + write_threshold_disable(bs); + } + } else { + if (threshold_bytes > 0) { + /* avoid multiple registration */ + write_threshold_register_notifier(bs); + write_threshold_update(bs, threshold_bytes); + } + /* discard bogus disable request */ + } +} + +void qmp_block_set_write_threshold(const char *node_name, + uint64_t threshold_bytes, + Error **errp) +{ + BlockDriverState *bs; + AioContext *aio_context; + + bs = bdrv_find_node(node_name); + if (!bs) { + error_setg(errp, "Device '%s' not found", node_name); + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + bdrv_write_threshold_set(bs, threshold_bytes); + + aio_context_release(aio_context); +} diff --git a/blockdev-nbd.c b/blockdev-nbd.c new file mode 100644 index 0000000..85cda4c --- /dev/null +++ b/blockdev-nbd.c @@ -0,0 +1,136 @@ +/* + * Serving QEMU block devices via NBD + * + * Copyright (c) 2012 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" +#include "hw/block/block.h" +#include "monitor/monitor.h" +#include "qapi/qmp/qerror.h" +#include "sysemu/sysemu.h" +#include "qmp-commands.h" +#include "trace.h" +#include "block/nbd.h" +#include "qemu/sockets.h" + +static int server_fd = -1; + +static void nbd_accept(void *opaque) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + + int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); + if (fd >= 0 && !nbd_client_new(NULL, fd, nbd_client_put)) { + shutdown(fd, 2); + close(fd); + } +} + +void qmp_nbd_server_start(SocketAddress *addr, Error **errp) +{ + if (server_fd != -1) { + error_setg(errp, "NBD server already running"); + return; + } + + server_fd = socket_listen(addr, errp); + if (server_fd != -1) { + qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL); + } +} + +/* + * Hook into the BlockBackend notifiers to close the export when the + * backend is closed. + */ +typedef struct NBDCloseNotifier { + Notifier n; + NBDExport *exp; + QTAILQ_ENTRY(NBDCloseNotifier) next; +} NBDCloseNotifier; + +static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers = + QTAILQ_HEAD_INITIALIZER(close_notifiers); + +static void nbd_close_notifier(Notifier *n, void *data) +{ + NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n); + + notifier_remove(&cn->n); + QTAILQ_REMOVE(&close_notifiers, cn, next); + + nbd_export_close(cn->exp); + nbd_export_put(cn->exp); + g_free(cn); +} + +void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, + Error **errp) +{ + BlockBackend *blk; + NBDExport *exp; + NBDCloseNotifier *n; + + if (server_fd == -1) { + error_setg(errp, "NBD server not running"); + return; + } + + if (nbd_export_find(device)) { + error_setg(errp, "NBD server already exporting device '%s'", device); + return; + } + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + if (!blk_is_inserted(blk)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + return; + } + + if (!has_writable) { + writable = false; + } + if (blk_is_read_only(blk)) { + writable = false; + } + + exp = nbd_export_new(blk, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, NULL, + errp); + if (!exp) { + return; + } + + nbd_export_set_name(exp, device); + + n = g_new0(NBDCloseNotifier, 1); + n->n.notify = nbd_close_notifier; + n->exp = exp; + blk_add_close_notifier(blk, &n->n); + QTAILQ_INSERT_TAIL(&close_notifiers, n, next); +} + +void qmp_nbd_server_stop(Error **errp) +{ + while (!QTAILQ_EMPTY(&close_notifiers)) { + NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers); + nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp)); + } + + if (server_fd != -1) { + qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL); + close(server_fd); + server_fd = -1; + } +} diff --git a/blockdev.c b/blockdev.c new file mode 100644 index 0000000..5eaf77e --- /dev/null +++ b/blockdev.c @@ -0,0 +1,3215 @@ +/* + * QEMU host block devices + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "hw/block/block.h" +#include "block/blockjob.h" +#include "monitor/monitor.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "qapi/qmp/types.h" +#include "qapi-visit.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/util.h" +#include "sysemu/sysemu.h" +#include "block/block_int.h" +#include "qmp-commands.h" +#include "trace.h" +#include "sysemu/arch_init.h" + +static const char *const if_name[IF_COUNT] = { + [IF_NONE] = "none", + [IF_IDE] = "ide", + [IF_SCSI] = "scsi", + [IF_FLOPPY] = "floppy", + [IF_PFLASH] = "pflash", + [IF_MTD] = "mtd", + [IF_SD] = "sd", + [IF_VIRTIO] = "virtio", + [IF_XEN] = "xen", +}; + +static int if_max_devs[IF_COUNT] = { + /* + * Do not change these numbers! They govern how drive option + * index maps to unit and bus. That mapping is ABI. + * + * All controllers used to imlement if=T drives need to support + * if_max_devs[T] units, for any T with if_max_devs[T] != 0. + * Otherwise, some index values map to "impossible" bus, unit + * values. + * + * For instance, if you change [IF_SCSI] to 255, -drive + * if=scsi,index=12 no longer means bus=1,unit=5, but + * bus=0,unit=12. With an lsi53c895a controller (7 units max), + * the drive can't be set up. Regression. + */ + [IF_IDE] = 2, + [IF_SCSI] = 7, +}; + +/** + * Boards may call this to offer board-by-board overrides + * of the default, global values. + */ +void override_max_devs(BlockInterfaceType type, int max_devs) +{ + BlockBackend *blk; + DriveInfo *dinfo; + + if (max_devs <= 0) { + return; + } + + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + dinfo = blk_legacy_dinfo(blk); + if (dinfo->type == type) { + fprintf(stderr, "Cannot override units-per-bus property of" + " the %s interface, because a drive of that type has" + " already been added.\n", if_name[type]); + g_assert_not_reached(); + } + } + + if_max_devs[type] = max_devs; +} + +/* + * We automatically delete the drive when a device using it gets + * unplugged. Questionable feature, but we can't just drop it. + * Device models call blockdev_mark_auto_del() to schedule the + * automatic deletion, and generic qdev code calls blockdev_auto_del() + * when deletion is actually safe. + */ +void blockdev_mark_auto_del(BlockBackend *blk) +{ + DriveInfo *dinfo = blk_legacy_dinfo(blk); + BlockDriverState *bs = blk_bs(blk); + AioContext *aio_context; + + if (!dinfo) { + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (bs->job) { + block_job_cancel(bs->job); + } + + aio_context_release(aio_context); + + dinfo->auto_del = 1; +} + +void blockdev_auto_del(BlockBackend *blk) +{ + DriveInfo *dinfo = blk_legacy_dinfo(blk); + + if (dinfo && dinfo->auto_del) { + blk_unref(blk); + } +} + +/** + * Returns the current mapping of how many units per bus + * a particular interface can support. + * + * A positive integer indicates n units per bus. + * 0 implies the mapping has not been established. + * -1 indicates an invalid BlockInterfaceType was given. + */ +int drive_get_max_devs(BlockInterfaceType type) +{ + if (type >= IF_IDE && type < IF_COUNT) { + return if_max_devs[type]; + } + + return -1; +} + +static int drive_index_to_bus_id(BlockInterfaceType type, int index) +{ + int max_devs = if_max_devs[type]; + return max_devs ? index / max_devs : 0; +} + +static int drive_index_to_unit_id(BlockInterfaceType type, int index) +{ + int max_devs = if_max_devs[type]; + return max_devs ? index % max_devs : index; +} + +QemuOpts *drive_def(const char *optstr) +{ + return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0); +} + +QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, + const char *optstr) +{ + QemuOpts *opts; + + opts = drive_def(optstr); + if (!opts) { + return NULL; + } + if (type != IF_DEFAULT) { + qemu_opt_set(opts, "if", if_name[type], &error_abort); + } + if (index >= 0) { + qemu_opt_set_number(opts, "index", index, &error_abort); + } + if (file) + qemu_opt_set(opts, "file", file, &error_abort); + return opts; +} + +DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) +{ + BlockBackend *blk; + DriveInfo *dinfo; + + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + dinfo = blk_legacy_dinfo(blk); + if (dinfo && dinfo->type == type + && dinfo->bus == bus && dinfo->unit == unit) { + return dinfo; + } + } + + return NULL; +} + +bool drive_check_orphaned(void) +{ + BlockBackend *blk; + DriveInfo *dinfo; + bool rs = false; + + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + dinfo = blk_legacy_dinfo(blk); + /* If dinfo->bdrv->dev is NULL, it has no device attached. */ + /* Unless this is a default drive, this may be an oversight. */ + if (!blk_get_attached_dev(blk) && !dinfo->is_default && + dinfo->type != IF_NONE) { + fprintf(stderr, "Warning: Orphaned drive without device: " + "id=%s,file=%s,if=%s,bus=%d,unit=%d\n", + blk_name(blk), blk_bs(blk)->filename, if_name[dinfo->type], + dinfo->bus, dinfo->unit); + rs = true; + } + } + + return rs; +} + +DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) +{ + return drive_get(type, + drive_index_to_bus_id(type, index), + drive_index_to_unit_id(type, index)); +} + +int drive_get_max_bus(BlockInterfaceType type) +{ + int max_bus; + BlockBackend *blk; + DriveInfo *dinfo; + + max_bus = -1; + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + dinfo = blk_legacy_dinfo(blk); + if (dinfo && dinfo->type == type && dinfo->bus > max_bus) { + max_bus = dinfo->bus; + } + } + return max_bus; +} + +/* Get a block device. This should only be used for single-drive devices + (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the + appropriate bus. */ +DriveInfo *drive_get_next(BlockInterfaceType type) +{ + static int next_block_unit[IF_COUNT]; + + return drive_get(type, 0, next_block_unit[type]++); +} + +static void bdrv_format_print(void *opaque, const char *name) +{ + error_printf(" %s", name); +} + +typedef struct { + QEMUBH *bh; + BlockDriverState *bs; +} BDRVPutRefBH; + +static void bdrv_put_ref_bh(void *opaque) +{ + BDRVPutRefBH *s = opaque; + + bdrv_unref(s->bs); + qemu_bh_delete(s->bh); + g_free(s); +} + +/* + * Release a BDS reference in a BH + * + * It is not safe to use bdrv_unref() from a callback function when the callers + * still need the BlockDriverState. In such cases we schedule a BH to release + * the reference. + */ +static void bdrv_put_ref_bh_schedule(BlockDriverState *bs) +{ + BDRVPutRefBH *s; + + s = g_new(BDRVPutRefBH, 1); + s->bh = qemu_bh_new(bdrv_put_ref_bh, s); + s->bs = bs; + qemu_bh_schedule(s->bh); +} + +static int parse_block_error_action(const char *buf, bool is_read, Error **errp) +{ + if (!strcmp(buf, "ignore")) { + return BLOCKDEV_ON_ERROR_IGNORE; + } else if (!is_read && !strcmp(buf, "enospc")) { + return BLOCKDEV_ON_ERROR_ENOSPC; + } else if (!strcmp(buf, "stop")) { + return BLOCKDEV_ON_ERROR_STOP; + } else if (!strcmp(buf, "report")) { + return BLOCKDEV_ON_ERROR_REPORT; + } else { + error_setg(errp, "'%s' invalid %s error action", + buf, is_read ? "read" : "write"); + return -1; + } +} + +static bool check_throttle_config(ThrottleConfig *cfg, Error **errp) +{ + if (throttle_conflicting(cfg)) { + error_setg(errp, "bps/iops/max total values and read/write values" + " cannot be used at the same time"); + return false; + } + + if (!throttle_is_valid(cfg)) { + error_setg(errp, "bps/iops/maxs values must be 0 or greater"); + return false; + } + + return true; +} + +typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType; + +/* Takes the ownership of bs_opts */ +static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, + Error **errp) +{ + const char *buf; + int ro = 0; + int bdrv_flags = 0; + int on_read_error, on_write_error; + BlockBackend *blk; + BlockDriverState *bs; + ThrottleConfig cfg; + int snapshot = 0; + bool copy_on_read; + Error *error = NULL; + QemuOpts *opts; + const char *id; + bool has_driver_specific_opts; + BlockdevDetectZeroesOptions detect_zeroes; + + /* Check common options by copying from bs_opts to opts, all other options + * stay in bs_opts for processing by bdrv_open(). */ + id = qdict_get_try_str(bs_opts, "id"); + opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error); + if (error) { + error_propagate(errp, error); + goto err_no_opts; + } + + qemu_opts_absorb_qdict(opts, bs_opts, &error); + if (error) { + error_propagate(errp, error); + goto early_err; + } + + if (id) { + qdict_del(bs_opts, "id"); + } + + has_driver_specific_opts = !!qdict_size(bs_opts); + + /* extract parameters */ + snapshot = qemu_opt_get_bool(opts, "snapshot", 0); + ro = qemu_opt_get_bool(opts, "read-only", 0); + copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false); + + if ((buf = qemu_opt_get(opts, "discard")) != NULL) { + if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) { + error_setg(errp, "invalid discard option"); + goto early_err; + } + } + + if (qemu_opt_get_bool(opts, "cache.writeback", true)) { + bdrv_flags |= BDRV_O_CACHE_WB; + } + if (qemu_opt_get_bool(opts, "cache.direct", false)) { + bdrv_flags |= BDRV_O_NOCACHE; + } + if (qemu_opt_get_bool(opts, "cache.no-flush", false)) { + bdrv_flags |= BDRV_O_NO_FLUSH; + } + +#ifdef CONFIG_LINUX_AIO + if ((buf = qemu_opt_get(opts, "aio")) != NULL) { + if (!strcmp(buf, "native")) { + bdrv_flags |= BDRV_O_NATIVE_AIO; + } else if (!strcmp(buf, "threads")) { + /* this is the default */ + } else { + error_setg(errp, "invalid aio option"); + goto early_err; + } + } +#endif + + if ((buf = qemu_opt_get(opts, "format")) != NULL) { + if (is_help_option(buf)) { + error_printf("Supported formats:"); + bdrv_iterate_format(bdrv_format_print, NULL); + error_printf("\n"); + goto early_err; + } + + if (qdict_haskey(bs_opts, "driver")) { + error_setg(errp, "Cannot specify both 'driver' and 'format'"); + goto early_err; + } + qdict_put(bs_opts, "driver", qstring_from_str(buf)); + } + + /* disk I/O throttling */ + memset(&cfg, 0, sizeof(cfg)); + cfg.buckets[THROTTLE_BPS_TOTAL].avg = + qemu_opt_get_number(opts, "throttling.bps-total", 0); + cfg.buckets[THROTTLE_BPS_READ].avg = + qemu_opt_get_number(opts, "throttling.bps-read", 0); + cfg.buckets[THROTTLE_BPS_WRITE].avg = + qemu_opt_get_number(opts, "throttling.bps-write", 0); + cfg.buckets[THROTTLE_OPS_TOTAL].avg = + qemu_opt_get_number(opts, "throttling.iops-total", 0); + cfg.buckets[THROTTLE_OPS_READ].avg = + qemu_opt_get_number(opts, "throttling.iops-read", 0); + cfg.buckets[THROTTLE_OPS_WRITE].avg = + qemu_opt_get_number(opts, "throttling.iops-write", 0); + + cfg.buckets[THROTTLE_BPS_TOTAL].max = + qemu_opt_get_number(opts, "throttling.bps-total-max", 0); + cfg.buckets[THROTTLE_BPS_READ].max = + qemu_opt_get_number(opts, "throttling.bps-read-max", 0); + cfg.buckets[THROTTLE_BPS_WRITE].max = + qemu_opt_get_number(opts, "throttling.bps-write-max", 0); + cfg.buckets[THROTTLE_OPS_TOTAL].max = + qemu_opt_get_number(opts, "throttling.iops-total-max", 0); + cfg.buckets[THROTTLE_OPS_READ].max = + qemu_opt_get_number(opts, "throttling.iops-read-max", 0); + cfg.buckets[THROTTLE_OPS_WRITE].max = + qemu_opt_get_number(opts, "throttling.iops-write-max", 0); + + cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); + + if (!check_throttle_config(&cfg, &error)) { + error_propagate(errp, error); + goto early_err; + } + + on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; + if ((buf = qemu_opt_get(opts, "werror")) != NULL) { + on_write_error = parse_block_error_action(buf, 0, &error); + if (error) { + error_propagate(errp, error); + goto early_err; + } + } + + on_read_error = BLOCKDEV_ON_ERROR_REPORT; + if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { + on_read_error = parse_block_error_action(buf, 1, &error); + if (error) { + error_propagate(errp, error); + goto early_err; + } + } + + detect_zeroes = + qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, + qemu_opt_get(opts, "detect-zeroes"), + BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, + &error); + if (error) { + error_propagate(errp, error); + goto early_err; + } + + if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && + !(bdrv_flags & BDRV_O_UNMAP)) { + error_setg(errp, "setting detect-zeroes to unmap is not allowed " + "without setting discard operation to unmap"); + goto early_err; + } + + /* init */ + if ((!file || !*file) && !has_driver_specific_opts) { + blk = blk_new_with_bs(qemu_opts_id(opts), errp); + if (!blk) { + goto early_err; + } + + bs = blk_bs(blk); + bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; + bs->read_only = ro; + + QDECREF(bs_opts); + } else { + if (file && !*file) { + file = NULL; + } + + if (snapshot) { + /* always use cache=unsafe with snapshot */ + bdrv_flags &= ~BDRV_O_CACHE_MASK; + bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); + } + + if (copy_on_read) { + bdrv_flags |= BDRV_O_COPY_ON_READ; + } + + if (runstate_check(RUN_STATE_INMIGRATE)) { + bdrv_flags |= BDRV_O_INCOMING; + } + + bdrv_flags |= ro ? 0 : BDRV_O_RDWR; + + blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags, + errp); + if (!blk) { + goto err_no_bs_opts; + } + bs = blk_bs(blk); + } + + bs->detect_zeroes = detect_zeroes; + + bdrv_set_on_error(bs, on_read_error, on_write_error); + + /* disk I/O throttling */ + if (throttle_enabled(&cfg)) { + bdrv_io_limits_enable(bs); + bdrv_set_io_limits(bs, &cfg); + } + + if (bdrv_key_required(bs)) { + autostart = 0; + } + +err_no_bs_opts: + qemu_opts_del(opts); + return blk; + +early_err: + qemu_opts_del(opts); +err_no_opts: + QDECREF(bs_opts); + return NULL; +} + +static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to, + Error **errp) +{ + const char *value; + + value = qemu_opt_get(opts, from); + if (value) { + if (qemu_opt_find(opts, to)) { + error_setg(errp, "'%s' and its alias '%s' can't be used at the " + "same time", to, from); + return; + } + } + + /* rename all items in opts */ + while ((value = qemu_opt_get(opts, from))) { + qemu_opt_set(opts, to, value, &error_abort); + qemu_opt_unset(opts, from); + } +} + +QemuOptsList qemu_legacy_drive_opts = { + .name = "drive", + .head = QTAILQ_HEAD_INITIALIZER(qemu_legacy_drive_opts.head), + .desc = { + { + .name = "bus", + .type = QEMU_OPT_NUMBER, + .help = "bus number", + },{ + .name = "unit", + .type = QEMU_OPT_NUMBER, + .help = "unit number (i.e. lun for scsi)", + },{ + .name = "index", + .type = QEMU_OPT_NUMBER, + .help = "index number", + },{ + .name = "media", + .type = QEMU_OPT_STRING, + .help = "media type (disk, cdrom)", + },{ + .name = "if", + .type = QEMU_OPT_STRING, + .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", + },{ + .name = "cyls", + .type = QEMU_OPT_NUMBER, + .help = "number of cylinders (ide disk geometry)", + },{ + .name = "heads", + .type = QEMU_OPT_NUMBER, + .help = "number of heads (ide disk geometry)", + },{ + .name = "secs", + .type = QEMU_OPT_NUMBER, + .help = "number of sectors (ide disk geometry)", + },{ + .name = "trans", + .type = QEMU_OPT_STRING, + .help = "chs translation (auto, lba, none)", + },{ + .name = "boot", + .type = QEMU_OPT_BOOL, + .help = "(deprecated, ignored)", + },{ + .name = "addr", + .type = QEMU_OPT_STRING, + .help = "pci address (virtio only)", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "disk serial number", + },{ + .name = "file", + .type = QEMU_OPT_STRING, + .help = "file name", + }, + + /* Options that are passed on, but have special semantics with -drive */ + { + .name = "read-only", + .type = QEMU_OPT_BOOL, + .help = "open drive file as read-only", + },{ + .name = "rerror", + .type = QEMU_OPT_STRING, + .help = "read error action", + },{ + .name = "werror", + .type = QEMU_OPT_STRING, + .help = "write error action", + },{ + .name = "copy-on-read", + .type = QEMU_OPT_BOOL, + .help = "copy read data from backing file into image file", + }, + + { /* end of list */ } + }, +}; + +DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) +{ + const char *value; + BlockBackend *blk; + DriveInfo *dinfo = NULL; + QDict *bs_opts; + QemuOpts *legacy_opts; + DriveMediaType media = MEDIA_DISK; + BlockInterfaceType type; + int cyls, heads, secs, translation; + int max_devs, bus_id, unit_id, index; + const char *devaddr; + const char *werror, *rerror; + bool read_only = false; + bool copy_on_read; + const char *serial; + const char *filename; + Error *local_err = NULL; + int i; + + /* Change legacy command line options into QMP ones */ + static const struct { + const char *from; + const char *to; + } opt_renames[] = { + { "iops", "throttling.iops-total" }, + { "iops_rd", "throttling.iops-read" }, + { "iops_wr", "throttling.iops-write" }, + + { "bps", "throttling.bps-total" }, + { "bps_rd", "throttling.bps-read" }, + { "bps_wr", "throttling.bps-write" }, + + { "iops_max", "throttling.iops-total-max" }, + { "iops_rd_max", "throttling.iops-read-max" }, + { "iops_wr_max", "throttling.iops-write-max" }, + + { "bps_max", "throttling.bps-total-max" }, + { "bps_rd_max", "throttling.bps-read-max" }, + { "bps_wr_max", "throttling.bps-write-max" }, + + { "iops_size", "throttling.iops-size" }, + + { "readonly", "read-only" }, + }; + + for (i = 0; i < ARRAY_SIZE(opt_renames); i++) { + qemu_opt_rename(all_opts, opt_renames[i].from, opt_renames[i].to, + &local_err); + if (local_err) { + error_report_err(local_err); + return NULL; + } + } + + value = qemu_opt_get(all_opts, "cache"); + if (value) { + int flags = 0; + + if (bdrv_parse_cache_flags(value, &flags) != 0) { + error_report("invalid cache option"); + return NULL; + } + + /* Specific options take precedence */ + if (!qemu_opt_get(all_opts, "cache.writeback")) { + qemu_opt_set_bool(all_opts, "cache.writeback", + !!(flags & BDRV_O_CACHE_WB), &error_abort); + } + if (!qemu_opt_get(all_opts, "cache.direct")) { + qemu_opt_set_bool(all_opts, "cache.direct", + !!(flags & BDRV_O_NOCACHE), &error_abort); + } + if (!qemu_opt_get(all_opts, "cache.no-flush")) { + qemu_opt_set_bool(all_opts, "cache.no-flush", + !!(flags & BDRV_O_NO_FLUSH), &error_abort); + } + qemu_opt_unset(all_opts, "cache"); + } + + /* Get a QDict for processing the options */ + bs_opts = qdict_new(); + qemu_opts_to_qdict(all_opts, bs_opts); + + legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0, + &error_abort); + qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err); + if (local_err) { + error_report_err(local_err); + goto fail; + } + + /* Deprecated option boot=[on|off] */ + if (qemu_opt_get(legacy_opts, "boot") != NULL) { + fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be " + "ignored. Future versions will reject this parameter. Please " + "update your scripts.\n"); + } + + /* Media type */ + value = qemu_opt_get(legacy_opts, "media"); + if (value) { + if (!strcmp(value, "disk")) { + media = MEDIA_DISK; + } else if (!strcmp(value, "cdrom")) { + media = MEDIA_CDROM; + read_only = true; + } else { + error_report("'%s' invalid media", value); + goto fail; + } + } + + /* copy-on-read is disabled with a warning for read-only devices */ + read_only |= qemu_opt_get_bool(legacy_opts, "read-only", false); + copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false); + + if (read_only && copy_on_read) { + error_report("warning: disabling copy-on-read on read-only drive"); + copy_on_read = false; + } + + qdict_put(bs_opts, "read-only", + qstring_from_str(read_only ? "on" : "off")); + qdict_put(bs_opts, "copy-on-read", + qstring_from_str(copy_on_read ? "on" :"off")); + + /* Controller type */ + value = qemu_opt_get(legacy_opts, "if"); + if (value) { + for (type = 0; + type < IF_COUNT && strcmp(value, if_name[type]); + type++) { + } + if (type == IF_COUNT) { + error_report("unsupported bus type '%s'", value); + goto fail; + } + } else { + type = block_default_type; + } + + /* Geometry */ + cyls = qemu_opt_get_number(legacy_opts, "cyls", 0); + heads = qemu_opt_get_number(legacy_opts, "heads", 0); + secs = qemu_opt_get_number(legacy_opts, "secs", 0); + + if (cyls || heads || secs) { + if (cyls < 1) { + error_report("invalid physical cyls number"); + goto fail; + } + if (heads < 1) { + error_report("invalid physical heads number"); + goto fail; + } + if (secs < 1) { + error_report("invalid physical secs number"); + goto fail; + } + } + + translation = BIOS_ATA_TRANSLATION_AUTO; + value = qemu_opt_get(legacy_opts, "trans"); + if (value != NULL) { + if (!cyls) { + error_report("'%s' trans must be used with cyls, heads and secs", + value); + goto fail; + } + if (!strcmp(value, "none")) { + translation = BIOS_ATA_TRANSLATION_NONE; + } else if (!strcmp(value, "lba")) { + translation = BIOS_ATA_TRANSLATION_LBA; + } else if (!strcmp(value, "large")) { + translation = BIOS_ATA_TRANSLATION_LARGE; + } else if (!strcmp(value, "rechs")) { + translation = BIOS_ATA_TRANSLATION_RECHS; + } else if (!strcmp(value, "auto")) { + translation = BIOS_ATA_TRANSLATION_AUTO; + } else { + error_report("'%s' invalid translation type", value); + goto fail; + } + } + + if (media == MEDIA_CDROM) { + if (cyls || secs || heads) { + error_report("CHS can't be set with media=cdrom"); + goto fail; + } + } + + /* Device address specified by bus/unit or index. + * If none was specified, try to find the first free one. */ + bus_id = qemu_opt_get_number(legacy_opts, "bus", 0); + unit_id = qemu_opt_get_number(legacy_opts, "unit", -1); + index = qemu_opt_get_number(legacy_opts, "index", -1); + + max_devs = if_max_devs[type]; + + if (index != -1) { + if (bus_id != 0 || unit_id != -1) { + error_report("index cannot be used with bus and unit"); + goto fail; + } + bus_id = drive_index_to_bus_id(type, index); + unit_id = drive_index_to_unit_id(type, index); + } + + if (unit_id == -1) { + unit_id = 0; + while (drive_get(type, bus_id, unit_id) != NULL) { + unit_id++; + if (max_devs && unit_id >= max_devs) { + unit_id -= max_devs; + bus_id++; + } + } + } + + if (max_devs && unit_id >= max_devs) { + error_report("unit %d too big (max is %d)", unit_id, max_devs - 1); + goto fail; + } + + if (drive_get(type, bus_id, unit_id) != NULL) { + error_report("drive with bus=%d, unit=%d (index=%d) exists", + bus_id, unit_id, index); + goto fail; + } + + /* Serial number */ + serial = qemu_opt_get(legacy_opts, "serial"); + + /* no id supplied -> create one */ + if (qemu_opts_id(all_opts) == NULL) { + char *new_id; + const char *mediastr = ""; + if (type == IF_IDE || type == IF_SCSI) { + mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd"; + } + if (max_devs) { + new_id = g_strdup_printf("%s%i%s%i", if_name[type], bus_id, + mediastr, unit_id); + } else { + new_id = g_strdup_printf("%s%s%i", if_name[type], + mediastr, unit_id); + } + qdict_put(bs_opts, "id", qstring_from_str(new_id)); + g_free(new_id); + } + + /* Add virtio block device */ + devaddr = qemu_opt_get(legacy_opts, "addr"); + if (devaddr && type != IF_VIRTIO) { + error_report("addr is not supported by this bus type"); + goto fail; + } + + if (type == IF_VIRTIO) { + QemuOpts *devopts; + devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, + &error_abort); + if (arch_type == QEMU_ARCH_S390X) { + qemu_opt_set(devopts, "driver", "virtio-blk-s390", &error_abort); + } else { + qemu_opt_set(devopts, "driver", "virtio-blk-pci", &error_abort); + } + qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), + &error_abort); + if (devaddr) { + qemu_opt_set(devopts, "addr", devaddr, &error_abort); + } + } + + filename = qemu_opt_get(legacy_opts, "file"); + + /* Check werror/rerror compatibility with if=... */ + werror = qemu_opt_get(legacy_opts, "werror"); + if (werror != NULL) { + if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && + type != IF_NONE) { + error_report("werror is not supported by this bus type"); + goto fail; + } + qdict_put(bs_opts, "werror", qstring_from_str(werror)); + } + + rerror = qemu_opt_get(legacy_opts, "rerror"); + if (rerror != NULL) { + if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && + type != IF_NONE) { + error_report("rerror is not supported by this bus type"); + goto fail; + } + qdict_put(bs_opts, "rerror", qstring_from_str(rerror)); + } + + /* Actual block device init: Functionality shared with blockdev-add */ + blk = blockdev_init(filename, bs_opts, &local_err); + bs_opts = NULL; + if (!blk) { + if (local_err) { + error_report_err(local_err); + } + goto fail; + } else { + assert(!local_err); + } + + /* Create legacy DriveInfo */ + dinfo = g_malloc0(sizeof(*dinfo)); + dinfo->opts = all_opts; + + dinfo->cyls = cyls; + dinfo->heads = heads; + dinfo->secs = secs; + dinfo->trans = translation; + + dinfo->type = type; + dinfo->bus = bus_id; + dinfo->unit = unit_id; + dinfo->devaddr = devaddr; + dinfo->serial = g_strdup(serial); + + blk_set_legacy_dinfo(blk, dinfo); + + switch(type) { + case IF_IDE: + case IF_SCSI: + case IF_XEN: + case IF_NONE: + dinfo->media_cd = media == MEDIA_CDROM; + break; + default: + break; + } + +fail: + qemu_opts_del(legacy_opts); + QDECREF(bs_opts); + return dinfo; +} + +void hmp_commit(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + BlockBackend *blk; + int ret; + + if (!strcmp(device, "all")) { + ret = bdrv_commit_all(); + } else { + blk = blk_by_name(device); + if (!blk) { + monitor_printf(mon, "Device '%s' not found\n", device); + return; + } + ret = bdrv_commit(blk_bs(blk)); + } + if (ret < 0) { + monitor_printf(mon, "'commit' error for '%s': %s\n", device, + strerror(-ret)); + } +} + +static void blockdev_do_action(int kind, void *data, Error **errp) +{ + TransactionAction action; + TransactionActionList list; + + action.kind = kind; + action.data = data; + list.value = &action; + list.next = NULL; + qmp_transaction(&list, errp); +} + +void qmp_blockdev_snapshot_sync(bool has_device, const char *device, + bool has_node_name, const char *node_name, + const char *snapshot_file, + bool has_snapshot_node_name, + const char *snapshot_node_name, + bool has_format, const char *format, + bool has_mode, NewImageMode mode, Error **errp) +{ + BlockdevSnapshot snapshot = { + .has_device = has_device, + .device = (char *) device, + .has_node_name = has_node_name, + .node_name = (char *) node_name, + .snapshot_file = (char *) snapshot_file, + .has_snapshot_node_name = has_snapshot_node_name, + .snapshot_node_name = (char *) snapshot_node_name, + .has_format = has_format, + .format = (char *) format, + .has_mode = has_mode, + .mode = mode, + }; + blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, + &snapshot, errp); +} + +void qmp_blockdev_snapshot_internal_sync(const char *device, + const char *name, + Error **errp) +{ + BlockdevSnapshotInternal snapshot = { + .device = (char *) device, + .name = (char *) name + }; + + blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC, + &snapshot, errp); +} + +SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, + bool has_id, + const char *id, + bool has_name, + const char *name, + Error **errp) +{ + BlockDriverState *bs; + BlockBackend *blk; + AioContext *aio_context; + QEMUSnapshotInfo sn; + Error *local_err = NULL; + SnapshotInfo *info = NULL; + int ret; + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return NULL; + } + bs = blk_bs(blk); + + if (!has_id) { + id = NULL; + } + + if (!has_name) { + name = NULL; + } + + if (!id && !name) { + error_setg(errp, "Name or id must be provided"); + return NULL; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { + goto out_aio_context; + } + + ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out_aio_context; + } + if (!ret) { + error_setg(errp, + "Snapshot with id '%s' and name '%s' does not exist on " + "device '%s'", + STR_OR_NULL(id), STR_OR_NULL(name), device); + goto out_aio_context; + } + + bdrv_snapshot_delete(bs, id, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out_aio_context; + } + + aio_context_release(aio_context); + + info = g_new0(SnapshotInfo, 1); + info->id = g_strdup(sn.id_str); + info->name = g_strdup(sn.name); + info->date_nsec = sn.date_nsec; + info->date_sec = sn.date_sec; + info->vm_state_size = sn.vm_state_size; + info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000; + info->vm_clock_sec = sn.vm_clock_nsec / 1000000000; + + return info; + +out_aio_context: + aio_context_release(aio_context); + return NULL; +} + +/** + * block_dirty_bitmap_lookup: + * Return a dirty bitmap (if present), after validating + * the node reference and bitmap names. + * + * @node: The name of the BDS node to search for bitmaps + * @name: The name of the bitmap to search for + * @pbs: Output pointer for BDS lookup, if desired. Can be NULL. + * @paio: Output pointer for aio_context acquisition, if desired. Can be NULL. + * @errp: Output pointer for error information. Can be NULL. + * + * @return: A bitmap object on success, or NULL on failure. + */ +static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, + const char *name, + BlockDriverState **pbs, + AioContext **paio, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + AioContext *aio_context; + + if (!node) { + error_setg(errp, "Node cannot be NULL"); + return NULL; + } + if (!name) { + error_setg(errp, "Bitmap name cannot be NULL"); + return NULL; + } + bs = bdrv_lookup_bs(node, node, NULL); + if (!bs) { + error_setg(errp, "Node '%s' not found", node); + return NULL; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + bitmap = bdrv_find_dirty_bitmap(bs, name); + if (!bitmap) { + error_setg(errp, "Dirty bitmap '%s' not found", name); + goto fail; + } + + if (pbs) { + *pbs = bs; + } + if (paio) { + *paio = aio_context; + } else { + aio_context_release(aio_context); + } + + return bitmap; + + fail: + aio_context_release(aio_context); + return NULL; +} + +/* New and old BlockDriverState structs for atomic group operations */ + +typedef struct BlkTransactionState BlkTransactionState; + +/* Only prepare() may fail. In a single transaction, only one of commit() or + abort() will be called, clean() will always be called if it present. */ +typedef struct BdrvActionOps { + /* Size of state struct, in bytes. */ + size_t instance_size; + /* Prepare the work, must NOT be NULL. */ + void (*prepare)(BlkTransactionState *common, Error **errp); + /* Commit the changes, can be NULL. */ + void (*commit)(BlkTransactionState *common); + /* Abort the changes on fail, can be NULL. */ + void (*abort)(BlkTransactionState *common); + /* Clean up resource in the end, can be NULL. */ + void (*clean)(BlkTransactionState *common); +} BdrvActionOps; + +/* + * This structure must be arranged as first member in child type, assuming + * that compiler will also arrange it to the same address with parent instance. + * Later it will be used in free(). + */ +struct BlkTransactionState { + TransactionAction *action; + const BdrvActionOps *ops; + QSIMPLEQ_ENTRY(BlkTransactionState) entry; +}; + +/* internal snapshot private data */ +typedef struct InternalSnapshotState { + BlkTransactionState common; + BlockDriverState *bs; + AioContext *aio_context; + QEMUSnapshotInfo sn; +} InternalSnapshotState; + +static void internal_snapshot_prepare(BlkTransactionState *common, + Error **errp) +{ + Error *local_err = NULL; + const char *device; + const char *name; + BlockBackend *blk; + BlockDriverState *bs; + QEMUSnapshotInfo old_sn, *sn; + bool ret; + qemu_timeval tv; + BlockdevSnapshotInternal *internal; + InternalSnapshotState *state; + int ret1; + + g_assert(common->action->kind == + TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC); + internal = common->action->blockdev_snapshot_internal_sync; + state = DO_UPCAST(InternalSnapshotState, common, common); + + /* 1. parse input */ + device = internal->device; + name = internal->name; + + /* 2. check for validation */ + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + bs = blk_bs(blk); + + /* AioContext is released in .clean() */ + state->aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(state->aio_context); + + if (!bdrv_is_inserted(bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + return; + } + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { + return; + } + + if (bdrv_is_read_only(bs)) { + error_setg(errp, "Device '%s' is read only", device); + return; + } + + if (!bdrv_can_snapshot(bs)) { + error_setg(errp, "Block format '%s' used by device '%s' " + "does not support internal snapshots", + bs->drv->format_name, device); + return; + } + + if (!strlen(name)) { + error_setg(errp, "Name is empty"); + return; + } + + /* check whether a snapshot with name exist */ + ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } else if (ret) { + error_setg(errp, + "Snapshot with name '%s' already exists on device '%s'", + name, device); + return; + } + + /* 3. take the snapshot */ + sn = &state->sn; + pstrcpy(sn->name, sizeof(sn->name), name); + qemu_gettimeofday(&tv); + sn->date_sec = tv.tv_sec; + sn->date_nsec = tv.tv_usec * 1000; + sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + ret1 = bdrv_snapshot_create(bs, sn); + if (ret1 < 0) { + error_setg_errno(errp, -ret1, + "Failed to create snapshot '%s' on device '%s'", + name, device); + return; + } + + /* 4. succeed, mark a snapshot is created */ + state->bs = bs; +} + +static void internal_snapshot_abort(BlkTransactionState *common) +{ + InternalSnapshotState *state = + DO_UPCAST(InternalSnapshotState, common, common); + BlockDriverState *bs = state->bs; + QEMUSnapshotInfo *sn = &state->sn; + Error *local_error = NULL; + + if (!bs) { + return; + } + + if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { + error_report("Failed to delete snapshot with id '%s' and name '%s' on " + "device '%s' in abort: %s", + sn->id_str, + sn->name, + bdrv_get_device_name(bs), + error_get_pretty(local_error)); + error_free(local_error); + } +} + +static void internal_snapshot_clean(BlkTransactionState *common) +{ + InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState, + common, common); + + if (state->aio_context) { + aio_context_release(state->aio_context); + } +} + +/* external snapshot private data */ +typedef struct ExternalSnapshotState { + BlkTransactionState common; + BlockDriverState *old_bs; + BlockDriverState *new_bs; + AioContext *aio_context; +} ExternalSnapshotState; + +static void external_snapshot_prepare(BlkTransactionState *common, + Error **errp) +{ + BlockDriver *drv; + int flags, ret; + QDict *options = NULL; + Error *local_err = NULL; + bool has_device = false; + const char *device; + bool has_node_name = false; + const char *node_name; + bool has_snapshot_node_name = false; + const char *snapshot_node_name; + const char *new_image_file; + const char *format = "qcow2"; + enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; + ExternalSnapshotState *state = + DO_UPCAST(ExternalSnapshotState, common, common); + TransactionAction *action = common->action; + + /* get parameters */ + g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC); + + has_device = action->blockdev_snapshot_sync->has_device; + device = action->blockdev_snapshot_sync->device; + has_node_name = action->blockdev_snapshot_sync->has_node_name; + node_name = action->blockdev_snapshot_sync->node_name; + has_snapshot_node_name = + action->blockdev_snapshot_sync->has_snapshot_node_name; + snapshot_node_name = action->blockdev_snapshot_sync->snapshot_node_name; + + new_image_file = action->blockdev_snapshot_sync->snapshot_file; + if (action->blockdev_snapshot_sync->has_format) { + format = action->blockdev_snapshot_sync->format; + } + if (action->blockdev_snapshot_sync->has_mode) { + mode = action->blockdev_snapshot_sync->mode; + } + + /* start processing */ + drv = bdrv_find_format(format); + if (!drv) { + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); + return; + } + + state->old_bs = bdrv_lookup_bs(has_device ? device : NULL, + has_node_name ? node_name : NULL, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (has_node_name && !has_snapshot_node_name) { + error_setg(errp, "New snapshot node name missing"); + return; + } + + if (has_snapshot_node_name && bdrv_find_node(snapshot_node_name)) { + error_setg(errp, "New snapshot node name already existing"); + return; + } + + /* Acquire AioContext now so any threads operating on old_bs stop */ + state->aio_context = bdrv_get_aio_context(state->old_bs); + aio_context_acquire(state->aio_context); + + if (!bdrv_is_inserted(state->old_bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + return; + } + + if (bdrv_op_is_blocked(state->old_bs, + BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) { + return; + } + + if (!bdrv_is_read_only(state->old_bs)) { + if (bdrv_flush(state->old_bs)) { + error_set(errp, QERR_IO_ERROR); + return; + } + } + + if (!bdrv_is_first_non_filter(state->old_bs)) { + error_set(errp, QERR_FEATURE_DISABLED, "snapshot"); + return; + } + + flags = state->old_bs->open_flags; + + /* create new image w/backing file */ + if (mode != NEW_IMAGE_MODE_EXISTING) { + bdrv_img_create(new_image_file, format, + state->old_bs->filename, + state->old_bs->drv->format_name, + NULL, -1, flags, &local_err, false); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + if (has_snapshot_node_name) { + options = qdict_new(); + qdict_put(options, "node-name", + qstring_from_str(snapshot_node_name)); + } + + /* TODO Inherit bs->options or only take explicit options with an + * extended QMP command? */ + assert(state->new_bs == NULL); + ret = bdrv_open(&state->new_bs, new_image_file, NULL, options, + flags | BDRV_O_NO_BACKING, drv, &local_err); + /* We will manually add the backing_hd field to the bs later */ + if (ret != 0) { + error_propagate(errp, local_err); + } +} + +static void external_snapshot_commit(BlkTransactionState *common) +{ + ExternalSnapshotState *state = + DO_UPCAST(ExternalSnapshotState, common, common); + + bdrv_set_aio_context(state->new_bs, state->aio_context); + + /* This removes our old bs and adds the new bs */ + bdrv_append(state->new_bs, state->old_bs); + /* We don't need (or want) to use the transactional + * bdrv_reopen_multiple() across all the entries at once, because we + * don't want to abort all of them if one of them fails the reopen */ + bdrv_reopen(state->new_bs, state->new_bs->open_flags & ~BDRV_O_RDWR, + NULL); + + aio_context_release(state->aio_context); +} + +static void external_snapshot_abort(BlkTransactionState *common) +{ + ExternalSnapshotState *state = + DO_UPCAST(ExternalSnapshotState, common, common); + if (state->new_bs) { + bdrv_unref(state->new_bs); + } + if (state->aio_context) { + aio_context_release(state->aio_context); + } +} + +typedef struct DriveBackupState { + BlkTransactionState common; + BlockDriverState *bs; + AioContext *aio_context; + BlockJob *job; +} DriveBackupState; + +static void drive_backup_prepare(BlkTransactionState *common, Error **errp) +{ + DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); + BlockDriverState *bs; + BlockBackend *blk; + DriveBackup *backup; + Error *local_err = NULL; + + assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); + backup = common->action->drive_backup; + + blk = blk_by_name(backup->device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); + return; + } + bs = blk_bs(blk); + + /* AioContext is released in .clean() */ + state->aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(state->aio_context); + + qmp_drive_backup(backup->device, backup->target, + backup->has_format, backup->format, + backup->sync, + backup->has_mode, backup->mode, + backup->has_speed, backup->speed, + backup->has_bitmap, backup->bitmap, + backup->has_on_source_error, backup->on_source_error, + backup->has_on_target_error, backup->on_target_error, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + state->bs = bs; + state->job = state->bs->job; +} + +static void drive_backup_abort(BlkTransactionState *common) +{ + DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); + BlockDriverState *bs = state->bs; + + /* Only cancel if it's the job we started */ + if (bs && bs->job && bs->job == state->job) { + block_job_cancel_sync(bs->job); + } +} + +static void drive_backup_clean(BlkTransactionState *common) +{ + DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); + + if (state->aio_context) { + aio_context_release(state->aio_context); + } +} + +typedef struct BlockdevBackupState { + BlkTransactionState common; + BlockDriverState *bs; + BlockJob *job; + AioContext *aio_context; +} BlockdevBackupState; + +static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + BlockdevBackup *backup; + BlockDriverState *bs, *target; + BlockBackend *blk; + Error *local_err = NULL; + + assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); + backup = common->action->blockdev_backup; + + blk = blk_by_name(backup->device); + if (!blk) { + error_setg(errp, "Device '%s' not found", backup->device); + return; + } + bs = blk_bs(blk); + + blk = blk_by_name(backup->target); + if (!blk) { + error_setg(errp, "Device '%s' not found", backup->target); + return; + } + target = blk_bs(blk); + + /* AioContext is released in .clean() */ + state->aio_context = bdrv_get_aio_context(bs); + if (state->aio_context != bdrv_get_aio_context(target)) { + state->aio_context = NULL; + error_setg(errp, "Backup between two IO threads is not implemented"); + return; + } + aio_context_acquire(state->aio_context); + + qmp_blockdev_backup(backup->device, backup->target, + backup->sync, + backup->has_speed, backup->speed, + backup->has_on_source_error, backup->on_source_error, + backup->has_on_target_error, backup->on_target_error, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + state->bs = bs; + state->job = state->bs->job; +} + +static void blockdev_backup_abort(BlkTransactionState *common) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + BlockDriverState *bs = state->bs; + + /* Only cancel if it's the job we started */ + if (bs && bs->job && bs->job == state->job) { + block_job_cancel_sync(bs->job); + } +} + +static void blockdev_backup_clean(BlkTransactionState *common) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + + if (state->aio_context) { + aio_context_release(state->aio_context); + } +} + +static void abort_prepare(BlkTransactionState *common, Error **errp) +{ + error_setg(errp, "Transaction aborted using Abort action"); +} + +static void abort_commit(BlkTransactionState *common) +{ + g_assert_not_reached(); /* this action never succeeds */ +} + +static const BdrvActionOps actions[] = { + [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = { + .instance_size = sizeof(ExternalSnapshotState), + .prepare = external_snapshot_prepare, + .commit = external_snapshot_commit, + .abort = external_snapshot_abort, + }, + [TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = { + .instance_size = sizeof(DriveBackupState), + .prepare = drive_backup_prepare, + .abort = drive_backup_abort, + .clean = drive_backup_clean, + }, + [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = { + .instance_size = sizeof(BlockdevBackupState), + .prepare = blockdev_backup_prepare, + .abort = blockdev_backup_abort, + .clean = blockdev_backup_clean, + }, + [TRANSACTION_ACTION_KIND_ABORT] = { + .instance_size = sizeof(BlkTransactionState), + .prepare = abort_prepare, + .commit = abort_commit, + }, + [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = { + .instance_size = sizeof(InternalSnapshotState), + .prepare = internal_snapshot_prepare, + .abort = internal_snapshot_abort, + .clean = internal_snapshot_clean, + }, +}; + +/* + * 'Atomic' group operations. The operations are performed as a set, and if + * any fail then we roll back all operations in the group. + */ +void qmp_transaction(TransactionActionList *dev_list, Error **errp) +{ + TransactionActionList *dev_entry = dev_list; + BlkTransactionState *state, *next; + Error *local_err = NULL; + + QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionState) snap_bdrv_states; + QSIMPLEQ_INIT(&snap_bdrv_states); + + /* drain all i/o before any operations */ + bdrv_drain_all(); + + /* We don't do anything in this loop that commits us to the operations */ + while (NULL != dev_entry) { + TransactionAction *dev_info = NULL; + const BdrvActionOps *ops; + + dev_info = dev_entry->value; + dev_entry = dev_entry->next; + + assert(dev_info->kind < ARRAY_SIZE(actions)); + + ops = &actions[dev_info->kind]; + assert(ops->instance_size > 0); + + state = g_malloc0(ops->instance_size); + state->ops = ops; + state->action = dev_info; + QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry); + + state->ops->prepare(state, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto delete_and_fail; + } + } + + QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) { + if (state->ops->commit) { + state->ops->commit(state); + } + } + + /* success */ + goto exit; + +delete_and_fail: + /* failure, and it is all-or-none; roll back all operations */ + QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) { + if (state->ops->abort) { + state->ops->abort(state); + } + } +exit: + QSIMPLEQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) { + if (state->ops->clean) { + state->ops->clean(state); + } + g_free(state); + } +} + + +static void eject_device(BlockBackend *blk, int force, Error **errp) +{ + BlockDriverState *bs = blk_bs(blk); + AioContext *aio_context; + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { + goto out; + } + if (!blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", + bdrv_get_device_name(bs)); + goto out; + } + + if (blk_dev_is_medium_locked(blk) && !blk_dev_is_tray_open(blk)) { + blk_dev_eject_request(blk, force); + if (!force) { + error_setg(errp, "Device '%s' is locked", + bdrv_get_device_name(bs)); + goto out; + } + } + + bdrv_close(bs); + +out: + aio_context_release(aio_context); +} + +void qmp_eject(const char *device, bool has_force, bool force, Error **errp) +{ + BlockBackend *blk; + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + eject_device(blk, force, errp); +} + +void qmp_block_passwd(bool has_device, const char *device, + bool has_node_name, const char *node_name, + const char *password, Error **errp) +{ + Error *local_err = NULL; + BlockDriverState *bs; + AioContext *aio_context; + + bs = bdrv_lookup_bs(has_device ? device : NULL, + has_node_name ? node_name : NULL, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + bdrv_add_key(bs, password, errp); + + aio_context_release(aio_context); +} + +/* Assumes AioContext is held */ +static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, + int bdrv_flags, BlockDriver *drv, + const char *password, Error **errp) +{ + Error *local_err = NULL; + int ret; + + ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return; + } + + bdrv_add_key(bs, password, errp); +} + +void qmp_change_blockdev(const char *device, const char *filename, + const char *format, Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + AioContext *aio_context; + BlockDriver *drv = NULL; + int bdrv_flags; + Error *err = NULL; + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (format) { + drv = bdrv_find_whitelisted_format(format, bs->read_only); + if (!drv) { + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); + goto out; + } + } + + eject_device(blk, 0, &err); + if (err) { + error_propagate(errp, err); + goto out; + } + + bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; + bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; + + qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp); + +out: + aio_context_release(aio_context); +} + +/* throttling disk I/O limits */ +void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, + int64_t bps_wr, + int64_t iops, + int64_t iops_rd, + int64_t iops_wr, + bool has_bps_max, + int64_t bps_max, + bool has_bps_rd_max, + int64_t bps_rd_max, + bool has_bps_wr_max, + int64_t bps_wr_max, + bool has_iops_max, + int64_t iops_max, + bool has_iops_rd_max, + int64_t iops_rd_max, + bool has_iops_wr_max, + int64_t iops_wr_max, + bool has_iops_size, + int64_t iops_size, Error **errp) +{ + ThrottleConfig cfg; + BlockDriverState *bs; + BlockBackend *blk; + AioContext *aio_context; + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + bs = blk_bs(blk); + + memset(&cfg, 0, sizeof(cfg)); + cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps; + cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd; + cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr; + + cfg.buckets[THROTTLE_OPS_TOTAL].avg = iops; + cfg.buckets[THROTTLE_OPS_READ].avg = iops_rd; + cfg.buckets[THROTTLE_OPS_WRITE].avg = iops_wr; + + if (has_bps_max) { + cfg.buckets[THROTTLE_BPS_TOTAL].max = bps_max; + } + if (has_bps_rd_max) { + cfg.buckets[THROTTLE_BPS_READ].max = bps_rd_max; + } + if (has_bps_wr_max) { + cfg.buckets[THROTTLE_BPS_WRITE].max = bps_wr_max; + } + if (has_iops_max) { + cfg.buckets[THROTTLE_OPS_TOTAL].max = iops_max; + } + if (has_iops_rd_max) { + cfg.buckets[THROTTLE_OPS_READ].max = iops_rd_max; + } + if (has_iops_wr_max) { + cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max; + } + + if (has_iops_size) { + cfg.op_size = iops_size; + } + + if (!check_throttle_config(&cfg, errp)) { + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (!bs->io_limits_enabled && throttle_enabled(&cfg)) { + bdrv_io_limits_enable(bs); + } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) { + bdrv_io_limits_disable(bs); + } + + if (bs->io_limits_enabled) { + bdrv_set_io_limits(bs, &cfg); + } + + aio_context_release(aio_context); +} + +void qmp_block_dirty_bitmap_add(const char *node, const char *name, + bool has_granularity, uint32_t granularity, + Error **errp) +{ + AioContext *aio_context; + BlockDriverState *bs; + + if (!name || name[0] == '\0') { + error_setg(errp, "Bitmap name cannot be empty"); + return; + } + + bs = bdrv_lookup_bs(node, node, errp); + if (!bs) { + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (has_granularity) { + if (granularity < 512 || !is_power_of_2(granularity)) { + error_setg(errp, "Granularity must be power of 2 " + "and at least 512"); + goto out; + } + } else { + /* Default to cluster size, if available: */ + granularity = bdrv_get_default_bitmap_granularity(bs); + } + + bdrv_create_dirty_bitmap(bs, granularity, name, errp); + + out: + aio_context_release(aio_context); +} + +void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + Error **errp) +{ + AioContext *aio_context; + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp); + if (!bitmap || !bs) { + return; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently frozen and cannot be removed", + name); + goto out; + } + bdrv_dirty_bitmap_make_anon(bitmap); + bdrv_release_dirty_bitmap(bs, bitmap); + + out: + aio_context_release(aio_context); +} + +/** + * Completely clear a bitmap, for the purposes of synchronizing a bitmap + * immediately after a full backup operation. + */ +void qmp_block_dirty_bitmap_clear(const char *node, const char *name, + Error **errp) +{ + AioContext *aio_context; + BdrvDirtyBitmap *bitmap; + BlockDriverState *bs; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp); + if (!bitmap || !bs) { + return; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently frozen and cannot be modified", + name); + goto out; + } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently disabled and cannot be cleared", + name); + goto out; + } + + bdrv_clear_dirty_bitmap(bitmap); + + out: + aio_context_release(aio_context); +} + +int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *id = qdict_get_str(qdict, "id"); + BlockBackend *blk; + BlockDriverState *bs; + AioContext *aio_context; + Error *local_err = NULL; + + blk = blk_by_name(id); + if (!blk) { + error_report("Device '%s' not found", id); + return -1; + } + bs = blk_bs(blk); + + if (!blk_legacy_dinfo(blk)) { + error_report("Deleting device added with blockdev-add" + " is not supported"); + return -1; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { + error_report_err(local_err); + aio_context_release(aio_context); + return -1; + } + + /* quiesce block driver; prevent further io */ + bdrv_drain_all(); + bdrv_flush(bs); + bdrv_close(bs); + + /* if we have a device attached to this BlockDriverState + * then we need to make the drive anonymous until the device + * can be removed. If this is a drive with no device backing + * then we can just get rid of the block driver state right here. + */ + if (blk_get_attached_dev(blk)) { + blk_hide_on_behalf_of_hmp_drive_del(blk); + /* Further I/O must not pause the guest */ + bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT, + BLOCKDEV_ON_ERROR_REPORT); + } else { + blk_unref(blk); + } + + aio_context_release(aio_context); + return 0; +} + +void qmp_block_resize(bool has_device, const char *device, + bool has_node_name, const char *node_name, + int64_t size, Error **errp) +{ + Error *local_err = NULL; + BlockDriverState *bs; + AioContext *aio_context; + int ret; + + bs = bdrv_lookup_bs(has_device ? device : NULL, + has_node_name ? node_name : NULL, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (!bdrv_is_first_non_filter(bs)) { + error_set(errp, QERR_FEATURE_DISABLED, "resize"); + goto out; + } + + if (size < 0) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size"); + goto out; + } + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) { + error_set(errp, QERR_DEVICE_IN_USE, device); + goto out; + } + + /* complete all in-flight operations before resizing the device */ + bdrv_drain_all(); + + ret = bdrv_truncate(bs, size); + switch (ret) { + case 0: + break; + case -ENOMEDIUM: + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + break; + case -ENOTSUP: + error_set(errp, QERR_UNSUPPORTED); + break; + case -EACCES: + error_setg(errp, "Device '%s' is read only", device); + break; + case -EBUSY: + error_set(errp, QERR_DEVICE_IN_USE, device); + break; + default: + error_setg_errno(errp, -ret, "Could not resize"); + break; + } + +out: + aio_context_release(aio_context); +} + +static void block_job_cb(void *opaque, int ret) +{ + /* Note that this function may be executed from another AioContext besides + * the QEMU main loop. If you need to access anything that assumes the + * QEMU global mutex, use a BH or introduce a mutex. + */ + + BlockDriverState *bs = opaque; + const char *msg = NULL; + + trace_block_job_cb(bs, bs->job, ret); + + assert(bs->job); + + if (ret < 0) { + msg = strerror(-ret); + } + + if (block_job_is_cancelled(bs->job)) { + block_job_event_cancelled(bs->job); + } else { + block_job_event_completed(bs->job, msg); + } + + bdrv_put_ref_bh_schedule(bs); +} + +void qmp_block_stream(const char *device, + bool has_base, const char *base, + bool has_backing_file, const char *backing_file, + bool has_speed, int64_t speed, + bool has_on_error, BlockdevOnError on_error, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + BlockDriverState *base_bs = NULL; + AioContext *aio_context; + Error *local_err = NULL; + const char *base_name = NULL; + + if (!has_on_error) { + on_error = BLOCKDEV_ON_ERROR_REPORT; + } + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) { + goto out; + } + + if (has_base) { + base_bs = bdrv_find_backing_image(bs, base); + if (base_bs == NULL) { + error_set(errp, QERR_BASE_NOT_FOUND, base); + goto out; + } + assert(bdrv_get_aio_context(base_bs) == aio_context); + base_name = base; + } + + /* if we are streaming the entire chain, the result will have no backing + * file, and specifying one is therefore an error */ + if (base_bs == NULL && has_backing_file) { + error_setg(errp, "backing file specified, but streaming the " + "entire chain"); + goto out; + } + + /* backing_file string overrides base bs filename */ + base_name = has_backing_file ? backing_file : base_name; + + stream_start(bs, base_bs, base_name, has_speed ? speed : 0, + on_error, block_job_cb, bs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + trace_qmp_block_stream(bs, bs->job); + +out: + aio_context_release(aio_context); +} + +void qmp_block_commit(const char *device, + bool has_base, const char *base, + bool has_top, const char *top, + bool has_backing_file, const char *backing_file, + bool has_speed, int64_t speed, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + BlockDriverState *base_bs, *top_bs; + AioContext *aio_context; + Error *local_err = NULL; + /* This will be part of the QMP command, if/when the + * BlockdevOnError change for blkmirror makes it in + */ + BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT; + + if (!has_speed) { + speed = 0; + } + + /* Important Note: + * libvirt relies on the DeviceNotFound error class in order to probe for + * live commit feature versions; for this to work, we must make sure to + * perform the device lookup before any generic errors that may occur in a + * scenario in which all optional arguments are omitted. */ + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + /* drain all i/o before commits */ + bdrv_drain_all(); + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { + goto out; + } + + /* default top_bs is the active layer */ + top_bs = bs; + + if (has_top && top) { + if (strcmp(bs->filename, top) != 0) { + top_bs = bdrv_find_backing_image(bs, top); + } + } + + if (top_bs == NULL) { + error_setg(errp, "Top image file %s not found", top ? top : "NULL"); + goto out; + } + + assert(bdrv_get_aio_context(top_bs) == aio_context); + + if (has_base && base) { + base_bs = bdrv_find_backing_image(top_bs, base); + } else { + base_bs = bdrv_find_base(top_bs); + } + + if (base_bs == NULL) { + error_set(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL"); + goto out; + } + + assert(bdrv_get_aio_context(base_bs) == aio_context); + + if (bdrv_op_is_blocked(base_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { + goto out; + } + + /* Do not allow attempts to commit an image into itself */ + if (top_bs == base_bs) { + error_setg(errp, "cannot commit an image into itself"); + goto out; + } + + if (top_bs == bs) { + if (has_backing_file) { + error_setg(errp, "'backing-file' specified," + " but 'top' is the active layer"); + goto out; + } + commit_active_start(bs, base_bs, speed, on_error, block_job_cb, + bs, &local_err); + } else { + commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs, + has_backing_file ? backing_file : NULL, &local_err); + } + if (local_err != NULL) { + error_propagate(errp, local_err); + goto out; + } + +out: + aio_context_release(aio_context); +} + +void qmp_drive_backup(const char *device, const char *target, + bool has_format, const char *format, + enum MirrorSyncMode sync, + bool has_mode, enum NewImageMode mode, + bool has_speed, int64_t speed, + bool has_bitmap, const char *bitmap, + bool has_on_source_error, BlockdevOnError on_source_error, + bool has_on_target_error, BlockdevOnError on_target_error, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + BlockDriverState *target_bs; + BlockDriverState *source = NULL; + BdrvDirtyBitmap *bmap = NULL; + AioContext *aio_context; + BlockDriver *drv = NULL; + Error *local_err = NULL; + int flags; + int64_t size; + int ret; + + if (!has_speed) { + speed = 0; + } + if (!has_on_source_error) { + on_source_error = BLOCKDEV_ON_ERROR_REPORT; + } + if (!has_on_target_error) { + on_target_error = BLOCKDEV_ON_ERROR_REPORT; + } + if (!has_mode) { + mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; + } + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + /* Although backup_run has this check too, we need to use bs->drv below, so + * do an early check redundantly. */ + if (!bdrv_is_inserted(bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + goto out; + } + + if (!has_format) { + format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; + } + if (format) { + drv = bdrv_find_format(format); + if (!drv) { + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); + goto out; + } + } + + /* Early check to avoid creating target */ + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { + goto out; + } + + flags = bs->open_flags | BDRV_O_RDWR; + + /* See if we have a backing HD we can use to create our new image + * on top of. */ + if (sync == MIRROR_SYNC_MODE_TOP) { + source = bs->backing_hd; + if (!source) { + sync = MIRROR_SYNC_MODE_FULL; + } + } + if (sync == MIRROR_SYNC_MODE_NONE) { + source = bs; + } + + size = bdrv_getlength(bs); + if (size < 0) { + error_setg_errno(errp, -size, "bdrv_getlength failed"); + goto out; + } + + if (mode != NEW_IMAGE_MODE_EXISTING) { + assert(format && drv); + if (source) { + bdrv_img_create(target, format, source->filename, + source->drv->format_name, NULL, + size, flags, &local_err, false); + } else { + bdrv_img_create(target, format, NULL, NULL, NULL, + size, flags, &local_err, false); + } + } + + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + target_bs = NULL; + ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto out; + } + + bdrv_set_aio_context(target_bs, aio_context); + + if (has_bitmap) { + bmap = bdrv_find_dirty_bitmap(bs, bitmap); + if (!bmap) { + error_setg(errp, "Bitmap '%s' could not be found", bitmap); + goto out; + } + } + + backup_start(bs, target_bs, speed, sync, bmap, + on_source_error, on_target_error, + block_job_cb, bs, &local_err); + if (local_err != NULL) { + bdrv_unref(target_bs); + error_propagate(errp, local_err); + goto out; + } + +out: + aio_context_release(aio_context); +} + +BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) +{ + return bdrv_named_nodes_list(errp); +} + +void qmp_blockdev_backup(const char *device, const char *target, + enum MirrorSyncMode sync, + bool has_speed, int64_t speed, + bool has_on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + BlockDriverState *target_bs; + Error *local_err = NULL; + AioContext *aio_context; + + if (!has_speed) { + speed = 0; + } + if (!has_on_source_error) { + on_source_error = BLOCKDEV_ON_ERROR_REPORT; + } + if (!has_on_target_error) { + on_target_error = BLOCKDEV_ON_ERROR_REPORT; + } + + blk = blk_by_name(device); + if (!blk) { + error_setg(errp, "Device '%s' not found", device); + return; + } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + blk = blk_by_name(target); + if (!blk) { + error_setg(errp, "Device '%s' not found", target); + goto out; + } + target_bs = blk_bs(blk); + + bdrv_ref(target_bs); + bdrv_set_aio_context(target_bs, aio_context); + backup_start(bs, target_bs, speed, sync, NULL, on_source_error, + on_target_error, block_job_cb, bs, &local_err); + if (local_err != NULL) { + bdrv_unref(target_bs); + error_propagate(errp, local_err); + } +out: + aio_context_release(aio_context); +} + +#define DEFAULT_MIRROR_BUF_SIZE (10 << 20) + +void qmp_drive_mirror(const char *device, const char *target, + bool has_format, const char *format, + bool has_node_name, const char *node_name, + bool has_replaces, const char *replaces, + enum MirrorSyncMode sync, + bool has_mode, enum NewImageMode mode, + bool has_speed, int64_t speed, + bool has_granularity, uint32_t granularity, + bool has_buf_size, int64_t buf_size, + bool has_on_source_error, BlockdevOnError on_source_error, + bool has_on_target_error, BlockdevOnError on_target_error, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + BlockDriverState *source, *target_bs; + AioContext *aio_context; + BlockDriver *drv = NULL; + Error *local_err = NULL; + QDict *options = NULL; + int flags; + int64_t size; + int ret; + + if (!has_speed) { + speed = 0; + } + if (!has_on_source_error) { + on_source_error = BLOCKDEV_ON_ERROR_REPORT; + } + if (!has_on_target_error) { + on_target_error = BLOCKDEV_ON_ERROR_REPORT; + } + if (!has_mode) { + mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; + } + if (!has_granularity) { + granularity = 0; + } + if (!has_buf_size) { + buf_size = DEFAULT_MIRROR_BUF_SIZE; + } + + if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", + "a value in range [512B, 64MB]"); + return; + } + if (granularity & (granularity - 1)) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", "power of 2"); + return; + } + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (!bdrv_is_inserted(bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + goto out; + } + + if (!has_format) { + format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; + } + if (format) { + drv = bdrv_find_format(format); + if (!drv) { + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); + goto out; + } + } + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) { + goto out; + } + + flags = bs->open_flags | BDRV_O_RDWR; + source = bs->backing_hd; + if (!source && sync == MIRROR_SYNC_MODE_TOP) { + sync = MIRROR_SYNC_MODE_FULL; + } + if (sync == MIRROR_SYNC_MODE_NONE) { + source = bs; + } + + size = bdrv_getlength(bs); + if (size < 0) { + error_setg_errno(errp, -size, "bdrv_getlength failed"); + goto out; + } + + if (has_replaces) { + BlockDriverState *to_replace_bs; + AioContext *replace_aio_context; + int64_t replace_size; + + if (!has_node_name) { + error_setg(errp, "a node-name must be provided when replacing a" + " named node of the graph"); + goto out; + } + + to_replace_bs = check_to_replace_node(replaces, &local_err); + + if (!to_replace_bs) { + error_propagate(errp, local_err); + goto out; + } + + replace_aio_context = bdrv_get_aio_context(to_replace_bs); + aio_context_acquire(replace_aio_context); + replace_size = bdrv_getlength(to_replace_bs); + aio_context_release(replace_aio_context); + + if (size != replace_size) { + error_setg(errp, "cannot replace image with a mirror image of " + "different size"); + goto out; + } + } + + if ((sync == MIRROR_SYNC_MODE_FULL || !source) + && mode != NEW_IMAGE_MODE_EXISTING) + { + /* create new image w/o backing file */ + assert(format && drv); + bdrv_img_create(target, format, + NULL, NULL, NULL, size, flags, &local_err, false); + } else { + switch (mode) { + case NEW_IMAGE_MODE_EXISTING: + break; + case NEW_IMAGE_MODE_ABSOLUTE_PATHS: + /* create new image with backing file */ + bdrv_img_create(target, format, + source->filename, + source->drv->format_name, + NULL, size, flags, &local_err, false); + break; + default: + abort(); + } + } + + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + if (has_node_name) { + options = qdict_new(); + qdict_put(options, "node-name", qstring_from_str(node_name)); + } + + /* Mirroring takes care of copy-on-write using the source's backing + * file. + */ + target_bs = NULL; + ret = bdrv_open(&target_bs, target, NULL, options, + flags | BDRV_O_NO_BACKING, drv, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto out; + } + + bdrv_set_aio_context(target_bs, aio_context); + + /* pass the node name to replace to mirror start since it's loose coupling + * and will allow to check whether the node still exist at mirror completion + */ + mirror_start(bs, target_bs, + has_replaces ? replaces : NULL, + speed, granularity, buf_size, sync, + on_source_error, on_target_error, + block_job_cb, bs, &local_err); + if (local_err != NULL) { + bdrv_unref(target_bs); + error_propagate(errp, local_err); + goto out; + } + +out: + aio_context_release(aio_context); +} + +/* Get the block job for a given device name and acquire its AioContext */ +static BlockJob *find_block_job(const char *device, AioContext **aio_context, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + + blk = blk_by_name(device); + if (!blk) { + goto notfound; + } + bs = blk_bs(blk); + + *aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(*aio_context); + + if (!bs->job) { + aio_context_release(*aio_context); + goto notfound; + } + + return bs->job; + +notfound: + error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, + "No active block job on device '%s'", device); + *aio_context = NULL; + return NULL; +} + +void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp) +{ + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context, errp); + + if (!job) { + return; + } + + block_job_set_speed(job, speed, errp); + aio_context_release(aio_context); +} + +void qmp_block_job_cancel(const char *device, + bool has_force, bool force, Error **errp) +{ + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context, errp); + + if (!job) { + return; + } + + if (!has_force) { + force = false; + } + + if (job->user_paused && !force) { + error_setg(errp, "The block job for device '%s' is currently paused", + device); + goto out; + } + + trace_qmp_block_job_cancel(job); + block_job_cancel(job); +out: + aio_context_release(aio_context); +} + +void qmp_block_job_pause(const char *device, Error **errp) +{ + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context, errp); + + if (!job || job->user_paused) { + return; + } + + job->user_paused = true; + trace_qmp_block_job_pause(job); + block_job_pause(job); + aio_context_release(aio_context); +} + +void qmp_block_job_resume(const char *device, Error **errp) +{ + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context, errp); + + if (!job || !job->user_paused) { + return; + } + + job->user_paused = false; + trace_qmp_block_job_resume(job); + block_job_resume(job); + aio_context_release(aio_context); +} + +void qmp_block_job_complete(const char *device, Error **errp) +{ + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_block_job_complete(job); + block_job_complete(job, errp); + aio_context_release(aio_context); +} + +void qmp_change_backing_file(const char *device, + const char *image_node_name, + const char *backing_file, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs = NULL; + AioContext *aio_context; + BlockDriverState *image_bs = NULL; + Error *local_err = NULL; + bool ro; + int open_flags; + int ret; + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + if (!image_bs) { + error_setg(errp, "image file not found"); + goto out; + } + + if (bdrv_find_base(image_bs) == image_bs) { + error_setg(errp, "not allowing backing file change on an image " + "without a backing file"); + goto out; + } + + /* even though we are not necessarily operating on bs, we need it to + * determine if block ops are currently prohibited on the chain */ + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) { + goto out; + } + + /* final sanity check */ + if (!bdrv_chain_contains(bs, image_bs)) { + error_setg(errp, "'%s' and image file are not in the same chain", + device); + goto out; + } + + /* if not r/w, reopen to make r/w */ + open_flags = image_bs->open_flags; + ro = bdrv_is_read_only(image_bs); + + if (ro) { + bdrv_reopen(image_bs, open_flags | BDRV_O_RDWR, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + } + + ret = bdrv_change_backing_file(image_bs, backing_file, + image_bs->drv ? image_bs->drv->format_name : ""); + + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not change backing file to '%s'", + backing_file); + /* don't exit here, so we can try to restore open flags if + * appropriate */ + } + + if (ro) { + bdrv_reopen(image_bs, open_flags, &local_err); + if (local_err) { + error_propagate(errp, local_err); /* will preserve prior errp */ + } + } + +out: + aio_context_release(aio_context); +} + +void qmp_blockdev_add(BlockdevOptions *options, Error **errp) +{ + QmpOutputVisitor *ov = qmp_output_visitor_new(); + BlockBackend *blk; + QObject *obj; + QDict *qdict; + Error *local_err = NULL; + + /* Require an ID in the top level */ + if (!options->has_id) { + error_setg(errp, "Block device needs an ID"); + goto fail; + } + + /* TODO Sort it out in raw-posix and drive_new(): Reject aio=native with + * cache.direct=false instead of silently switching to aio=threads, except + * when called from drive_new(). + * + * For now, simply forbidding the combination for all drivers will do. */ + if (options->has_aio && options->aio == BLOCKDEV_AIO_OPTIONS_NATIVE) { + bool direct = options->has_cache && + options->cache->has_direct && + options->cache->direct; + if (!direct) { + error_setg(errp, "aio=native requires cache.direct=true"); + goto fail; + } + } + + visit_type_BlockdevOptions(qmp_output_get_visitor(ov), + &options, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + + obj = qmp_output_get_qobject(ov); + qdict = qobject_to_qdict(obj); + + qdict_flatten(qdict); + + blk = blockdev_init(NULL, qdict, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + + if (bdrv_key_required(blk_bs(blk))) { + blk_unref(blk); + error_setg(errp, "blockdev-add doesn't support encrypted devices"); + goto fail; + } + +fail: + qmp_output_visitor_cleanup(ov); +} + +BlockJobInfoList *qmp_query_block_jobs(Error **errp) +{ + BlockJobInfoList *head = NULL, **p_next = &head; + BlockDriverState *bs; + + for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + + if (bs->job) { + BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1); + elem->value = block_job_query(bs->job); + *p_next = elem; + p_next = &elem->next; + } + + aio_context_release(aio_context); + } + + return head; +} + +QemuOptsList qemu_common_drive_opts = { + .name = "drive", + .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), + .desc = { + { + .name = "snapshot", + .type = QEMU_OPT_BOOL, + .help = "enable/disable snapshot mode", + },{ + .name = "discard", + .type = QEMU_OPT_STRING, + .help = "discard operation (ignore/off, unmap/on)", + },{ + .name = "cache.writeback", + .type = QEMU_OPT_BOOL, + .help = "enables writeback mode for any caches", + },{ + .name = "cache.direct", + .type = QEMU_OPT_BOOL, + .help = "enables use of O_DIRECT (bypass the host page cache)", + },{ + .name = "cache.no-flush", + .type = QEMU_OPT_BOOL, + .help = "ignore any flush requests for the device", + },{ + .name = "aio", + .type = QEMU_OPT_STRING, + .help = "host AIO implementation (threads, native)", + },{ + .name = "format", + .type = QEMU_OPT_STRING, + .help = "disk format (raw, qcow2, ...)", + },{ + .name = "rerror", + .type = QEMU_OPT_STRING, + .help = "read error action", + },{ + .name = "werror", + .type = QEMU_OPT_STRING, + .help = "write error action", + },{ + .name = "read-only", + .type = QEMU_OPT_BOOL, + .help = "open drive file as read-only", + },{ + .name = "throttling.iops-total", + .type = QEMU_OPT_NUMBER, + .help = "limit total I/O operations per second", + },{ + .name = "throttling.iops-read", + .type = QEMU_OPT_NUMBER, + .help = "limit read operations per second", + },{ + .name = "throttling.iops-write", + .type = QEMU_OPT_NUMBER, + .help = "limit write operations per second", + },{ + .name = "throttling.bps-total", + .type = QEMU_OPT_NUMBER, + .help = "limit total bytes per second", + },{ + .name = "throttling.bps-read", + .type = QEMU_OPT_NUMBER, + .help = "limit read bytes per second", + },{ + .name = "throttling.bps-write", + .type = QEMU_OPT_NUMBER, + .help = "limit write bytes per second", + },{ + .name = "throttling.iops-total-max", + .type = QEMU_OPT_NUMBER, + .help = "I/O operations burst", + },{ + .name = "throttling.iops-read-max", + .type = QEMU_OPT_NUMBER, + .help = "I/O operations read burst", + },{ + .name = "throttling.iops-write-max", + .type = QEMU_OPT_NUMBER, + .help = "I/O operations write burst", + },{ + .name = "throttling.bps-total-max", + .type = QEMU_OPT_NUMBER, + .help = "total bytes burst", + },{ + .name = "throttling.bps-read-max", + .type = QEMU_OPT_NUMBER, + .help = "total bytes read burst", + },{ + .name = "throttling.bps-write-max", + .type = QEMU_OPT_NUMBER, + .help = "total bytes write burst", + },{ + .name = "throttling.iops-size", + .type = QEMU_OPT_NUMBER, + .help = "when limiting by iops max size of an I/O in bytes", + },{ + .name = "copy-on-read", + .type = QEMU_OPT_BOOL, + .help = "copy read data from backing file into image file", + },{ + .name = "detect-zeroes", + .type = QEMU_OPT_STRING, + .help = "try to optimize zero writes (off, on, unmap)", + }, + { /* end of list */ } + }, +}; + +QemuOptsList qemu_drive_opts = { + .name = "drive", + .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), + .desc = { + /* + * no elements => accept any params + * validation will happen later + */ + { /* end of list */ } + }, +}; diff --git a/blockjob.c b/blockjob.c new file mode 100644 index 0000000..2755465 --- /dev/null +++ b/blockjob.c @@ -0,0 +1,401 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config-host.h" +#include "qemu-common.h" +#include "trace.h" +#include "block/block.h" +#include "block/blockjob.h" +#include "block/block_int.h" +#include "qapi/qmp/qjson.h" +#include "block/coroutine.h" +#include "qmp-commands.h" +#include "qemu/timer.h" +#include "qapi-event.h" + +void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, + int64_t speed, BlockCompletionFunc *cb, + void *opaque, Error **errp) +{ + BlockJob *job; + + if (bs->job) { + error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs)); + return NULL; + } + bdrv_ref(bs); + job = g_malloc0(driver->instance_size); + error_setg(&job->blocker, "block device is in use by block job: %s", + BlockJobType_lookup[driver->job_type]); + bdrv_op_block_all(bs, job->blocker); + bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); + + job->driver = driver; + job->bs = bs; + job->cb = cb; + job->opaque = opaque; + job->busy = true; + bs->job = job; + + /* Only set speed when necessary to avoid NotSupported error */ + if (speed != 0) { + Error *local_err = NULL; + + block_job_set_speed(job, speed, &local_err); + if (local_err) { + bs->job = NULL; + bdrv_op_unblock_all(bs, job->blocker); + error_free(job->blocker); + g_free(job); + error_propagate(errp, local_err); + return NULL; + } + } + return job; +} + +void block_job_completed(BlockJob *job, int ret) +{ + BlockDriverState *bs = job->bs; + + assert(bs->job == job); + job->cb(job->opaque, ret); + bs->job = NULL; + bdrv_op_unblock_all(bs, job->blocker); + error_free(job->blocker); + g_free(job); +} + +void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) +{ + Error *local_err = NULL; + + if (!job->driver->set_speed) { + error_set(errp, QERR_UNSUPPORTED); + return; + } + job->driver->set_speed(job, speed, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + job->speed = speed; +} + +void block_job_complete(BlockJob *job, Error **errp) +{ + if (job->pause_count || job->cancelled || !job->driver->complete) { + error_set(errp, QERR_BLOCK_JOB_NOT_READY, + bdrv_get_device_name(job->bs)); + return; + } + + job->driver->complete(job, errp); +} + +void block_job_pause(BlockJob *job) +{ + job->pause_count++; +} + +bool block_job_is_paused(BlockJob *job) +{ + return job->pause_count > 0; +} + +void block_job_resume(BlockJob *job) +{ + assert(job->pause_count > 0); + job->pause_count--; + if (job->pause_count) { + return; + } + block_job_enter(job); +} + +void block_job_enter(BlockJob *job) +{ + block_job_iostatus_reset(job); + if (job->co && !job->busy) { + qemu_coroutine_enter(job->co, NULL); + } +} + +void block_job_cancel(BlockJob *job) +{ + job->cancelled = true; + block_job_enter(job); +} + +bool block_job_is_cancelled(BlockJob *job) +{ + return job->cancelled; +} + +void block_job_iostatus_reset(BlockJob *job) +{ + job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; + if (job->driver->iostatus_reset) { + job->driver->iostatus_reset(job); + } +} + +struct BlockFinishData { + BlockJob *job; + BlockCompletionFunc *cb; + void *opaque; + bool cancelled; + int ret; +}; + +static void block_job_finish_cb(void *opaque, int ret) +{ + struct BlockFinishData *data = opaque; + + data->cancelled = block_job_is_cancelled(data->job); + data->ret = ret; + data->cb(data->opaque, ret); +} + +static int block_job_finish_sync(BlockJob *job, + void (*finish)(BlockJob *, Error **errp), + Error **errp) +{ + struct BlockFinishData data; + BlockDriverState *bs = job->bs; + Error *local_err = NULL; + + assert(bs->job == job); + + /* Set up our own callback to store the result and chain to + * the original callback. + */ + data.job = job; + data.cb = job->cb; + data.opaque = job->opaque; + data.ret = -EINPROGRESS; + job->cb = block_job_finish_cb; + job->opaque = &data; + finish(job, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EBUSY; + } + while (data.ret == -EINPROGRESS) { + aio_poll(bdrv_get_aio_context(bs), true); + } + return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret; +} + +/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be + * used with block_job_finish_sync() without the need for (rather nasty) + * function pointer casts there. */ +static void block_job_cancel_err(BlockJob *job, Error **errp) +{ + block_job_cancel(job); +} + +int block_job_cancel_sync(BlockJob *job) +{ + return block_job_finish_sync(job, &block_job_cancel_err, NULL); +} + +int block_job_complete_sync(BlockJob *job, Error **errp) +{ + return block_job_finish_sync(job, &block_job_complete, errp); +} + +void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (block_job_is_cancelled(job)) { + return; + } + + job->busy = false; + if (block_job_is_paused(job)) { + qemu_coroutine_yield(); + } else { + co_aio_sleep_ns(bdrv_get_aio_context(job->bs), type, ns); + } + job->busy = true; +} + +void block_job_yield(BlockJob *job) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (block_job_is_cancelled(job)) { + return; + } + + job->busy = false; + qemu_coroutine_yield(); + job->busy = true; +} + +BlockJobInfo *block_job_query(BlockJob *job) +{ + BlockJobInfo *info = g_new0(BlockJobInfo, 1); + info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]); + info->device = g_strdup(bdrv_get_device_name(job->bs)); + info->len = job->len; + info->busy = job->busy; + info->paused = job->pause_count > 0; + info->offset = job->offset; + info->speed = job->speed; + info->io_status = job->iostatus; + info->ready = job->ready; + return info; +} + +static void block_job_iostatus_set_err(BlockJob *job, int error) +{ + if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + job->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : + BLOCK_DEVICE_IO_STATUS_FAILED; + } +} + +void block_job_event_cancelled(BlockJob *job) +{ + qapi_event_send_block_job_cancelled(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, + &error_abort); +} + +void block_job_event_completed(BlockJob *job, const char *msg) +{ + qapi_event_send_block_job_completed(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, + !!msg, + msg, + &error_abort); +} + +void block_job_event_ready(BlockJob *job) +{ + job->ready = true; + + qapi_event_send_block_job_ready(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, &error_abort); +} + +BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, + BlockdevOnError on_err, + int is_read, int error) +{ + BlockErrorAction action; + + switch (on_err) { + case BLOCKDEV_ON_ERROR_ENOSPC: + action = (error == ENOSPC) ? + BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; + break; + case BLOCKDEV_ON_ERROR_STOP: + action = BLOCK_ERROR_ACTION_STOP; + break; + case BLOCKDEV_ON_ERROR_REPORT: + action = BLOCK_ERROR_ACTION_REPORT; + break; + case BLOCKDEV_ON_ERROR_IGNORE: + action = BLOCK_ERROR_ACTION_IGNORE; + break; + default: + abort(); + } + qapi_event_send_block_job_error(bdrv_get_device_name(job->bs), + is_read ? IO_OPERATION_TYPE_READ : + IO_OPERATION_TYPE_WRITE, + action, &error_abort); + if (action == BLOCK_ERROR_ACTION_STOP) { + /* make the pause user visible, which will be resumed from QMP. */ + job->user_paused = true; + block_job_pause(job); + block_job_iostatus_set_err(job, error); + if (bs != job->bs) { + bdrv_iostatus_set_err(bs, error); + } + } + return action; +} + +typedef struct { + BlockJob *job; + QEMUBH *bh; + AioContext *aio_context; + BlockJobDeferToMainLoopFn *fn; + void *opaque; +} BlockJobDeferToMainLoopData; + +static void block_job_defer_to_main_loop_bh(void *opaque) +{ + BlockJobDeferToMainLoopData *data = opaque; + AioContext *aio_context; + + qemu_bh_delete(data->bh); + + /* Prevent race with block_job_defer_to_main_loop() */ + aio_context_acquire(data->aio_context); + + /* Fetch BDS AioContext again, in case it has changed */ + aio_context = bdrv_get_aio_context(data->job->bs); + aio_context_acquire(aio_context); + + data->fn(data->job, data->opaque); + + aio_context_release(aio_context); + + aio_context_release(data->aio_context); + + g_free(data); +} + +void block_job_defer_to_main_loop(BlockJob *job, + BlockJobDeferToMainLoopFn *fn, + void *opaque) +{ + BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data)); + data->job = job; + data->bh = qemu_bh_new(block_job_defer_to_main_loop_bh, data); + data->aio_context = bdrv_get_aio_context(job->bs); + data->fn = fn; + data->opaque = opaque; + + qemu_bh_schedule(data->bh); +} diff --git a/bootdevice.c b/bootdevice.c new file mode 100644 index 0000000..3cdc0d7 --- /dev/null +++ b/bootdevice.c @@ -0,0 +1,341 @@ +/* + * QEMU Boot Device Implement + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysemu/sysemu.h" +#include "qapi/visitor.h" +#include "qemu/error-report.h" +#include "hw/hw.h" + +typedef struct FWBootEntry FWBootEntry; + +struct FWBootEntry { + QTAILQ_ENTRY(FWBootEntry) link; + int32_t bootindex; + DeviceState *dev; + char *suffix; +}; + +static QTAILQ_HEAD(, FWBootEntry) fw_boot_order = + QTAILQ_HEAD_INITIALIZER(fw_boot_order); +static QEMUBootSetHandler *boot_set_handler; +static void *boot_set_opaque; + +void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) +{ + boot_set_handler = func; + boot_set_opaque = opaque; +} + +void qemu_boot_set(const char *boot_order, Error **errp) +{ + Error *local_err = NULL; + + if (!boot_set_handler) { + error_setg(errp, "no function defined to set boot device list for" + " this architecture"); + return; + } + + validate_bootdevices(boot_order, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + boot_set_handler(boot_set_opaque, boot_order, errp); +} + +void validate_bootdevices(const char *devices, Error **errp) +{ + /* We just do some generic consistency checks */ + const char *p; + int bitmap = 0; + + for (p = devices; *p != '\0'; p++) { + /* Allowed boot devices are: + * a-b: floppy disk drives + * c-f: IDE disk drives + * g-m: machine implementation dependent drives + * n-p: network devices + * It's up to each machine implementation to check if the given boot + * devices match the actual hardware implementation and firmware + * features. + */ + if (*p < 'a' || *p > 'p') { + error_setg(errp, "Invalid boot device '%c'", *p); + return; + } + if (bitmap & (1 << (*p - 'a'))) { + error_setg(errp, "Boot device '%c' was given twice", *p); + return; + } + bitmap |= 1 << (*p - 'a'); + } +} + +void restore_boot_order(void *opaque) +{ + char *normal_boot_order = opaque; + static int first = 1; + + /* Restore boot order and remove ourselves after the first boot */ + if (first) { + first = 0; + return; + } + + if (boot_set_handler) { + qemu_boot_set(normal_boot_order, &error_abort); + } + + qemu_unregister_reset(restore_boot_order, normal_boot_order); + g_free(normal_boot_order); +} + +void check_boot_index(int32_t bootindex, Error **errp) +{ + FWBootEntry *i; + + if (bootindex >= 0) { + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if (i->bootindex == bootindex) { + error_setg(errp, "The bootindex %d has already been used", + bootindex); + return; + } + } + } +} + +void del_boot_device_path(DeviceState *dev, const char *suffix) +{ + FWBootEntry *i; + + if (dev == NULL) { + return; + } + + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if ((!suffix || !g_strcmp0(i->suffix, suffix)) && + i->dev == dev) { + QTAILQ_REMOVE(&fw_boot_order, i, link); + g_free(i->suffix); + g_free(i); + + break; + } + } +} + +void add_boot_device_path(int32_t bootindex, DeviceState *dev, + const char *suffix) +{ + FWBootEntry *node, *i; + + if (bootindex < 0) { + del_boot_device_path(dev, suffix); + return; + } + + assert(dev != NULL || suffix != NULL); + + del_boot_device_path(dev, suffix); + + node = g_malloc0(sizeof(FWBootEntry)); + node->bootindex = bootindex; + node->suffix = g_strdup(suffix); + node->dev = dev; + + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if (i->bootindex == bootindex) { + error_report("Two devices with same boot index %d", bootindex); + exit(1); + } else if (i->bootindex < bootindex) { + continue; + } + QTAILQ_INSERT_BEFORE(i, node, link); + return; + } + QTAILQ_INSERT_TAIL(&fw_boot_order, node, link); +} + +DeviceState *get_boot_device(uint32_t position) +{ + uint32_t counter = 0; + FWBootEntry *i = NULL; + DeviceState *res = NULL; + + if (!QTAILQ_EMPTY(&fw_boot_order)) { + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if (counter == position) { + res = i->dev; + break; + } + counter++; + } + } + return res; +} + +/* + * This function returns null terminated string that consist of new line + * separated device paths. + * + * memory pointed by "size" is assigned total length of the array in bytes + * + */ +char *get_boot_devices_list(size_t *size, bool ignore_suffixes) +{ + FWBootEntry *i; + size_t total = 0; + char *list = NULL; + + QTAILQ_FOREACH(i, &fw_boot_order, link) { + char *devpath = NULL, *suffix = NULL; + char *bootpath; + char *d; + size_t len; + + if (i->dev) { + devpath = qdev_get_fw_dev_path(i->dev); + assert(devpath); + } + + if (!ignore_suffixes) { + if (i->dev) { + d = qdev_get_own_fw_dev_path_from_handler(i->dev->parent_bus, + i->dev); + if (d) { + assert(!i->suffix); + suffix = d; + } else { + suffix = g_strdup(i->suffix); + } + } else { + suffix = g_strdup(i->suffix); + } + } + + bootpath = g_strdup_printf("%s%s", + devpath ? devpath : "", + suffix ? suffix : ""); + g_free(devpath); + g_free(suffix); + + if (total) { + list[total-1] = '\n'; + } + len = strlen(bootpath) + 1; + list = g_realloc(list, total + len); + memcpy(&list[total], bootpath, len); + total += len; + g_free(bootpath); + } + + *size = total; + + if (boot_strict && *size > 0) { + list[total-1] = '\n'; + list = g_realloc(list, total + 5); + memcpy(&list[total], "HALT", 5); + *size = total + 5; + } + return list; +} + +typedef struct { + int32_t *bootindex; + const char *suffix; + DeviceState *dev; +} BootIndexProperty; + +static void device_get_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + BootIndexProperty *prop = opaque; + visit_type_int32(v, prop->bootindex, name, errp); +} + +static void device_set_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + BootIndexProperty *prop = opaque; + int32_t boot_index; + Error *local_err = NULL; + + visit_type_int32(v, &boot_index, name, &local_err); + if (local_err) { + goto out; + } + /* check whether bootindex is present in fw_boot_order list */ + check_boot_index(boot_index, &local_err); + if (local_err) { + goto out; + } + /* change bootindex to a new one */ + *prop->bootindex = boot_index; + + add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix); + +out: + if (local_err) { + error_propagate(errp, local_err); + } +} + +static void property_release_bootindex(Object *obj, const char *name, + void *opaque) + +{ + BootIndexProperty *prop = opaque; + + del_boot_device_path(prop->dev, prop->suffix); + g_free(prop); +} + +void device_add_bootindex_property(Object *obj, int32_t *bootindex, + const char *name, const char *suffix, + DeviceState *dev, Error **errp) +{ + Error *local_err = NULL; + BootIndexProperty *prop = g_malloc0(sizeof(*prop)); + + prop->bootindex = bootindex; + prop->suffix = suffix; + prop->dev = dev; + + object_property_add(obj, name, "int32", + device_get_bootindex, + device_set_bootindex, + property_release_bootindex, + prop, &local_err); + + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + return; + } + /* initialize devices' bootindex property to -1 */ + object_property_set_int(obj, -1, name, NULL); +} diff --git a/bsd-user/Makefile.objs b/bsd-user/Makefile.objs new file mode 100644 index 0000000..5e77f57 --- /dev/null +++ b/bsd-user/Makefile.objs @@ -0,0 +1,2 @@ +obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ + uaccess.o diff --git a/bsd-user/bsd-mman.h b/bsd-user/bsd-mman.h new file mode 100644 index 0000000..910e8c1 --- /dev/null +++ b/bsd-user/bsd-mman.h @@ -0,0 +1,121 @@ +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mman.h 8.2 (Berkeley) 1/9/95 + * $FreeBSD: src/sys/sys/mman.h,v 1.42 2008/03/28 04:29:27 ps Exp $ + */ + +#define TARGET_FREEBSD_MAP_RESERVED0080 0x0080 /* previously misimplemented MAP_INHERIT */ +#define TARGET_FREEBSD_MAP_RESERVED0100 0x0100 /* previously unimplemented MAP_NOEXTEND */ +#define TARGET_FREEBSD_MAP_STACK 0x0400 /* region grows down, like a stack */ +#define TARGET_FREEBSD_MAP_NOSYNC 0x0800 /* page to but do not sync underlying file */ + +#define TARGET_FREEBSD_MAP_FLAGMASK 0x1ff7 + +/* $NetBSD: mman.h,v 1.42 2008/11/18 22:13:49 ad Exp $ */ + +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mman.h 8.2 (Berkeley) 1/9/95 + */ +#define TARGET_NETBSD_MAP_INHERIT 0x0080 /* region is retained after exec */ +#define TARGET_NETBSD_MAP_TRYFIXED 0x0400 /* attempt hint address, even within break */ +#define TARGET_NETBSD_MAP_WIRED 0x0800 /* mlock() mapping when it is established */ + +#define TARGET_NETBSD_MAP_STACK 0x2000 /* allocated from memory, swap space (stack) */ + +#define TARGET_NETBSD_MAP_FLAGMASK 0x3ff7 + +/* $OpenBSD: mman.h,v 1.18 2003/07/21 22:52:19 tedu Exp $ */ +/* $NetBSD: mman.h,v 1.11 1995/03/26 20:24:23 jtc Exp $ */ + +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mman.h 8.1 (Berkeley) 6/2/93 + */ + +#define TARGET_OPENBSD_MAP_INHERIT 0x0080 /* region is retained after exec */ +#define TARGET_OPENBSD_MAP_NOEXTEND 0x0100 /* for MAP_FILE, don't change file size */ +#define TARGET_OPENBSD_MAP_TRYFIXED 0x0400 /* attempt hint address, even within heap */ + +#define TARGET_OPENBSD_MAP_FLAGMASK 0x17f7 + +// XXX +#define TARGET_BSD_MAP_FLAGMASK 0x3ff7 diff --git a/bsd-user/bsdload.c b/bsd-user/bsdload.c new file mode 100644 index 0000000..2abc713 --- /dev/null +++ b/bsd-user/bsdload.c @@ -0,0 +1,202 @@ +/* Code for loading BSD executables. Mostly linux kernel code. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "qemu.h" + +#define TARGET_NGROUPS 32 + +/* ??? This should really be somewhere else. */ +abi_long memcpy_to_target(abi_ulong dest, const void *src, + unsigned long len) +{ + void *host_ptr; + + host_ptr = lock_user(VERIFY_WRITE, dest, len, 0); + if (!host_ptr) + return -TARGET_EFAULT; + memcpy(host_ptr, src, len); + unlock_user(host_ptr, dest, 1); + return 0; +} + +static int in_group_p(gid_t g) +{ + /* return TRUE if we're in the specified group, FALSE otherwise */ + int ngroup; + int i; + gid_t grouplist[TARGET_NGROUPS]; + + ngroup = getgroups(TARGET_NGROUPS, grouplist); + for(i = 0; i < ngroup; i++) { + if(grouplist[i] == g) { + return 1; + } + } + return 0; +} + +static int count(char ** vec) +{ + int i; + + for(i = 0; *vec; i++) { + vec++; + } + + return(i); +} + +static int prepare_binprm(struct linux_binprm *bprm) +{ + struct stat st; + int mode; + int retval, id_change; + + if(fstat(bprm->fd, &st) < 0) { + return(-errno); + } + + mode = st.st_mode; + if(!S_ISREG(mode)) { /* Must be regular file */ + return(-EACCES); + } + if(!(mode & 0111)) { /* Must have at least one execute bit set */ + return(-EACCES); + } + + bprm->e_uid = geteuid(); + bprm->e_gid = getegid(); + id_change = 0; + + /* Set-uid? */ + if(mode & S_ISUID) { + bprm->e_uid = st.st_uid; + if(bprm->e_uid != geteuid()) { + id_change = 1; + } + } + + /* Set-gid? */ + /* + * If setgid is set but no group execute bit then this + * is a candidate for mandatory locking, not a setgid + * executable. + */ + if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { + bprm->e_gid = st.st_gid; + if (!in_group_p(bprm->e_gid)) { + id_change = 1; + } + } + + memset(bprm->buf, 0, sizeof(bprm->buf)); + retval = lseek(bprm->fd, 0L, SEEK_SET); + if(retval >= 0) { + retval = read(bprm->fd, bprm->buf, 128); + } + if(retval < 0) { + perror("prepare_binprm"); + exit(-1); + /* return(-errno); */ + } + else { + return(retval); + } +} + +/* Construct the envp and argv tables on the target stack. */ +abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, + abi_ulong stringp, int push_ptr) +{ + int n = sizeof(abi_ulong); + abi_ulong envp; + abi_ulong argv; + + sp -= (envc + 1) * n; + envp = sp; + sp -= (argc + 1) * n; + argv = sp; + if (push_ptr) { + /* FIXME - handle put_user() failures */ + sp -= n; + put_user_ual(envp, sp); + sp -= n; + put_user_ual(argv, sp); + } + sp -= n; + /* FIXME - handle put_user() failures */ + put_user_ual(argc, sp); + + while (argc-- > 0) { + /* FIXME - handle put_user() failures */ + put_user_ual(stringp, argv); + argv += n; + stringp += target_strlen(stringp) + 1; + } + /* FIXME - handle put_user() failures */ + put_user_ual(0, argv); + while (envc-- > 0) { + /* FIXME - handle put_user() failures */ + put_user_ual(stringp, envp); + envp += n; + stringp += target_strlen(stringp) + 1; + } + /* FIXME - handle put_user() failures */ + put_user_ual(0, envp); + + return sp; +} + +int loader_exec(const char * filename, char ** argv, char ** envp, + struct target_pt_regs * regs, struct image_info *infop) +{ + struct linux_binprm bprm; + int retval; + int i; + + bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); + for (i=0 ; i=0) { + if (bprm.buf[0] == 0x7f + && bprm.buf[1] == 'E' + && bprm.buf[2] == 'L' + && bprm.buf[3] == 'F') { + retval = load_elf_binary(&bprm,regs,infop); + } else { + fprintf(stderr, "Unknown binary format\n"); + return -1; + } + } + + if(retval>=0) { + /* success. Initialize important registers */ + do_init_thread(regs, infop); + return retval; + } + + /* Something went wrong, return the inode and free the argument pages*/ + for (i=0 ; i +#include +#include +#include +#include +#include +#include +#include + +#include "qemu.h" +#include "disas/disas.h" + +#ifdef _ARCH_PPC64 +#undef ARCH_DLINFO +#undef ELF_PLATFORM +#undef ELF_HWCAP +#undef ELF_CLASS +#undef ELF_DATA +#undef ELF_ARCH +#endif + +/* from personality.h */ + +/* + * Flags for bug emulation. + * + * These occupy the top three bytes. + */ +enum { + ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization of VA space */ + FDPIC_FUNCPTRS = 0x0080000, /* userspace function ptrs point to descriptors + * (signal handling) + */ + MMAP_PAGE_ZERO = 0x0100000, + ADDR_COMPAT_LAYOUT = 0x0200000, + READ_IMPLIES_EXEC = 0x0400000, + ADDR_LIMIT_32BIT = 0x0800000, + SHORT_INODE = 0x1000000, + WHOLE_SECONDS = 0x2000000, + STICKY_TIMEOUTS = 0x4000000, + ADDR_LIMIT_3GB = 0x8000000, +}; + +/* + * Personality types. + * + * These go in the low byte. Avoid using the top bit, it will + * conflict with error returns. + */ +enum { + PER_LINUX = 0x0000, + PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT, + PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS, + PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, + PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE, + PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS | + WHOLE_SECONDS | SHORT_INODE, + PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS, + PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE, + PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS, + PER_BSD = 0x0006, + PER_SUNOS = 0x0006 | STICKY_TIMEOUTS, + PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE, + PER_LINUX32 = 0x0008, + PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB, + PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */ + PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */ + PER_IRIX64 = 0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */ + PER_RISCOS = 0x000c, + PER_SOLARIS = 0x000d | STICKY_TIMEOUTS, + PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, + PER_OSF4 = 0x000f, /* OSF/1 v4 */ + PER_HPUX = 0x0010, + PER_MASK = 0x00ff, +}; + +/* + * Return the base personality without flags. + */ +#define personality(pers) (pers & PER_MASK) + +/* this flag is uneffective under linux too, should be deleted */ +#ifndef MAP_DENYWRITE +#define MAP_DENYWRITE 0 +#endif + +/* should probably go in elf.h */ +#ifndef ELIBBAD +#define ELIBBAD 80 +#endif + +#ifdef TARGET_I386 + +#define ELF_PLATFORM get_elf_platform() + +static const char *get_elf_platform(void) +{ + static char elf_platform[] = "i386"; + int family = object_property_get_int(OBJECT(thread_cpu), "family", NULL); + if (family > 6) + family = 6; + if (family >= 3) + elf_platform[1] = '0' + family; + return elf_platform; +} + +#define ELF_HWCAP get_elf_hwcap() + +static uint32_t get_elf_hwcap(void) +{ + X86CPU *cpu = X86_CPU(thread_cpu); + + return cpu->env.features[FEAT_1_EDX]; +} + +#ifdef TARGET_X86_64 +#define ELF_START_MMAP 0x2aaaaab000ULL +#define elf_check_arch(x) ( ((x) == ELF_ARCH) ) + +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_X86_64 + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->rax = 0; + regs->rsp = infop->start_stack; + regs->rip = infop->entry; + if (bsd_type == target_freebsd) { + regs->rdi = infop->start_stack; + } +} + +#else + +#define ELF_START_MMAP 0x80000000 + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) ) + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_386 + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->esp = infop->start_stack; + regs->eip = infop->entry; + + /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program + starts %edx contains a pointer to a function which might be + registered using `atexit'. This provides a mean for the + dynamic linker to call DT_FINI functions for shared libraries + that have been loaded before the code runs. + + A value of 0 tells we have no such handler. */ + regs->edx = 0; +} +#endif + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#endif + +#ifdef TARGET_ARM + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_ARM ) + +#define ELF_CLASS ELFCLASS32 +#ifdef TARGET_WORDS_BIGENDIAN +#define ELF_DATA ELFDATA2MSB +#else +#define ELF_DATA ELFDATA2LSB +#endif +#define ELF_ARCH EM_ARM + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + abi_long stack = infop->start_stack; + memset(regs, 0, sizeof(*regs)); + regs->ARM_cpsr = 0x10; + if (infop->entry & 1) + regs->ARM_cpsr |= CPSR_T; + regs->ARM_pc = infop->entry & 0xfffffffe; + regs->ARM_sp = infop->start_stack; + /* FIXME - what to for failure of get_user()? */ + get_user_ual(regs->ARM_r2, stack + 8); /* envp */ + get_user_ual(regs->ARM_r1, stack + 4); /* envp */ + /* XXX: it seems that r0 is zeroed after ! */ + regs->ARM_r0 = 0; + /* For uClinux PIC binaries. */ + /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ + regs->ARM_r10 = infop->start_data; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +enum +{ + ARM_HWCAP_ARM_SWP = 1 << 0, + ARM_HWCAP_ARM_HALF = 1 << 1, + ARM_HWCAP_ARM_THUMB = 1 << 2, + ARM_HWCAP_ARM_26BIT = 1 << 3, + ARM_HWCAP_ARM_FAST_MULT = 1 << 4, + ARM_HWCAP_ARM_FPA = 1 << 5, + ARM_HWCAP_ARM_VFP = 1 << 6, + ARM_HWCAP_ARM_EDSP = 1 << 7, +}; + +#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \ + | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \ + | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP) + +#endif + +#ifdef TARGET_SPARC +#ifdef TARGET_SPARC64 + +#define ELF_START_MMAP 0x80000000 + +#ifndef TARGET_ABI32 +#define elf_check_arch(x) ( (x) == EM_SPARCV9 || (x) == EM_SPARC32PLUS ) +#else +#define elf_check_arch(x) ( (x) == EM_SPARC32PLUS || (x) == EM_SPARC ) +#endif + +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_SPARCV9 + +#define STACK_BIAS 2047 + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ +#ifndef TARGET_ABI32 + regs->tstate = 0; +#endif + regs->pc = infop->entry; + regs->npc = regs->pc + 4; + regs->y = 0; +#ifdef TARGET_ABI32 + regs->u_regs[14] = infop->start_stack - 16 * 4; +#else + if (personality(infop->personality) == PER_LINUX32) + regs->u_regs[14] = infop->start_stack - 16 * 4; + else { + regs->u_regs[14] = infop->start_stack - 16 * 8 - STACK_BIAS; + if (bsd_type == target_freebsd) { + regs->u_regs[8] = infop->start_stack; + regs->u_regs[11] = infop->start_stack; + } + } +#endif +} + +#else +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_SPARC ) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_SPARC + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->psr = 0; + regs->pc = infop->entry; + regs->npc = regs->pc + 4; + regs->y = 0; + regs->u_regs[14] = infop->start_stack - 16 * 4; +} + +#endif +#endif + +#ifdef TARGET_PPC + +#define ELF_START_MMAP 0x80000000 + +#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) + +#define elf_check_arch(x) ( (x) == EM_PPC64 ) + +#define ELF_CLASS ELFCLASS64 + +#else + +#define elf_check_arch(x) ( (x) == EM_PPC ) + +#define ELF_CLASS ELFCLASS32 + +#endif + +#ifdef TARGET_WORDS_BIGENDIAN +#define ELF_DATA ELFDATA2MSB +#else +#define ELF_DATA ELFDATA2LSB +#endif +#define ELF_ARCH EM_PPC + +/* + * We need to put in some extra aux table entries to tell glibc what + * the cache block size is, so it can use the dcbz instruction safely. + */ +#define AT_DCACHEBSIZE 19 +#define AT_ICACHEBSIZE 20 +#define AT_UCACHEBSIZE 21 +/* A special ignored type value for PPC, for glibc compatibility. */ +#define AT_IGNOREPPC 22 +/* + * The requirements here are: + * - keep the final alignment of sp (sp & 0xf) + * - make sure the 32-bit value at the first 16 byte aligned position of + * AUXV is greater than 16 for glibc compatibility. + * AT_IGNOREPPC is used for that. + * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC, + * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. + */ +#define DLINFO_ARCH_ITEMS 5 +#define ARCH_DLINFO \ +do { \ + NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \ + NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \ + NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ + /* \ + * Now handle glibc compatibility. \ + */ \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + } while (0) + +static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop) +{ + abi_ulong pos = infop->start_stack; + abi_ulong tmp; +#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) + abi_ulong entry, toc; +#endif + + _regs->gpr[1] = infop->start_stack; +#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) + get_user_u64(entry, infop->entry); + entry += infop->load_addr; + get_user_u64(toc, infop->entry + 8); + toc += infop->load_addr; + _regs->gpr[2] = toc; + infop->entry = entry; +#endif + _regs->nip = infop->entry; + /* Note that isn't exactly what regular kernel does + * but this is what the ABI wants and is needed to allow + * execution of PPC BSD programs. + */ + /* FIXME - what to for failure of get_user()? */ + get_user_ual(_regs->gpr[3], pos); + pos += sizeof(abi_ulong); + _regs->gpr[4] = pos; + for (tmp = 1; tmp != 0; pos += sizeof(abi_ulong)) { + get_user_ual(tmp, pos); + } + _regs->gpr[5] = pos; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#endif + +#ifdef TARGET_MIPS + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_MIPS ) + +#ifdef TARGET_MIPS64 +#define ELF_CLASS ELFCLASS64 +#else +#define ELF_CLASS ELFCLASS32 +#endif +#ifdef TARGET_WORDS_BIGENDIAN +#define ELF_DATA ELFDATA2MSB +#else +#define ELF_DATA ELFDATA2LSB +#endif +#define ELF_ARCH EM_MIPS + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->cp0_status = 2 << CP0St_KSU; + regs->cp0_epc = infop->entry; + regs->regs[29] = infop->start_stack; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#endif /* TARGET_MIPS */ + +#ifdef TARGET_SH4 + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_SH ) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_SH + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + /* Check other registers XXXXX */ + regs->pc = infop->entry; + regs->regs[15] = infop->start_stack; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#endif + +#ifdef TARGET_CRIS + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_CRIS ) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_CRIS + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->erp = infop->entry; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 8192 + +#endif + +#ifdef TARGET_M68K + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_68K ) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_68K + +/* ??? Does this need to do anything? +#define ELF_PLAT_INIT(_r) */ + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->usp = infop->start_stack; + regs->sr = 0; + regs->pc = infop->entry; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 8192 + +#endif + +#ifdef TARGET_ALPHA + +#define ELF_START_MMAP (0x30000000000ULL) + +#define elf_check_arch(x) ( (x) == ELF_ARCH ) + +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_ALPHA + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->pc = infop->entry; + regs->ps = 8; + regs->usp = infop->start_stack; + regs->unique = infop->start_data; /* ? */ + printf("Set unique value to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", + regs->unique, infop->start_data); +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 8192 + +#endif /* TARGET_ALPHA */ + +#ifndef ELF_PLATFORM +#define ELF_PLATFORM (NULL) +#endif + +#ifndef ELF_HWCAP +#define ELF_HWCAP 0 +#endif + +#ifdef TARGET_ABI32 +#undef ELF_CLASS +#define ELF_CLASS ELFCLASS32 +#undef bswaptls +#define bswaptls(ptr) bswap32s(ptr) +#endif + +#include "elf.h" + +struct exec +{ + unsigned int a_info; /* Use macros N_MAGIC, etc for access */ + unsigned int a_text; /* length of text, in bytes */ + unsigned int a_data; /* length of data, in bytes */ + unsigned int a_bss; /* length of uninitialized data area, in bytes */ + unsigned int a_syms; /* length of symbol table data in file, in bytes */ + unsigned int a_entry; /* start address */ + unsigned int a_trsize; /* length of relocation info for text, in bytes */ + unsigned int a_drsize; /* length of relocation info for data, in bytes */ +}; + + +#define N_MAGIC(exec) ((exec).a_info & 0xffff) +#define OMAGIC 0407 +#define NMAGIC 0410 +#define ZMAGIC 0413 +#define QMAGIC 0314 + +/* max code+data+bss space allocated to elf interpreter */ +#define INTERP_MAP_SIZE (32 * 1024 * 1024) + +/* max code+data+bss+brk space allocated to ET_DYN executables */ +#define ET_DYN_MAP_SIZE (128 * 1024 * 1024) + +/* Necessary parameters */ +#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE +#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1)) +#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1)) + +#define INTERPRETER_NONE 0 +#define INTERPRETER_AOUT 1 +#define INTERPRETER_ELF 2 + +#define DLINFO_ITEMS 12 + +static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) +{ + memcpy(to, from, n); +} + +static int load_aout_interp(void * exptr, int interp_fd); + +#ifdef BSWAP_NEEDED +static void bswap_ehdr(struct elfhdr *ehdr) +{ + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswaptls(&ehdr->e_entry); /* Entry point virtual address */ + bswaptls(&ehdr->e_phoff); /* Program header table file offset */ + bswaptls(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void bswap_phdr(struct elf_phdr *phdr) +{ + bswap32s(&phdr->p_type); /* Segment type */ + bswaptls(&phdr->p_offset); /* Segment file offset */ + bswaptls(&phdr->p_vaddr); /* Segment virtual address */ + bswaptls(&phdr->p_paddr); /* Segment physical address */ + bswaptls(&phdr->p_filesz); /* Segment size in file */ + bswaptls(&phdr->p_memsz); /* Segment size in memory */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswaptls(&phdr->p_align); /* Segment alignment */ +} + +static void bswap_shdr(struct elf_shdr *shdr) +{ + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswaptls(&shdr->sh_flags); + bswaptls(&shdr->sh_addr); + bswaptls(&shdr->sh_offset); + bswaptls(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswaptls(&shdr->sh_addralign); + bswaptls(&shdr->sh_entsize); +} + +static void bswap_sym(struct elf_sym *sym) +{ + bswap32s(&sym->st_name); + bswaptls(&sym->st_value); + bswaptls(&sym->st_size); + bswap16s(&sym->st_shndx); +} +#endif + +/* + * 'copy_elf_strings()' copies argument/envelope strings from user + * memory to free pages in kernel mem. These are in a format ready + * to be put directly into the top of new user memory. + * + */ +static abi_ulong copy_elf_strings(int argc,char ** argv, void **page, + abi_ulong p) +{ + char *tmp, *tmp1, *pag = NULL; + int len, offset = 0; + + if (!p) { + return 0; /* bullet-proofing */ + } + while (argc-- > 0) { + tmp = argv[argc]; + if (!tmp) { + fprintf(stderr, "VFS: argc is wrong"); + exit(-1); + } + tmp1 = tmp; + while (*tmp++); + len = tmp - tmp1; + if (p < len) { /* this shouldn't happen - 128kB */ + return 0; + } + while (len) { + --p; --tmp; --len; + if (--offset < 0) { + offset = p % TARGET_PAGE_SIZE; + pag = (char *)page[p/TARGET_PAGE_SIZE]; + if (!pag) { + pag = g_try_malloc0(TARGET_PAGE_SIZE); + page[p/TARGET_PAGE_SIZE] = pag; + if (!pag) + return 0; + } + } + if (len == 0 || offset == 0) { + *(pag + offset) = *tmp; + } + else { + int bytes_to_copy = (len > offset) ? offset : len; + tmp -= bytes_to_copy; + p -= bytes_to_copy; + offset -= bytes_to_copy; + len -= bytes_to_copy; + memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1); + } + } + } + return p; +} + +static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, + struct image_info *info) +{ + abi_ulong stack_base, size, error; + int i; + + /* Create enough stack to hold everything. If we don't use + * it for args, we'll use it for something else... + */ + size = x86_stack_size; + if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) + size = MAX_ARG_PAGES*TARGET_PAGE_SIZE; + error = target_mmap(0, + size + qemu_host_page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, 0); + if (error == -1) { + perror("stk mmap"); + exit(-1); + } + /* we reserve one extra page at the top of the stack as guard */ + target_mprotect(error + size, qemu_host_page_size, PROT_NONE); + + stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE; + p += stack_base; + + for (i = 0 ; i < MAX_ARG_PAGES ; i++) { + if (bprm->page[i]) { + info->rss++; + /* FIXME - check return value of memcpy_to_target() for failure */ + memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE); + g_free(bprm->page[i]); + } + stack_base += TARGET_PAGE_SIZE; + } + return p; +} + +static void set_brk(abi_ulong start, abi_ulong end) +{ + /* page-align the start and end addresses... */ + start = HOST_PAGE_ALIGN(start); + end = HOST_PAGE_ALIGN(end); + if (end <= start) + return; + if(target_mmap(start, end - start, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == -1) { + perror("cannot mmap brk"); + exit(-1); + } +} + + +/* We need to explicitly zero any fractional pages after the data + section (i.e. bss). This would contain the junk from the file that + should not be in memory. */ +static void padzero(abi_ulong elf_bss, abi_ulong last_bss) +{ + abi_ulong nbyte; + + if (elf_bss >= last_bss) + return; + + /* XXX: this is really a hack : if the real host page size is + smaller than the target page size, some pages after the end + of the file may not be mapped. A better fix would be to + patch target_mmap(), but it is more complicated as the file + size must be known */ + if (qemu_real_host_page_size < qemu_host_page_size) { + abi_ulong end_addr, end_addr1; + end_addr1 = (elf_bss + qemu_real_host_page_size - 1) & + ~(qemu_real_host_page_size - 1); + end_addr = HOST_PAGE_ALIGN(elf_bss); + if (end_addr1 < end_addr) { + mmap((void *)g2h(end_addr1), end_addr - end_addr1, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); + } + } + + nbyte = elf_bss & (qemu_host_page_size-1); + if (nbyte) { + nbyte = qemu_host_page_size - nbyte; + do { + /* FIXME - what to do if put_user() fails? */ + put_user_u8(0, elf_bss); + elf_bss++; + } while (--nbyte); + } +} + + +static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, + struct elfhdr * exec, + abi_ulong load_addr, + abi_ulong load_bias, + abi_ulong interp_load_addr, int ibcs, + struct image_info *info) +{ + abi_ulong sp; + int size; + abi_ulong u_platform; + const char *k_platform; + const int n = sizeof(elf_addr_t); + + sp = p; + u_platform = 0; + k_platform = ELF_PLATFORM; + if (k_platform) { + size_t len = strlen(k_platform) + 1; + sp -= (len + n - 1) & ~(n - 1); + u_platform = sp; + /* FIXME - check return value of memcpy_to_target() for failure */ + memcpy_to_target(sp, k_platform, len); + } + /* + * Force 16 byte _final_ alignment here for generality. + */ + sp = sp &~ (abi_ulong)15; + size = (DLINFO_ITEMS + 1) * 2; + if (k_platform) + size += 2; +#ifdef DLINFO_ARCH_ITEMS + size += DLINFO_ARCH_ITEMS * 2; +#endif + size += envc + argc + 2; + size += (!ibcs ? 3 : 1); /* argc itself */ + size *= n; + if (size & 15) + sp -= 16 - (size & 15); + + /* This is correct because Linux defines + * elf_addr_t as Elf32_Off / Elf64_Off + */ +#define NEW_AUX_ENT(id, val) do { \ + sp -= n; put_user_ual(val, sp); \ + sp -= n; put_user_ual(id, sp); \ + } while(0) + + NEW_AUX_ENT (AT_NULL, 0); + + /* There must be exactly DLINFO_ITEMS entries here. */ + NEW_AUX_ENT(AT_PHDR, (abi_ulong)(load_addr + exec->e_phoff)); + NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr))); + NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum)); + NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE)); + NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_load_addr)); + NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0); + NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry); + NEW_AUX_ENT(AT_UID, (abi_ulong) getuid()); + NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid()); + NEW_AUX_ENT(AT_GID, (abi_ulong) getgid()); + NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid()); + NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP); + NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK)); + if (k_platform) + NEW_AUX_ENT(AT_PLATFORM, u_platform); +#ifdef ARCH_DLINFO + /* + * ARCH_DLINFO must come last so platform specific code can enforce + * special alignment requirements on the AUXV if necessary (eg. PPC). + */ + ARCH_DLINFO; +#endif +#undef NEW_AUX_ENT + + sp = loader_build_argptr(envc, argc, sp, p, !ibcs); + return sp; +} + + +static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex, + int interpreter_fd, + abi_ulong *interp_load_addr) +{ + struct elf_phdr *elf_phdata = NULL; + struct elf_phdr *eppnt; + abi_ulong load_addr = 0; + int load_addr_set = 0; + int retval; + abi_ulong last_bss, elf_bss; + abi_ulong error; + int i; + + elf_bss = 0; + last_bss = 0; + error = 0; + +#ifdef BSWAP_NEEDED + bswap_ehdr(interp_elf_ex); +#endif + /* First of all, some simple consistency checks */ + if ((interp_elf_ex->e_type != ET_EXEC && + interp_elf_ex->e_type != ET_DYN) || + !elf_check_arch(interp_elf_ex->e_machine)) { + return ~((abi_ulong)0UL); + } + + + /* Now read in all of the header information */ + + if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE) + return ~(abi_ulong)0UL; + + elf_phdata = (struct elf_phdr *) + malloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); + + if (!elf_phdata) + return ~((abi_ulong)0UL); + + /* + * If the size of this structure has changed, then punt, since + * we will be doing the wrong thing. + */ + if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) { + free(elf_phdata); + return ~((abi_ulong)0UL); + } + + retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET); + if(retval >= 0) { + retval = read(interpreter_fd, + (char *) elf_phdata, + sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); + } + if (retval < 0) { + perror("load_elf_interp"); + exit(-1); + free (elf_phdata); + return retval; + } +#ifdef BSWAP_NEEDED + eppnt = elf_phdata; + for (i=0; ie_phnum; i++, eppnt++) { + bswap_phdr(eppnt); + } +#endif + + if (interp_elf_ex->e_type == ET_DYN) { + /* in order to avoid hardcoding the interpreter load + address in qemu, we allocate a big enough memory zone */ + error = target_mmap(0, INTERP_MAP_SIZE, + PROT_NONE, MAP_PRIVATE | MAP_ANON, + -1, 0); + if (error == -1) { + perror("mmap"); + exit(-1); + } + load_addr = error; + load_addr_set = 1; + } + + eppnt = elf_phdata; + for(i=0; ie_phnum; i++, eppnt++) + if (eppnt->p_type == PT_LOAD) { + int elf_type = MAP_PRIVATE | MAP_DENYWRITE; + int elf_prot = 0; + abi_ulong vaddr = 0; + abi_ulong k; + + if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; + if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; + if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; + if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) { + elf_type |= MAP_FIXED; + vaddr = eppnt->p_vaddr; + } + error = target_mmap(load_addr+TARGET_ELF_PAGESTART(vaddr), + eppnt->p_filesz + TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr), + elf_prot, + elf_type, + interpreter_fd, + eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr)); + + if (error == -1) { + /* Real error */ + close(interpreter_fd); + free(elf_phdata); + return ~((abi_ulong)0UL); + } + + if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) { + load_addr = error; + load_addr_set = 1; + } + + /* + * Find the end of the file mapping for this phdr, and keep + * track of the largest address we see for this. + */ + k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; + if (k > elf_bss) elf_bss = k; + + /* + * Do the same thing for the memory mapping - between + * elf_bss and last_bss is the bss section. + */ + k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; + if (k > last_bss) last_bss = k; + } + + /* Now use mmap to map the library into memory. */ + + close(interpreter_fd); + + /* + * Now fill out the bss section. First pad the last page up + * to the page boundary, and then perform a mmap to make sure + * that there are zeromapped pages up to and including the last + * bss page. + */ + padzero(elf_bss, last_bss); + elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */ + + /* Map the last of the bss segment */ + if (last_bss > elf_bss) { + target_mmap(elf_bss, last_bss-elf_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); + } + free(elf_phdata); + + *interp_load_addr = load_addr; + return ((abi_ulong) interp_elf_ex->e_entry) + load_addr; +} + +static int symfind(const void *s0, const void *s1) +{ + target_ulong addr = *(target_ulong *)s0; + struct elf_sym *sym = (struct elf_sym *)s1; + int result = 0; + if (addr < sym->st_value) { + result = -1; + } else if (addr >= sym->st_value + sym->st_size) { + result = 1; + } + return result; +} + +static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr) +{ +#if ELF_CLASS == ELFCLASS32 + struct elf_sym *syms = s->disas_symtab.elf32; +#else + struct elf_sym *syms = s->disas_symtab.elf64; +#endif + + // binary search + struct elf_sym *sym; + + sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), symfind); + if (sym != NULL) { + return s->disas_strtab + sym->st_name; + } + + return ""; +} + +/* FIXME: This should use elf_ops.h */ +static int symcmp(const void *s0, const void *s1) +{ + struct elf_sym *sym0 = (struct elf_sym *)s0; + struct elf_sym *sym1 = (struct elf_sym *)s1; + return (sym0->st_value < sym1->st_value) + ? -1 + : ((sym0->st_value > sym1->st_value) ? 1 : 0); +} + +/* Best attempt to load symbols from this ELF object. */ +static void load_symbols(struct elfhdr *hdr, int fd) +{ + unsigned int i, nsyms; + struct elf_shdr sechdr, symtab, strtab; + char *strings; + struct syminfo *s; + struct elf_sym *syms, *new_syms; + + lseek(fd, hdr->e_shoff, SEEK_SET); + for (i = 0; i < hdr->e_shnum; i++) { + if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr)) + return; +#ifdef BSWAP_NEEDED + bswap_shdr(&sechdr); +#endif + if (sechdr.sh_type == SHT_SYMTAB) { + symtab = sechdr; + lseek(fd, hdr->e_shoff + + sizeof(sechdr) * sechdr.sh_link, SEEK_SET); + if (read(fd, &strtab, sizeof(strtab)) + != sizeof(strtab)) + return; +#ifdef BSWAP_NEEDED + bswap_shdr(&strtab); +#endif + goto found; + } + } + return; /* Shouldn't happen... */ + + found: + /* Now know where the strtab and symtab are. Snarf them. */ + s = malloc(sizeof(*s)); + syms = malloc(symtab.sh_size); + if (!syms) { + free(s); + return; + } + s->disas_strtab = strings = malloc(strtab.sh_size); + if (!s->disas_strtab) { + free(s); + free(syms); + return; + } + + lseek(fd, symtab.sh_offset, SEEK_SET); + if (read(fd, syms, symtab.sh_size) != symtab.sh_size) { + free(s); + free(syms); + free(strings); + return; + } + + nsyms = symtab.sh_size / sizeof(struct elf_sym); + + i = 0; + while (i < nsyms) { +#ifdef BSWAP_NEEDED + bswap_sym(syms + i); +#endif + // Throw away entries which we do not need. + if (syms[i].st_shndx == SHN_UNDEF || + syms[i].st_shndx >= SHN_LORESERVE || + ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { + nsyms--; + if (i < nsyms) { + syms[i] = syms[nsyms]; + } + continue; + } +#if defined(TARGET_ARM) || defined (TARGET_MIPS) + /* The bottom address bit marks a Thumb or MIPS16 symbol. */ + syms[i].st_value &= ~(target_ulong)1; +#endif + i++; + } + + /* Attempt to free the storage associated with the local symbols + that we threw away. Whether or not this has any effect on the + memory allocation depends on the malloc implementation and how + many symbols we managed to discard. */ + new_syms = realloc(syms, nsyms * sizeof(*syms)); + if (new_syms == NULL) { + free(s); + free(syms); + free(strings); + return; + } + syms = new_syms; + + qsort(syms, nsyms, sizeof(*syms), symcmp); + + lseek(fd, strtab.sh_offset, SEEK_SET); + if (read(fd, strings, strtab.sh_size) != strtab.sh_size) { + free(s); + free(syms); + free(strings); + return; + } + s->disas_num_syms = nsyms; +#if ELF_CLASS == ELFCLASS32 + s->disas_symtab.elf32 = syms; + s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; +#else + s->disas_symtab.elf64 = syms; + s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; +#endif + s->next = syminfos; + syminfos = s; +} + +int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, + struct image_info * info) +{ + struct elfhdr elf_ex; + struct elfhdr interp_elf_ex; + struct exec interp_ex; + int interpreter_fd = -1; /* avoid warning */ + abi_ulong load_addr, load_bias; + int load_addr_set = 0; + unsigned int interpreter_type = INTERPRETER_NONE; + unsigned char ibcs2_interpreter; + int i; + abi_ulong mapped_addr; + struct elf_phdr * elf_ppnt; + struct elf_phdr *elf_phdata; + abi_ulong elf_bss, k, elf_brk; + int retval; + char * elf_interpreter; + abi_ulong elf_entry, interp_load_addr = 0; + int status; + abi_ulong start_code, end_code, start_data, end_data; + abi_ulong reloc_func_desc = 0; + abi_ulong elf_stack; + char passed_fileno[6]; + + ibcs2_interpreter = 0; + status = 0; + load_addr = 0; + load_bias = 0; + elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ +#ifdef BSWAP_NEEDED + bswap_ehdr(&elf_ex); +#endif + + /* First of all, some simple consistency checks */ + if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || + (! elf_check_arch(elf_ex.e_machine))) { + return -ENOEXEC; + } + + bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p); + bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p); + bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p); + if (!bprm->p) { + retval = -E2BIG; + } + + /* Now read in all of the header information */ + elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum); + if (elf_phdata == NULL) { + return -ENOMEM; + } + + retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET); + if(retval > 0) { + retval = read(bprm->fd, (char *) elf_phdata, + elf_ex.e_phentsize * elf_ex.e_phnum); + } + + if (retval < 0) { + perror("load_elf_binary"); + exit(-1); + free (elf_phdata); + return -errno; + } + +#ifdef BSWAP_NEEDED + elf_ppnt = elf_phdata; + for (i=0; ip_type == PT_INTERP) { + if ( elf_interpreter != NULL ) + { + free (elf_phdata); + free(elf_interpreter); + close(bprm->fd); + return -EINVAL; + } + + /* This is the program interpreter used for + * shared libraries - for now assume that this + * is an a.out format binary + */ + + elf_interpreter = (char *)malloc(elf_ppnt->p_filesz); + + if (elf_interpreter == NULL) { + free (elf_phdata); + close(bprm->fd); + return -ENOMEM; + } + + retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET); + if(retval >= 0) { + retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz); + } + if(retval < 0) { + perror("load_elf_binary2"); + exit(-1); + } + + /* If the program interpreter is one of these two, + then assume an iBCS2 image. Otherwise assume + a native linux image. */ + + /* JRP - Need to add X86 lib dir stuff here... */ + + if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 || + strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) { + ibcs2_interpreter = 1; + } + +#if 0 + printf("Using ELF interpreter %s\n", path(elf_interpreter)); +#endif + if (retval >= 0) { + retval = open(path(elf_interpreter), O_RDONLY); + if(retval >= 0) { + interpreter_fd = retval; + } + else { + perror(elf_interpreter); + exit(-1); + /* retval = -errno; */ + } + } + + if (retval >= 0) { + retval = lseek(interpreter_fd, 0, SEEK_SET); + if(retval >= 0) { + retval = read(interpreter_fd,bprm->buf,128); + } + } + if (retval >= 0) { + interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */ + interp_elf_ex = *((struct elfhdr *) bprm->buf); /* elf exec-header */ + } + if (retval < 0) { + perror("load_elf_binary3"); + exit(-1); + free (elf_phdata); + free(elf_interpreter); + close(bprm->fd); + return retval; + } + } + elf_ppnt++; + } + + /* Some simple consistency checks for the interpreter */ + if (elf_interpreter){ + interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; + + /* Now figure out which format our binary is */ + if ((N_MAGIC(interp_ex) != OMAGIC) && (N_MAGIC(interp_ex) != ZMAGIC) && + (N_MAGIC(interp_ex) != QMAGIC)) { + interpreter_type = INTERPRETER_ELF; + } + + if (interp_elf_ex.e_ident[0] != 0x7f || + strncmp((char *)&interp_elf_ex.e_ident[1], "ELF",3) != 0) { + interpreter_type &= ~INTERPRETER_ELF; + } + + if (!interpreter_type) { + free(elf_interpreter); + free(elf_phdata); + close(bprm->fd); + return -ELIBBAD; + } + } + + /* OK, we are done with that, now set up the arg stuff, + and then start this sucker up */ + + { + char * passed_p; + + if (interpreter_type == INTERPRETER_AOUT) { + snprintf(passed_fileno, sizeof(passed_fileno), "%d", bprm->fd); + passed_p = passed_fileno; + + if (elf_interpreter) { + bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p); + bprm->argc++; + } + } + if (!bprm->p) { + if (elf_interpreter) { + free(elf_interpreter); + } + free (elf_phdata); + close(bprm->fd); + return -E2BIG; + } + } + + /* OK, This is the point of no return */ + info->end_data = 0; + info->end_code = 0; + info->start_mmap = (abi_ulong)ELF_START_MMAP; + info->mmap = 0; + elf_entry = (abi_ulong) elf_ex.e_entry; + +#if defined(CONFIG_USE_GUEST_BASE) + /* + * In case where user has not explicitly set the guest_base, we + * probe here that should we set it automatically. + */ + if (!have_guest_base) { + /* + * Go through ELF program header table and find out whether + * any of the segments drop below our current mmap_min_addr and + * in that case set guest_base to corresponding address. + */ + for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; + i++, elf_ppnt++) { + if (elf_ppnt->p_type != PT_LOAD) + continue; + if (HOST_PAGE_ALIGN(elf_ppnt->p_vaddr) < mmap_min_addr) { + guest_base = HOST_PAGE_ALIGN(mmap_min_addr); + break; + } + } + } +#endif /* CONFIG_USE_GUEST_BASE */ + + /* Do this so that we can load the interpreter, if need be. We will + change some of these later */ + info->rss = 0; + bprm->p = setup_arg_pages(bprm->p, bprm, info); + info->start_stack = bprm->p; + + /* Now we do a little grungy work by mmaping the ELF image into + * the correct location in memory. At this point, we assume that + * the image should be loaded at fixed address, not at a variable + * address. + */ + + for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { + int elf_prot = 0; + int elf_flags = 0; + abi_ulong error; + + if (elf_ppnt->p_type != PT_LOAD) + continue; + + if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ; + if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; + if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; + elf_flags = MAP_PRIVATE | MAP_DENYWRITE; + if (elf_ex.e_type == ET_EXEC || load_addr_set) { + elf_flags |= MAP_FIXED; + } else if (elf_ex.e_type == ET_DYN) { + /* Try and get dynamic programs out of the way of the default mmap + base, as well as whatever program they might try to exec. This + is because the brk will follow the loader, and is not movable. */ + /* NOTE: for qemu, we do a big mmap to get enough space + without hardcoding any address */ + error = target_mmap(0, ET_DYN_MAP_SIZE, + PROT_NONE, MAP_PRIVATE | MAP_ANON, + -1, 0); + if (error == -1) { + perror("mmap"); + exit(-1); + } + load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr); + } + + error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr), + (elf_ppnt->p_filesz + + TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)), + elf_prot, + (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE), + bprm->fd, + (elf_ppnt->p_offset - + TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); + if (error == -1) { + perror("mmap"); + exit(-1); + } + +#ifdef LOW_ELF_STACK + if (TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack) + elf_stack = TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr); +#endif + + if (!load_addr_set) { + load_addr_set = 1; + load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset; + if (elf_ex.e_type == ET_DYN) { + load_bias += error - + TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr); + load_addr += load_bias; + reloc_func_desc = load_bias; + } + } + k = elf_ppnt->p_vaddr; + if (k < start_code) + start_code = k; + if (start_data < k) + start_data = k; + k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; + if (k > elf_bss) + elf_bss = k; + if ((elf_ppnt->p_flags & PF_X) && end_code < k) + end_code = k; + if (end_data < k) + end_data = k; + k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; + if (k > elf_brk) elf_brk = k; + } + + elf_entry += load_bias; + elf_bss += load_bias; + elf_brk += load_bias; + start_code += load_bias; + end_code += load_bias; + start_data += load_bias; + end_data += load_bias; + + if (elf_interpreter) { + if (interpreter_type & 1) { + elf_entry = load_aout_interp(&interp_ex, interpreter_fd); + } + else if (interpreter_type & 2) { + elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd, + &interp_load_addr); + } + reloc_func_desc = interp_load_addr; + + close(interpreter_fd); + free(elf_interpreter); + + if (elf_entry == ~((abi_ulong)0UL)) { + printf("Unable to load interpreter\n"); + free(elf_phdata); + exit(-1); + return 0; + } + } + + free(elf_phdata); + + if (qemu_log_enabled()) + load_symbols(&elf_ex, bprm->fd); + + if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd); + info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX); + +#ifdef LOW_ELF_STACK + info->start_stack = bprm->p = elf_stack - 4; +#endif + bprm->p = create_elf_tables(bprm->p, + bprm->argc, + bprm->envc, + &elf_ex, + load_addr, load_bias, + interp_load_addr, + (interpreter_type == INTERPRETER_AOUT ? 0 : 1), + info); + info->load_addr = reloc_func_desc; + info->start_brk = info->brk = elf_brk; + info->end_code = end_code; + info->start_code = start_code; + info->start_data = start_data; + info->end_data = end_data; + info->start_stack = bprm->p; + + /* Calling set_brk effectively mmaps the pages that we need for the bss and break + sections */ + set_brk(elf_bss, elf_brk); + + padzero(elf_bss, elf_brk); + +#if 0 + printf("(start_brk) %x\n" , info->start_brk); + printf("(end_code) %x\n" , info->end_code); + printf("(start_code) %x\n" , info->start_code); + printf("(end_data) %x\n" , info->end_data); + printf("(start_stack) %x\n" , info->start_stack); + printf("(brk) %x\n" , info->brk); +#endif + + if ( info->personality == PER_SVR4 ) + { + /* Why this, you ask??? Well SVr4 maps page 0 as read-only, + and some applications "depend" upon this behavior. + Since we do not have the power to recompile these, we + emulate the SVr4 behavior. Sigh. */ + mapped_addr = target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, -1, 0); + } + + info->entry = elf_entry; + + return 0; +} + +static int load_aout_interp(void * exptr, int interp_fd) +{ + printf("a.out interpreter not yet supported\n"); + return(0); +} + +void do_init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + init_thread(regs, infop); +} diff --git a/bsd-user/errno_defs.h b/bsd-user/errno_defs.h new file mode 100644 index 0000000..1efa502 --- /dev/null +++ b/bsd-user/errno_defs.h @@ -0,0 +1,149 @@ +/* $OpenBSD: errno.h,v 1.20 2007/09/03 14:37:52 millert Exp $ */ +/* $NetBSD: errno.h,v 1.10 1996/01/20 01:33:53 jtc Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)errno.h 8.5 (Berkeley) 1/21/94 + */ + +#define TARGET_EPERM 1 /* Operation not permitted */ +#define TARGET_ENOENT 2 /* No such file or directory */ +#define TARGET_ESRCH 3 /* No such process */ +#define TARGET_EINTR 4 /* Interrupted system call */ +#define TARGET_EIO 5 /* Input/output error */ +#define TARGET_ENXIO 6 /* Device not configured */ +#define TARGET_E2BIG 7 /* Argument list too long */ +#define TARGET_ENOEXEC 8 /* Exec format error */ +#define TARGET_EBADF 9 /* Bad file descriptor */ +#define TARGET_ECHILD 10 /* No child processes */ +#define TARGET_EDEADLK 11 /* Resource deadlock avoided */ + /* 11 was EAGAIN */ +#define TARGET_ENOMEM 12 /* Cannot allocate memory */ +#define TARGET_EACCES 13 /* Permission denied */ +#define TARGET_EFAULT 14 /* Bad address */ +#define TARGET_ENOTBLK 15 /* Block device required */ +#define TARGET_EBUSY 16 /* Device busy */ +#define TARGET_EEXIST 17 /* File exists */ +#define TARGET_EXDEV 18 /* Cross-device link */ +#define TARGET_ENODEV 19 /* Operation not supported by device */ +#define TARGET_ENOTDIR 20 /* Not a directory */ +#define TARGET_EISDIR 21 /* Is a directory */ +#define TARGET_EINVAL 22 /* Invalid argument */ +#define TARGET_ENFILE 23 /* Too many open files in system */ +#define TARGET_EMFILE 24 /* Too many open files */ +#define TARGET_ENOTTY 25 /* Inappropriate ioctl for device */ +#define TARGET_ETXTBSY 26 /* Text file busy */ +#define TARGET_EFBIG 27 /* File too large */ +#define TARGET_ENOSPC 28 /* No space left on device */ +#define TARGET_ESPIPE 29 /* Illegal seek */ +#define TARGET_EROFS 30 /* Read-only file system */ +#define TARGET_EMLINK 31 /* Too many links */ +#define TARGET_EPIPE 32 /* Broken pipe */ + +/* math software */ +#define TARGET_EDOM 33 /* Numerical argument out of domain */ +#define TARGET_ERANGE 34 /* Result too large */ + +/* non-blocking and interrupt i/o */ +#define TARGET_EAGAIN 35 /* Resource temporarily unavailable */ +#define TARGET_EWOULDBLOCK EAGAIN /* Operation would block */ +#define TARGET_EINPROGRESS 36 /* Operation now in progress */ +#define TARGET_EALREADY 37 /* Operation already in progress */ + +/* ipc/network software -- argument errors */ +#define TARGET_ENOTSOCK 38 /* Socket operation on non-socket */ +#define TARGET_EDESTADDRREQ 39 /* Destination address required */ +#define TARGET_EMSGSIZE 40 /* Message too long */ +#define TARGET_EPROTOTYPE 41 /* Protocol wrong type for socket */ +#define TARGET_ENOPROTOOPT 42 /* Protocol not available */ +#define TARGET_EPROTONOSUPPORT 43 /* Protocol not supported */ +#define TARGET_ESOCKTNOSUPPORT 44 /* Socket type not supported */ +#define TARGET_EOPNOTSUPP 45 /* Operation not supported */ +#define TARGET_EPFNOSUPPORT 46 /* Protocol family not supported */ +#define TARGET_EAFNOSUPPORT 47 /* Address family not supported by protocol family */ +#define TARGET_EADDRINUSE 48 /* Address already in use */ +#define TARGET_EADDRNOTAVAIL 49 /* Can't assign requested address */ + +/* ipc/network software -- operational errors */ +#define TARGET_ENETDOWN 50 /* Network is down */ +#define TARGET_ENETUNREACH 51 /* Network is unreachable */ +#define TARGET_ENETRESET 52 /* Network dropped connection on reset */ +#define TARGET_ECONNABORTED 53 /* Software caused connection abort */ +#define TARGET_ECONNRESET 54 /* Connection reset by peer */ +#define TARGET_ENOBUFS 55 /* No buffer space available */ +#define TARGET_EISCONN 56 /* Socket is already connected */ +#define TARGET_ENOTCONN 57 /* Socket is not connected */ +#define TARGET_ESHUTDOWN 58 /* Can't send after socket shutdown */ +#define TARGET_ETOOMANYREFS 59 /* Too many references: can't splice */ +#define TARGET_ETIMEDOUT 60 /* Operation timed out */ +#define TARGET_ECONNREFUSED 61 /* Connection refused */ + +#define TARGET_ELOOP 62 /* Too many levels of symbolic links */ +#define TARGET_ENAMETOOLONG 63 /* File name too long */ + +/* should be rearranged */ +#define TARGET_EHOSTDOWN 64 /* Host is down */ +#define TARGET_EHOSTUNREACH 65 /* No route to host */ +#define TARGET_ENOTEMPTY 66 /* Directory not empty */ + +/* quotas & mush */ +#define TARGET_EPROCLIM 67 /* Too many processes */ +#define TARGET_EUSERS 68 /* Too many users */ +#define TARGET_EDQUOT 69 /* Disk quota exceeded */ + +/* Network File System */ +#define TARGET_ESTALE 70 /* Stale NFS file handle */ +#define TARGET_EREMOTE 71 /* Too many levels of remote in path */ +#define TARGET_EBADRPC 72 /* RPC struct is bad */ +#define TARGET_ERPCMISMATCH 73 /* RPC version wrong */ +#define TARGET_EPROGUNAVAIL 74 /* RPC prog. not avail */ +#define TARGET_EPROGMISMATCH 75 /* Program version wrong */ +#define TARGET_EPROCUNAVAIL 76 /* Bad procedure for program */ + +#define TARGET_ENOLCK 77 /* No locks available */ +#define TARGET_ENOSYS 78 /* Function not implemented */ + +#define TARGET_EFTYPE 79 /* Inappropriate file type or format */ +#define TARGET_EAUTH 80 /* Authentication error */ +#define TARGET_ENEEDAUTH 81 /* Need authenticator */ +#define TARGET_EIPSEC 82 /* IPsec processing failure */ +#define TARGET_ENOATTR 83 /* Attribute not found */ +#define TARGET_EILSEQ 84 /* Illegal byte sequence */ +#define TARGET_ENOMEDIUM 85 /* No medium found */ +#define TARGET_EMEDIUMTYPE 86 /* Wrong Medium Type */ +#define TARGET_EOVERFLOW 87 /* Conversion overflow */ +#define TARGET_ECANCELED 88 /* Operation canceled */ +#define TARGET_EIDRM 89 /* Identifier removed */ +#define TARGET_ENOMSG 90 /* No message of desired type */ +#define TARGET_ELAST 90 /* Must be equal largest errno */ diff --git a/bsd-user/freebsd/strace.list b/bsd-user/freebsd/strace.list new file mode 100644 index 0000000..2800a2d --- /dev/null +++ b/bsd-user/freebsd/strace.list @@ -0,0 +1,231 @@ +/* + * FreeBSD strace list + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +{ TARGET_FREEBSD_NR___acl_aclcheck_fd, "__acl_aclcheck_fd", "%s(%d, %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_aclcheck_file, "__acl_aclcheck_file", "%s(\"%s\", %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_aclcheck_link, "__acl_aclcheck_link", "%s(\"%s\", %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_delete_fd, "__acl_delete_fd", "%s(%d, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_delete_file, "__acl_delete_file", "%s(\"%s\", %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_delete_link, "__acl_delete_link", "%s(\"%s\", %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_get_fd, "__acl_get_fd", "%s(%d, %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_get_file, "__acl_get_file", "%s(\"%s\", %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_get_link, "__acl_get_link", "%s(\"%s\", %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_set_fd, "__acl_set_fd", "%s(%d, %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_set_file, "__acl_set_file", "%s(\"%s\", %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___acl_set_link, "__acl_set_link", "%s(\"%s\", %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___semctl, "__semctl", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR___syscall, "__syscall", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR___sysctl, "__sysctl", NULL, print_sysctl, NULL }, +{ TARGET_FREEBSD_NR__umtx_op, "_umtx_op", "%s(%#x, %d, %d, %#x, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_FREEBSD_NR_acct, "acct", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_adjtime, "adjtime", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_bind, "bind", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_break, "break", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_chflags, "chflags", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_FREEBSD_NR_chown, "chown", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_chroot, "chroot", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_clock_getres, "clock_getres", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_clock_gettime, "clock_gettime", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_close, "close", "%s(%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_dup, "dup", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_dup2, "dup2", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_eaccess, "eaccess", "%s(\"%s\",%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_execve, "execve", NULL, print_execve, NULL }, +{ TARGET_FREEBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattrctl, "extattrctl", "%s(\"%s\", %d, \"%s\", %d, \"%s\"", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_delete_fd, "extattr_delete_fd", "%s(%d, %d, \"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_delete_file, "extattr_delete_file", "%s(\"%s\", %d, \"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_delete_link, "extattr_delete_link", "%s(\"%s\", %d, \"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_get_fd, "extattr_get_fd", "%s(%d, %d, \"%s\", %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_get_file, "extattr_get_file", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_get_file, "extattr_get_link", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_list_fd, "extattr_list_fd", "%s(%d, %d, %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_list_file, "extattr_list_file", "%s(\"%s\", %d, %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_list_link, "extattr_list_link", "%s(\"%s\", %d, %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_set_fd, "extattr_set_fd", "%s(%d, %d, \"%s\", %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_set_file, "extattr_set_file", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_set_link, "extattr_set_link", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_fchdir, "fchdir", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_fchflags, "fchflags", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL }, +{ TARGET_FREEBSD_NR_fchown, "fchown", "%s(%d,%d,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_fcntl, "fcntl", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_fexecve, "fexecve", NULL, print_execve, NULL }, +{ TARGET_FREEBSD_NR_fhopen, "fhopen", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_fhstat, "fhstat", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_fhstatfs, "fhstatfs", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_flock, "flock", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_fork, "fork", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_fstat, "fstat", "%s(%d,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_fstatat, "fstatat", "%s(%d,\"%s\", %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_fstatfs, "fstatfs", "%s(%d,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_fsync, "fsync", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_futimes, "futimes", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getcontext, "getcontext", "%s(%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_getdirentries, "getdirentries", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd6_mmap, "freebsd6_mmap", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getegid, "getegid", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_getfh, "getfh", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getfsstat, "getfsstat", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getgid, "getgid", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_getgroups, "getgroups", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getitimer, "getitimer", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getlogin, "getlogin", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getpeername, "getpeername", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getpgid, "getpgid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getpgrp, "getpgrp", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_getpid, "getpid", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_getppid, "getppid", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_getresgid, "getresgid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getresuid, "getresuid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getrusage, "getrusage", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getsid, "getsid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getsockname, "getsockname", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_getuid, "getuid", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_ioctl, "ioctl", NULL, print_ioctl, NULL }, +{ TARGET_FREEBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_kevent, "kevent", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_kill, "kill", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_kqueue, "kqueue", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_ktrace, "ktrace", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_lchown, "lchown", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_listen, "listen", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_lpathconf, "lpathconf", "%s(\"%s\", %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_lseek, "lseek", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_lstat, "lstat", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_FREEBSD_NR_madvise, "madvise", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_mincore, "mincore", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_minherit, "minherit", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_FREEBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_mlock, "mlock", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_mlockall, "mlockall", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr }, +{ TARGET_FREEBSD_NR_mount, "mount", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_mprotect, "mprotect", "%s(%#x,%#x,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_msgctl, "msgctl", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_msgget, "msgget", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_msgrcv, "msgrcv", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_msgsnd, "msgsnd", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_msync, "msync", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_munlock, "munlock", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_munlockall, "munlockall", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_munmap, "munmap", "%s(%p,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL }, +{ TARGET_FREEBSD_NR_openat, "openat", "%s(%d, \"%s\",%#x,%#o)", NULL, NULL }, +{ TARGET_FREEBSD_NR_pathconf, "pathconf", "%s(\"%s\", %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_pipe, "pipe", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_poll, "poll", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_pread, "pread", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_preadv, "preadv", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_profil, "profil", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_ptrace, "ptrace", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_pwrite, "pwrite", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_pwritev, "pwritev", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_quotactl, "quotactl", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_read, "read", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_readlink, "readlink", "%s(\"%s\",%p,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_readv, "readv", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_reboot, "reboot", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_recvfrom, "recvfrom", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_recvmsg, "recvmsg", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_rename, "rename", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_revoke, "revoke", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_rfork, "rfork", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_rmdir, "rmdir", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_rtprio_thread, "rtprio_thread", "%s(%d, %d, %p)", NULL, NULL }, +{ TARGET_FREEBSD_NR_sbrk, "sbrk", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_select, "select", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_semget, "semget", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_semop, "semop", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sendto, "sendto", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setcontext, "setcontext", "%s(%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_setegid, "setegid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_seteuid, "seteuid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setgid, "setgid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setgroups, "setgroups", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setitimer, "setitimer", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setlogin, "setlogin", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setpgid, "setpgid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setpriority, "setpriority", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setregid, "setregid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setresgid, "setresgid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setresuid, "setresuid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setreuid, "setreuid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setrlimit, "setrlimit", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setsid, "setsid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setsockopt, "setsockopt", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_settimeofday, "settimeofday", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_setuid, "setuid", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_shmat, "shmat", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_shmctl, "shmctl", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_shmdt, "shmdt", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_shmget, "shmget", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_shutdown, "shutdown", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sigaction, "sigaction", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sigaltstack, "sigaltstack", "%s(%p,%p)", NULL, NULL }, +{ TARGET_FREEBSD_NR_sigpending, "sigpending", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sigprocmask, "sigprocmask", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sigreturn, "sigreturn", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sigsuspend, "sigsuspend", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_socket, "socket", "%s(%d,%d,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_socketpair, "socketpair", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sstk, "sstk", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_stat, "stat", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_FREEBSD_NR_statfs, "statfs", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_FREEBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_sync, "sync", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sysarch, "sysarch", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_syscall, "syscall", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_create, "thr_create", "%s(%#x, %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_exit, "thr_exit", "%s(%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_kill, "thr_kill", "%s(%d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_kill2, "thr_kill2", "%s(%d, %d, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_new, "thr_new", "%s(%#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_self, "thr_self", "%s(%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_set_name, "thr_set_name", "%s(%d, \"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_suspend, "thr_suspend", "%s(%d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_thr_wake, "thr_wake", "%s(%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_truncate, "truncate", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL }, +{ TARGET_FREEBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL }, +{ TARGET_FREEBSD_NR_unmount, "unmount", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_utimes, "utimes", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_vfork, "vfork", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_wait4, "wait4", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL }, diff --git a/bsd-user/freebsd/syscall_nr.h b/bsd-user/freebsd/syscall_nr.h new file mode 100644 index 0000000..d849024 --- /dev/null +++ b/bsd-user/freebsd/syscall_nr.h @@ -0,0 +1,450 @@ +/* + * System call numbers. + * + * created from FreeBSD: releng/9.1/sys/kern/syscalls.master 229723 + * 2012-01-06 19:29:16Z jhb + */ + +#define TARGET_FREEBSD_NR_syscall 0 +#define TARGET_FREEBSD_NR_exit 1 +#define TARGET_FREEBSD_NR_fork 2 +#define TARGET_FREEBSD_NR_read 3 +#define TARGET_FREEBSD_NR_write 4 +#define TARGET_FREEBSD_NR_open 5 +#define TARGET_FREEBSD_NR_close 6 +#define TARGET_FREEBSD_NR_wait4 7 + /* 8 is old creat */ +#define TARGET_FREEBSD_NR_link 9 +#define TARGET_FREEBSD_NR_unlink 10 + /* 11 is obsolete execv */ +#define TARGET_FREEBSD_NR_chdir 12 +#define TARGET_FREEBSD_NR_fchdir 13 +#define TARGET_FREEBSD_NR_mknod 14 +#define TARGET_FREEBSD_NR_chmod 15 +#define TARGET_FREEBSD_NR_chown 16 +#define TARGET_FREEBSD_NR_break 17 +#define TARGET_FREEBSD_NR_freebsd4_getfsstat 18 + /* 19 is old lseek */ +#define TARGET_FREEBSD_NR_getpid 20 +#define TARGET_FREEBSD_NR_mount 21 +#define TARGET_FREEBSD_NR_unmount 22 +#define TARGET_FREEBSD_NR_setuid 23 +#define TARGET_FREEBSD_NR_getuid 24 +#define TARGET_FREEBSD_NR_geteuid 25 +#define TARGET_FREEBSD_NR_ptrace 26 +#define TARGET_FREEBSD_NR_recvmsg 27 +#define TARGET_FREEBSD_NR_sendmsg 28 +#define TARGET_FREEBSD_NR_recvfrom 29 +#define TARGET_FREEBSD_NR_accept 30 +#define TARGET_FREEBSD_NR_getpeername 31 +#define TARGET_FREEBSD_NR_getsockname 32 +#define TARGET_FREEBSD_NR_access 33 +#define TARGET_FREEBSD_NR_chflags 34 +#define TARGET_FREEBSD_NR_fchflags 35 +#define TARGET_FREEBSD_NR_sync 36 +#define TARGET_FREEBSD_NR_kill 37 + /* 38 is old stat */ +#define TARGET_FREEBSD_NR_getppid 39 + /* 40 is old lstat */ +#define TARGET_FREEBSD_NR_dup 41 +#define TARGET_FREEBSD_NR_pipe 42 +#define TARGET_FREEBSD_NR_getegid 43 +#define TARGET_FREEBSD_NR_profil 44 +#define TARGET_FREEBSD_NR_ktrace 45 + /* 46 is old sigaction */ +#define TARGET_FREEBSD_NR_getgid 47 + /* 48 is old sigprocmask */ +#define TARGET_FREEBSD_NR_getlogin 49 +#define TARGET_FREEBSD_NR_setlogin 50 +#define TARGET_FREEBSD_NR_acct 51 + /* 52 is old sigpending */ +#define TARGET_FREEBSD_NR_sigaltstack 53 +#define TARGET_FREEBSD_NR_ioctl 54 +#define TARGET_FREEBSD_NR_reboot 55 +#define TARGET_FREEBSD_NR_revoke 56 +#define TARGET_FREEBSD_NR_symlink 57 +#define TARGET_FREEBSD_NR_readlink 58 +#define TARGET_FREEBSD_NR_execve 59 +#define TARGET_FREEBSD_NR_umask 60 +#define TARGET_FREEBSD_NR_chroot 61 + /* 62 is old fstat */ + /* 63 is old getkerninfo */ + /* 64 is old getpagesize */ +#define TARGET_FREEBSD_NR_msync 65 +#define TARGET_FREEBSD_NR_vfork 66 + /* 67 is obsolete vread */ + /* 68 is obsolete vwrite */ +#define TARGET_FREEBSD_NR_sbrk 69 +#define TARGET_FREEBSD_NR_sstk 70 + /* 71 is old mmap */ +#define TARGET_FREEBSD_NR_vadvise 72 +#define TARGET_FREEBSD_NR_munmap 73 +#define TARGET_FREEBSD_NR_mprotect 74 +#define TARGET_FREEBSD_NR_madvise 75 + /* 76 is obsolete vhangup */ + /* 77 is obsolete vlimit */ +#define TARGET_FREEBSD_NR_mincore 78 +#define TARGET_FREEBSD_NR_getgroups 79 +#define TARGET_FREEBSD_NR_setgroups 80 +#define TARGET_FREEBSD_NR_getpgrp 81 +#define TARGET_FREEBSD_NR_setpgid 82 +#define TARGET_FREEBSD_NR_setitimer 83 + /* 84 is old wait */ +#define TARGET_FREEBSD_NR_swapon 85 +#define TARGET_FREEBSD_NR_getitimer 86 + /* 87 is old gethostname */ + /* 88 is old sethostname */ +#define TARGET_FREEBSD_NR_getdtablesize 89 +#define TARGET_FREEBSD_NR_dup2 90 +#define TARGET_FREEBSD_NR_fcntl 92 +#define TARGET_FREEBSD_NR_select 93 +#define TARGET_FREEBSD_NR_fsync 95 +#define TARGET_FREEBSD_NR_setpriority 96 +#define TARGET_FREEBSD_NR_socket 97 +#define TARGET_FREEBSD_NR_connect 98 + /* 99 is old accept */ +#define TARGET_FREEBSD_NR_getpriority 100 + /* 101 is old send */ + /* 102 is old recv */ + /* 103 is old sigreturn */ +#define TARGET_FREEBSD_NR_bind 104 +#define TARGET_FREEBSD_NR_setsockopt 105 +#define TARGET_FREEBSD_NR_listen 106 + /* 107 is obsolete vtimes */ + /* 108 is old sigvec */ + /* 109 is old sigblock */ + /* 110 is old sigsetmask */ + /* 111 is old sigsuspend */ + /* 112 is old sigstack */ + /* 113 is old recvmsg */ + /* 114 is old sendmsg */ + /* 115 is obsolete vtrace */ +#define TARGET_FREEBSD_NR_gettimeofday 116 +#define TARGET_FREEBSD_NR_getrusage 117 +#define TARGET_FREEBSD_NR_getsockopt 118 +#define TARGET_FREEBSD_NR_readv 120 +#define TARGET_FREEBSD_NR_writev 121 +#define TARGET_FREEBSD_NR_settimeofday 122 +#define TARGET_FREEBSD_NR_fchown 123 +#define TARGET_FREEBSD_NR_fchmod 124 + /* 125 is old recvfrom */ +#define TARGET_FREEBSD_NR_setreuid 126 +#define TARGET_FREEBSD_NR_setregid 127 +#define TARGET_FREEBSD_NR_rename 128 + /* 129 is old truncate */ + /* 130 is old ftruncate */ +#define TARGET_FREEBSD_NR_flock 131 +#define TARGET_FREEBSD_NR_mkfifo 132 +#define TARGET_FREEBSD_NR_sendto 133 +#define TARGET_FREEBSD_NR_shutdown 134 +#define TARGET_FREEBSD_NR_socketpair 135 +#define TARGET_FREEBSD_NR_mkdir 136 +#define TARGET_FREEBSD_NR_rmdir 137 +#define TARGET_FREEBSD_NR_utimes 138 + /* 139 is obsolete 4.2 sigreturn */ +#define TARGET_FREEBSD_NR_adjtime 140 + /* 141 is old getpeername */ + /* 142 is old gethostid */ + /* 143 is old sethostid */ + /* 144 is old getrlimit */ + /* 145 is old setrlimit */ + /* 146 is old killpg */ +#define TARGET_FREEBSD_NR_killpg 146 /* COMPAT */ +#define TARGET_FREEBSD_NR_setsid 147 +#define TARGET_FREEBSD_NR_quotactl 148 + /* 149 is old quota */ + /* 150 is old getsockname */ +#define TARGET_FREEBSD_NR_nlm_syscall 154 +#define TARGET_FREEBSD_NR_nfssvc 155 + /* 156 is old getdirentries */ +#define TARGET_FREEBSD_NR_freebsd4_statfs 157 +#define TARGET_FREEBSD_NR_freebsd4_fstatfs 158 +#define TARGET_FREEBSD_NR_lgetfh 160 +#define TARGET_FREEBSD_NR_getfh 161 +#define TARGET_FREEBSD_NR_freebsd4_getdomainname 162 +#define TARGET_FREEBSD_NR_freebsd4_setdomainname 163 +#define TARGET_FREEBSD_NR_freebsd4_uname 164 +#define TARGET_FREEBSD_NR_sysarch 165 +#define TARGET_FREEBSD_NR_rtprio 166 +#define TARGET_FREEBSD_NR_semsys 169 +#define TARGET_FREEBSD_NR_msgsys 170 +#define TARGET_FREEBSD_NR_shmsys 171 +#define TARGET_FREEBSD_NR_freebsd6_pread 173 +#define TARGET_FREEBSD_NR_freebsd6_pwrite 174 +#define TARGET_FREEBSD_NR_setfib 175 +#define TARGET_FREEBSD_NR_ntp_adjtime 176 +#define TARGET_FREEBSD_NR_setgid 181 +#define TARGET_FREEBSD_NR_setegid 182 +#define TARGET_FREEBSD_NR_seteuid 183 +#define TARGET_FREEBSD_NR_stat 188 +#define TARGET_FREEBSD_NR_fstat 189 +#define TARGET_FREEBSD_NR_lstat 190 +#define TARGET_FREEBSD_NR_pathconf 191 +#define TARGET_FREEBSD_NR_fpathconf 192 +#define TARGET_FREEBSD_NR_getrlimit 194 +#define TARGET_FREEBSD_NR_setrlimit 195 +#define TARGET_FREEBSD_NR_getdirentries 196 +#define TARGET_FREEBSD_NR_freebsd6_mmap 197 +#define TARGET_FREEBSD_NR___syscall 198 +#define TARGET_FREEBSD_NR_freebsd6_lseek 199 +#define TARGET_FREEBSD_NR_freebsd6_truncate 200 +#define TARGET_FREEBSD_NR_freebsd6_ftruncate 201 +#define TARGET_FREEBSD_NR___sysctl 202 +#define TARGET_FREEBSD_NR_mlock 203 +#define TARGET_FREEBSD_NR_munlock 204 +#define TARGET_FREEBSD_NR_undelete 205 +#define TARGET_FREEBSD_NR_futimes 206 +#define TARGET_FREEBSD_NR_getpgid 207 +#define TARGET_FREEBSD_NR_poll 209 +#define TARGET_FREEBSD_NR_freebsd7___semctl 220 +#define TARGET_FREEBSD_NR_semget 221 +#define TARGET_FREEBSD_NR_semop 222 +#define TARGET_FREEBSD_NR_freebsd7_msgctl 224 +#define TARGET_FREEBSD_NR_msgget 225 +#define TARGET_FREEBSD_NR_msgsnd 226 +#define TARGET_FREEBSD_NR_msgrcv 227 +#define TARGET_FREEBSD_NR_shmat 228 +#define TARGET_FREEBSD_NR_freebsd7_shmctl 229 +#define TARGET_FREEBSD_NR_shmdt 230 +#define TARGET_FREEBSD_NR_shmget 231 +#define TARGET_FREEBSD_NR_clock_gettime 232 +#define TARGET_FREEBSD_NR_clock_settime 233 +#define TARGET_FREEBSD_NR_clock_getres 234 +#define TARGET_FREEBSD_NR_ktimer_create 235 +#define TARGET_FREEBSD_NR_ktimer_delete 236 +#define TARGET_FREEBSD_NR_ktimer_settime 237 +#define TARGET_FREEBSD_NR_ktimer_gettime 238 +#define TARGET_FREEBSD_NR_ktimer_getoverrun 239 +#define TARGET_FREEBSD_NR_nanosleep 240 +#define TARGET_FREEBSD_NR_ntp_gettime 248 +#define TARGET_FREEBSD_NR_minherit 250 +#define TARGET_FREEBSD_NR_rfork 251 +#define TARGET_FREEBSD_NR_openbsd_poll 252 +#define TARGET_FREEBSD_NR_issetugid 253 +#define TARGET_FREEBSD_NR_lchown 254 +#define TARGET_FREEBSD_NR_aio_read 255 +#define TARGET_FREEBSD_NR_aio_write 256 +#define TARGET_FREEBSD_NR_lio_listio 257 +#define TARGET_FREEBSD_NR_getdents 272 +#define TARGET_FREEBSD_NR_lchmod 274 +#define TARGET_FREEBSD_NR_netbsd_lchown 275 +#define TARGET_FREEBSD_NR_lutimes 276 +#define TARGET_FREEBSD_NR_netbsd_msync 277 +#define TARGET_FREEBSD_NR_nstat 278 +#define TARGET_FREEBSD_NR_nfstat 279 +#define TARGET_FREEBSD_NR_nlstat 280 +#define TARGET_FREEBSD_NR_preadv 289 +#define TARGET_FREEBSD_NR_pwritev 290 +#define TARGET_FREEBSD_NR_freebsd4_fhstatfs 297 +#define TARGET_FREEBSD_NR_fhopen 298 +#define TARGET_FREEBSD_NR_fhstat 299 +#define TARGET_FREEBSD_NR_modnext 300 +#define TARGET_FREEBSD_NR_modstat 301 +#define TARGET_FREEBSD_NR_modfnext 302 +#define TARGET_FREEBSD_NR_modfind 303 +#define TARGET_FREEBSD_NR_kldload 304 +#define TARGET_FREEBSD_NR_kldunload 305 +#define TARGET_FREEBSD_NR_kldfind 306 +#define TARGET_FREEBSD_NR_kldnext 307 +#define TARGET_FREEBSD_NR_kldstat 308 +#define TARGET_FREEBSD_NR_kldfirstmod 309 +#define TARGET_FREEBSD_NR_getsid 310 +#define TARGET_FREEBSD_NR_setresuid 311 +#define TARGET_FREEBSD_NR_setresgid 312 + /* 313 is obsolete signanosleep */ +#define TARGET_FREEBSD_NR_aio_return 314 +#define TARGET_FREEBSD_NR_aio_suspend 315 +#define TARGET_FREEBSD_NR_aio_cancel 316 +#define TARGET_FREEBSD_NR_aio_error 317 +#define TARGET_FREEBSD_NR_oaio_read 318 +#define TARGET_FREEBSD_NR_oaio_write 319 +#define TARGET_FREEBSD_NR_olio_listio 320 +#define TARGET_FREEBSD_NR_yield 321 + /* 322 is obsolete thr_sleep */ + /* 323 is obsolete thr_wakeup */ +#define TARGET_FREEBSD_NR_mlockall 324 +#define TARGET_FREEBSD_NR_munlockall 325 +#define TARGET_FREEBSD_NR___getcwd 326 +#define TARGET_FREEBSD_NR_sched_setparam 327 +#define TARGET_FREEBSD_NR_sched_getparam 328 +#define TARGET_FREEBSD_NR_sched_setscheduler 329 +#define TARGET_FREEBSD_NR_sched_getscheduler 330 +#define TARGET_FREEBSD_NR_sched_yield 331 +#define TARGET_FREEBSD_NR_sched_get_priority_max 332 +#define TARGET_FREEBSD_NR_sched_get_priority_min 333 +#define TARGET_FREEBSD_NR_sched_rr_get_interval 334 +#define TARGET_FREEBSD_NR_utrace 335 +#define TARGET_FREEBSD_NR_freebsd4_sendfile 336 +#define TARGET_FREEBSD_NR_kldsym 337 +#define TARGET_FREEBSD_NR_jail 338 +#define TARGET_FREEBSD_NR_nnpfs_syscall 339 +#define TARGET_FREEBSD_NR_sigprocmask 340 +#define TARGET_FREEBSD_NR_sigsuspend 341 +#define TARGET_FREEBSD_NR_freebsd4_sigaction 342 +#define TARGET_FREEBSD_NR_sigpending 343 +#define TARGET_FREEBSD_NR_freebsd4_sigreturn 344 +#define TARGET_FREEBSD_NR_sigtimedwait 345 +#define TARGET_FREEBSD_NR_sigwaitinfo 346 +#define TARGET_FREEBSD_NR___acl_get_file 347 +#define TARGET_FREEBSD_NR___acl_set_file 348 +#define TARGET_FREEBSD_NR___acl_get_fd 349 +#define TARGET_FREEBSD_NR___acl_set_fd 350 +#define TARGET_FREEBSD_NR___acl_delete_file 351 +#define TARGET_FREEBSD_NR___acl_delete_fd 352 +#define TARGET_FREEBSD_NR___acl_aclcheck_file 353 +#define TARGET_FREEBSD_NR___acl_aclcheck_fd 354 +#define TARGET_FREEBSD_NR_extattrctl 355 +#define TARGET_FREEBSD_NR_extattr_set_file 356 +#define TARGET_FREEBSD_NR_extattr_get_file 357 +#define TARGET_FREEBSD_NR_extattr_delete_file 358 +#define TARGET_FREEBSD_NR_aio_waitcomplete 359 +#define TARGET_FREEBSD_NR_getresuid 360 +#define TARGET_FREEBSD_NR_getresgid 361 +#define TARGET_FREEBSD_NR_kqueue 362 +#define TARGET_FREEBSD_NR_kevent 363 +#define TARGET_FREEBSD_NR_extattr_set_fd 371 +#define TARGET_FREEBSD_NR_extattr_get_fd 372 +#define TARGET_FREEBSD_NR_extattr_delete_fd 373 +#define TARGET_FREEBSD_NR___setugid 374 +#define TARGET_FREEBSD_NR_eaccess 376 +#define TARGET_FREEBSD_NR_afs3_syscall 377 +#define TARGET_FREEBSD_NR_nmount 378 +#define TARGET_FREEBSD_NR___mac_get_proc 384 +#define TARGET_FREEBSD_NR___mac_set_proc 385 +#define TARGET_FREEBSD_NR___mac_get_fd 386 +#define TARGET_FREEBSD_NR___mac_get_file 387 +#define TARGET_FREEBSD_NR___mac_set_fd 388 +#define TARGET_FREEBSD_NR___mac_set_file 389 +#define TARGET_FREEBSD_NR_kenv 390 +#define TARGET_FREEBSD_NR_lchflags 391 +#define TARGET_FREEBSD_NR_uuidgen 392 +#define TARGET_FREEBSD_NR_sendfile 393 +#define TARGET_FREEBSD_NR_mac_syscall 394 +#define TARGET_FREEBSD_NR_getfsstat 395 +#define TARGET_FREEBSD_NR_statfs 396 +#define TARGET_FREEBSD_NR_fstatfs 397 +#define TARGET_FREEBSD_NR_fhstatfs 398 +#define TARGET_FREEBSD_NR_ksem_close 400 +#define TARGET_FREEBSD_NR_ksem_post 401 +#define TARGET_FREEBSD_NR_ksem_wait 402 +#define TARGET_FREEBSD_NR_ksem_trywait 403 +#define TARGET_FREEBSD_NR_ksem_init 404 +#define TARGET_FREEBSD_NR_ksem_open 405 +#define TARGET_FREEBSD_NR_ksem_unlink 406 +#define TARGET_FREEBSD_NR_ksem_getvalue 407 +#define TARGET_FREEBSD_NR_ksem_destroy 408 +#define TARGET_FREEBSD_NR___mac_get_pid 409 +#define TARGET_FREEBSD_NR___mac_get_link 410 +#define TARGET_FREEBSD_NR___mac_set_link 411 +#define TARGET_FREEBSD_NR_extattr_set_link 412 +#define TARGET_FREEBSD_NR_extattr_get_link 413 +#define TARGET_FREEBSD_NR_extattr_delete_link 414 +#define TARGET_FREEBSD_NR___mac_execve 415 +#define TARGET_FREEBSD_NR_sigaction 416 +#define TARGET_FREEBSD_NR_sigreturn 417 +#define TARGET_FREEBSD_NR_getcontext 421 +#define TARGET_FREEBSD_NR_setcontext 422 +#define TARGET_FREEBSD_NR_swapcontext 423 +#define TARGET_FREEBSD_NR_swapoff 424 +#define TARGET_FREEBSD_NR___acl_get_link 425 +#define TARGET_FREEBSD_NR___acl_set_link 426 +#define TARGET_FREEBSD_NR___acl_delete_link 427 +#define TARGET_FREEBSD_NR___acl_aclcheck_link 428 +#define TARGET_FREEBSD_NR_sigwait 429 +#define TARGET_FREEBSD_NR_thr_create 430 +#define TARGET_FREEBSD_NR_thr_exit 431 +#define TARGET_FREEBSD_NR_thr_self 432 +#define TARGET_FREEBSD_NR_thr_kill 433 +#define TARGET_FREEBSD_NR__umtx_lock 434 +#define TARGET_FREEBSD_NR__umtx_unlock 435 +#define TARGET_FREEBSD_NR_jail_attach 436 +#define TARGET_FREEBSD_NR_extattr_list_fd 437 +#define TARGET_FREEBSD_NR_extattr_list_file 438 +#define TARGET_FREEBSD_NR_extattr_list_link 439 +#define TARGET_FREEBSD_NR_ksem_timedwait 441 +#define TARGET_FREEBSD_NR_thr_suspend 442 +#define TARGET_FREEBSD_NR_thr_wake 443 +#define TARGET_FREEBSD_NR_kldunloadf 444 +#define TARGET_FREEBSD_NR_audit 445 +#define TARGET_FREEBSD_NR_auditon 446 +#define TARGET_FREEBSD_NR_getauid 447 +#define TARGET_FREEBSD_NR_setauid 448 +#define TARGET_FREEBSD_NR_getaudit 449 +#define TARGET_FREEBSD_NR_setaudit 450 +#define TARGET_FREEBSD_NR_getaudit_addr 451 +#define TARGET_FREEBSD_NR_setaudit_addr 452 +#define TARGET_FREEBSD_NR_auditctl 453 +#define TARGET_FREEBSD_NR__umtx_op 454 +#define TARGET_FREEBSD_NR_thr_new 455 +#define TARGET_FREEBSD_NR_sigqueue 456 +#define TARGET_FREEBSD_NR_kmq_open 457 +#define TARGET_FREEBSD_NR_kmq_setattr 458 +#define TARGET_FREEBSD_NR_kmq_timedreceive 459 +#define TARGET_FREEBSD_NR_kmq_timedsend 460 +#define TARGET_FREEBSD_NR_kmq_notify 461 +#define TARGET_FREEBSD_NR_kmq_unlink 462 +#define TARGET_FREEBSD_NR_abort2 463 +#define TARGET_FREEBSD_NR_thr_set_name 464 +#define TARGET_FREEBSD_NR_aio_fsync 465 +#define TARGET_FREEBSD_NR_rtprio_thread 466 +#define TARGET_FREEBSD_NR_sctp_peeloff 471 +#define TARGET_FREEBSD_NR_sctp_generic_sendmsg 472 +#define TARGET_FREEBSD_NR_sctp_generic_sendmsg_iov 473 +#define TARGET_FREEBSD_NR_sctp_generic_recvmsg 474 +#define TARGET_FREEBSD_NR_pread 475 +#define TARGET_FREEBSD_NR_pwrite 476 +#define TARGET_FREEBSD_NR_mmap 477 +#define TARGET_FREEBSD_NR_lseek 478 +#define TARGET_FREEBSD_NR_truncate 479 +#define TARGET_FREEBSD_NR_ftruncate 480 +#define TARGET_FREEBSD_NR_thr_kill2 481 +#define TARGET_FREEBSD_NR_shm_open 482 +#define TARGET_FREEBSD_NR_shm_unlink 483 +#define TARGET_FREEBSD_NR_cpuset 484 +#define TARGET_FREEBSD_NR_cpuset_setid 485 +#define TARGET_FREEBSD_NR_cpuset_getid 486 +#define TARGET_FREEBSD_NR_cpuset_getaffinity 487 +#define TARGET_FREEBSD_NR_cpuset_setaffinity 488 +#define TARGET_FREEBSD_NR_faccessat 489 +#define TARGET_FREEBSD_NR_fchmodat 490 +#define TARGET_FREEBSD_NR_fchownat 491 +#define TARGET_FREEBSD_NR_fexecve 492 +#define TARGET_FREEBSD_NR_fstatat 493 +#define TARGET_FREEBSD_NR_futimesat 494 +#define TARGET_FREEBSD_NR_linkat 495 +#define TARGET_FREEBSD_NR_mkdirat 496 +#define TARGET_FREEBSD_NR_mkfifoat 497 +#define TARGET_FREEBSD_NR_mknodat 498 +#define TARGET_FREEBSD_NR_openat 499 +#define TARGET_FREEBSD_NR_readlinkat 500 +#define TARGET_FREEBSD_NR_renameat 501 +#define TARGET_FREEBSD_NR_symlinkat 502 +#define TARGET_FREEBSD_NR_unlinkat 503 +#define TARGET_FREEBSD_NR_posix_openpt 504 +#define TARGET_FREEBSD_NR_gssd_syscall 505 +#define TARGET_FREEBSD_NR_jail_get 506 +#define TARGET_FREEBSD_NR_jail_set 507 +#define TARGET_FREEBSD_NR_jail_remove 508 +#define TARGET_FREEBSD_NR_closefrom 509 +#define TARGET_FREEBSD_NR___semctl 510 +#define TARGET_FREEBSD_NR_msgctl 511 +#define TARGET_FREEBSD_NR_shmctl 512 +#define TARGET_FREEBSD_NR_lpathconf 513 +#define TARGET_FREEBSD_NR_cap_new 514 +#define TARGET_FREEBSD_NR_cap_getrights 515 +#define TARGET_FREEBSD_NR_cap_enter 516 +#define TARGET_FREEBSD_NR_cap_getmode 517 +#define TARGET_FREEBSD_NR_pdfork 518 +#define TARGET_FREEBSD_NR_pdkill 519 +#define TARGET_FREEBSD_NR_pdgetpid 520 +#define TARGET_FREEBSD_NR_pselect 522 +#define TARGET_FREEBSD_NR_getloginclass 523 +#define TARGET_FREEBSD_NR_setloginclass 524 +#define TARGET_FREEBSD_NR_rctl_get_racct 525 +#define TARGET_FREEBSD_NR_rctl_get_rules 526 +#define TARGET_FREEBSD_NR_rctl_get_limits 527 +#define TARGET_FREEBSD_NR_rctl_add_rule 528 +#define TARGET_FREEBSD_NR_rctl_remove_rule 529 +#define TARGET_FREEBSD_NR_posix_fallocate 530 +#define TARGET_FREEBSD_NR_posix_fadvise 531 +#define TARGET_FREEBSD_NR_MAXSYSCALL 532 diff --git a/bsd-user/i386/syscall.h b/bsd-user/i386/syscall.h new file mode 100644 index 0000000..9b34c61 --- /dev/null +++ b/bsd-user/i386/syscall.h @@ -0,0 +1,161 @@ +/* default linux values for the selectors */ +#define __USER_CS (0x23) +#define __USER_DS (0x2B) + +struct target_pt_regs { + long ebx; + long ecx; + long edx; + long esi; + long edi; + long ebp; + long eax; + int xds; + int xes; + long orig_eax; + long eip; + int xcs; + long eflags; + long esp; + int xss; +}; + +/* ioctls */ + +#define TARGET_LDT_ENTRIES 8192 +#define TARGET_LDT_ENTRY_SIZE 8 + +#define TARGET_GDT_ENTRIES 9 +#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 +#define TARGET_GDT_ENTRY_TLS_MIN 6 +#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1) + +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; + +/* vm86 defines */ + +#define TARGET_BIOSSEG 0x0f000 + +#define TARGET_CPU_086 0 +#define TARGET_CPU_186 1 +#define TARGET_CPU_286 2 +#define TARGET_CPU_386 3 +#define TARGET_CPU_486 4 +#define TARGET_CPU_586 5 + +#define TARGET_VM86_SIGNAL 0 /* return due to signal */ +#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */ +#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */ +#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */ + +/* + * Additional return values when invoking new vm86() + */ +#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */ +#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */ + +/* + * function codes when invoking new vm86() + */ +#define TARGET_VM86_PLUS_INSTALL_CHECK 0 +#define TARGET_VM86_ENTER 1 +#define TARGET_VM86_ENTER_NO_BYPASS 2 +#define TARGET_VM86_REQUEST_IRQ 3 +#define TARGET_VM86_FREE_IRQ 4 +#define TARGET_VM86_GET_IRQ_BITS 5 +#define TARGET_VM86_GET_AND_RESET_IRQ 6 + +/* + * This is the stack-layout seen by the user space program when we have + * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout + * is 'kernel_vm86_regs' (see below). + */ + +struct target_vm86_regs { +/* + * normal regs, with special meaning for the segment descriptors.. + */ + abi_long ebx; + abi_long ecx; + abi_long edx; + abi_long esi; + abi_long edi; + abi_long ebp; + abi_long eax; + abi_long __null_ds; + abi_long __null_es; + abi_long __null_fs; + abi_long __null_gs; + abi_long orig_eax; + abi_long eip; + unsigned short cs, __csh; + abi_long eflags; + abi_long esp; + unsigned short ss, __ssh; +/* + * these are specific to v86 mode: + */ + unsigned short es, __esh; + unsigned short ds, __dsh; + unsigned short fs, __fsh; + unsigned short gs, __gsh; +}; + +struct target_revectored_struct { + abi_ulong __map[8]; /* 256 bits */ +}; + +struct target_vm86_struct { + struct target_vm86_regs regs; + abi_ulong flags; + abi_ulong screen_bitmap; + abi_ulong cpu_type; + struct target_revectored_struct int_revectored; + struct target_revectored_struct int21_revectored; +}; + +/* + * flags masks + */ +#define TARGET_VM86_SCREEN_BITMAP 0x0001 + +struct target_vm86plus_info_struct { + abi_ulong flags; +#define TARGET_force_return_for_pic (1 << 0) +#define TARGET_vm86dbg_active (1 << 1) /* for debugger */ +#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */ +#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */ + unsigned char vm86dbg_intxxtab[32]; /* for debugger */ +}; + +struct target_vm86plus_struct { + struct target_vm86_regs regs; + abi_ulong flags; + abi_ulong screen_bitmap; + abi_ulong cpu_type; + struct target_revectored_struct int_revectored; + struct target_revectored_struct int21_revectored; + struct target_vm86plus_info_struct vm86plus; +}; + +/* FreeBSD sysarch(2) */ +#define TARGET_FREEBSD_I386_GET_LDT 0 +#define TARGET_FREEBSD_I386_SET_LDT 1 + /* I386_IOPL */ +#define TARGET_FREEBSD_I386_GET_IOPERM 3 +#define TARGET_FREEBSD_I386_SET_IOPERM 4 + /* xxxxx */ +#define TARGET_FREEBSD_I386_VM86 6 +#define TARGET_FREEBSD_I386_GET_FSBASE 7 +#define TARGET_FREEBSD_I386_SET_FSBASE 8 +#define TARGET_FREEBSD_I386_GET_GSBASE 9 +#define TARGET_FREEBSD_I386_SET_GSBASE 10 + + +#define UNAME_MACHINE "i386" + diff --git a/bsd-user/i386/target_signal.h b/bsd-user/i386/target_signal.h new file mode 100644 index 0000000..2ef36d1 --- /dev/null +++ b/bsd-user/i386/target_signal.h @@ -0,0 +1,20 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_long ss_flags; + abi_ulong ss_size; +} target_stack_t; + + +static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) +{ + return state->regs[R_ESP]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/bsd-user/main.c b/bsd-user/main.c new file mode 100644 index 0000000..5bfaf5c --- /dev/null +++ b/bsd-user/main.c @@ -0,0 +1,1141 @@ +/* + * qemu user main + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qemu.h" +#include "qemu-common.h" +/* For tb_lock */ +#include "cpu.h" +#include "tcg.h" +#include "qemu/timer.h" +#include "qemu/envlist.h" + +int singlestep; +#if defined(CONFIG_USE_GUEST_BASE) +unsigned long mmap_min_addr; +unsigned long guest_base; +int have_guest_base; +unsigned long reserved_va; +#endif + +static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; +const char *qemu_uname_release; +extern char **environ; +enum BSDType bsd_type; + +/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so + we allocate a bigger stack. Need a better solution, for example + by remapping the process stack directly at the right place */ +unsigned long x86_stack_size = 512 * 1024; + +void gemu_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +#if defined(TARGET_I386) +int cpu_get_pic_interrupt(CPUX86State *env) +{ + return -1; +} +#endif + +/* These are no-ops because we are not threadsafe. */ +static inline void cpu_exec_start(CPUArchState *env) +{ +} + +static inline void cpu_exec_end(CPUArchState *env) +{ +} + +static inline void start_exclusive(void) +{ +} + +static inline void end_exclusive(void) +{ +} + +void fork_start(void) +{ +} + +void fork_end(int child) +{ + if (child) { + gdbserver_fork((CPUArchState *)thread_cpu->env_ptr); + } +} + +void cpu_list_lock(void) +{ +} + +void cpu_list_unlock(void) +{ +} + +#ifdef TARGET_I386 +/***********************************************************/ +/* CPUX86 core interface */ + +void cpu_smm_update(CPUX86State *env) +{ +} + +uint64_t cpu_get_tsc(CPUX86State *env) +{ + return cpu_get_real_ticks(); +} + +static void write_dt(void *ptr, unsigned long addr, unsigned long limit, + int flags) +{ + unsigned int e1, e2; + uint32_t *p; + e1 = (addr << 16) | (limit & 0xffff); + e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); + e2 |= flags; + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); +} + +static uint64_t *idt_table; +#ifdef TARGET_X86_64 +static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, + uint64_t addr, unsigned int sel) +{ + uint32_t *p, e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); + p[2] = tswap32(addr >> 32); + p[3] = 0; +} +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate64(idt_table + n * 2, 0, dpl, 0, 0); +} +#else +static void set_gate(void *ptr, unsigned int type, unsigned int dpl, + uint32_t addr, unsigned int sel) +{ + uint32_t *p, e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); +} + +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate(idt_table + n, 0, dpl, 0, 0); +} +#endif + +void cpu_loop(CPUX86State *env) +{ + int trapnr; + abi_ulong pc; + //target_siginfo_t info; + + for(;;) { + trapnr = cpu_x86_exec(env); + switch(trapnr) { + case 0x80: + /* syscall from int $0x80 */ + if (bsd_type == target_freebsd) { + abi_ulong params = (abi_ulong) env->regs[R_ESP] + + sizeof(int32_t); + int32_t syscall_nr = env->regs[R_EAX]; + int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; + + if (syscall_nr == TARGET_FREEBSD_NR_syscall) { + get_user_s32(syscall_nr, params); + params += sizeof(int32_t); + } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) { + get_user_s32(syscall_nr, params); + params += sizeof(int64_t); + } + get_user_s32(arg1, params); + params += sizeof(int32_t); + get_user_s32(arg2, params); + params += sizeof(int32_t); + get_user_s32(arg3, params); + params += sizeof(int32_t); + get_user_s32(arg4, params); + params += sizeof(int32_t); + get_user_s32(arg5, params); + params += sizeof(int32_t); + get_user_s32(arg6, params); + params += sizeof(int32_t); + get_user_s32(arg7, params); + params += sizeof(int32_t); + get_user_s32(arg8, params); + env->regs[R_EAX] = do_freebsd_syscall(env, + syscall_nr, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8); + } else { //if (bsd_type == target_openbsd) + env->regs[R_EAX] = do_openbsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EBX], + env->regs[R_ECX], + env->regs[R_EDX], + env->regs[R_ESI], + env->regs[R_EDI], + env->regs[R_EBP]); + } + if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) { + env->regs[R_EAX] = -env->regs[R_EAX]; + env->eflags |= CC_C; + } else { + env->eflags &= ~CC_C; + } + break; +#ifndef TARGET_ABI32 + case EXCP_SYSCALL: + /* syscall from syscall instruction */ + if (bsd_type == target_freebsd) + env->regs[R_EAX] = do_freebsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[R_ECX], + env->regs[8], + env->regs[9], 0, 0); + else { //if (bsd_type == target_openbsd) + env->regs[R_EAX] = do_openbsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[10], + env->regs[8], + env->regs[9]); + } + env->eip = env->exception_next_eip; + if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) { + env->regs[R_EAX] = -env->regs[R_EAX]; + env->eflags |= CC_C; + } else { + env->eflags &= ~CC_C; + } + break; +#endif +#if 0 + case EXCP0B_NOSEG: + case EXCP0C_STACK: + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, &info); + break; + case EXCP0D_GPF: + /* XXX: potential problem if ABI32 */ +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_fault(env); + } else +#endif + { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP0E_PAGE: + info.si_signo = SIGSEGV; + info.si_errno = 0; + if (!(env->error_code & 1)) + info.si_code = TARGET_SEGV_MAPERR; + else + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->cr[2]; + queue_signal(env, info.si_signo, &info); + break; + case EXCP00_DIVZ: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + /* division by zero */ + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->eip; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP01_DB: + case EXCP03_INT3: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + info.si_signo = SIGTRAP; + info.si_errno = 0; + if (trapnr == EXCP01_DB) { + info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->eip; + } else { + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + } + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP04_INTO: + case EXCP05_BOUND: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP06_ILLOP: + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->eip; + queue_signal(env, info.si_signo, &info); + break; +#endif + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; +#if 0 + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig (env, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, &info); + } + } + break; +#endif + default: + pc = env->segs[R_CS].base + env->eip; + fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", + (long)pc, trapnr); + abort(); + } + process_pending_signals(env); + } +} +#endif + +#ifdef TARGET_SPARC +#define SPARC64_STACK_BIAS 2047 + +//#define DEBUG_WIN +/* WARNING: dealing with register windows _is_ complicated. More info + can be found at http://www.sics.se/~psm/sparcstack.html */ +static inline int get_reg_index(CPUSPARCState *env, int cwp, int index) +{ + index = (index + cwp * 16) % (16 * env->nwindows); + /* wrap handling : if cwp is on the last window, then we use the + registers 'after' the end */ + if (index < 8 && env->cwp == env->nwindows - 1) + index += 16 * env->nwindows; + return index; +} + +/* save the register window 'cwp1' */ +static inline void save_window_offset(CPUSPARCState *env, int cwp1) +{ + unsigned int i; + abi_ulong sp_ptr; + + sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; +#ifdef TARGET_SPARC64 + if (sp_ptr & 3) + sp_ptr += SPARC64_STACK_BIAS; +#endif +#if defined(DEBUG_WIN) + printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n", + sp_ptr, cwp1); +#endif + for(i = 0; i < 16; i++) { + /* FIXME - what to do if put_user() fails? */ + put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); + sp_ptr += sizeof(abi_ulong); + } +} + +static void save_window(CPUSPARCState *env) +{ +#ifndef TARGET_SPARC64 + unsigned int new_wim; + new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) & + ((1LL << env->nwindows) - 1); + save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); + env->wim = new_wim; +#else + save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); + env->cansave++; + env->canrestore--; +#endif +} + +static void restore_window(CPUSPARCState *env) +{ +#ifndef TARGET_SPARC64 + unsigned int new_wim; +#endif + unsigned int i, cwp1; + abi_ulong sp_ptr; + +#ifndef TARGET_SPARC64 + new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) & + ((1LL << env->nwindows) - 1); +#endif + + /* restore the invalid window */ + cwp1 = cpu_cwp_inc(env, env->cwp + 1); + sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; +#ifdef TARGET_SPARC64 + if (sp_ptr & 3) + sp_ptr += SPARC64_STACK_BIAS; +#endif +#if defined(DEBUG_WIN) + printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n", + sp_ptr, cwp1); +#endif + for(i = 0; i < 16; i++) { + /* FIXME - what to do if get_user() fails? */ + get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); + sp_ptr += sizeof(abi_ulong); + } +#ifdef TARGET_SPARC64 + env->canrestore++; + if (env->cleanwin < env->nwindows - 1) + env->cleanwin++; + env->cansave--; +#else + env->wim = new_wim; +#endif +} + +static void flush_windows(CPUSPARCState *env) +{ + int offset, cwp1; + + offset = 1; + for(;;) { + /* if restore would invoke restore_window(), then we can stop */ + cwp1 = cpu_cwp_inc(env, env->cwp + offset); +#ifndef TARGET_SPARC64 + if (env->wim & (1 << cwp1)) + break; +#else + if (env->canrestore == 0) + break; + env->cansave++; + env->canrestore--; +#endif + save_window_offset(env, cwp1); + offset++; + } + cwp1 = cpu_cwp_inc(env, env->cwp + 1); +#ifndef TARGET_SPARC64 + /* set wim so that restore will reload the registers */ + env->wim = 1 << cwp1; +#endif +#if defined(DEBUG_WIN) + printf("flush_windows: nb=%d\n", offset - 1); +#endif +} + +void cpu_loop(CPUSPARCState *env) +{ + CPUState *cs = CPU(sparc_env_get_cpu(env)); + int trapnr, ret, syscall_nr; + //target_siginfo_t info; + + while (1) { + trapnr = cpu_sparc_exec (env); + + switch (trapnr) { +#ifndef TARGET_SPARC64 + case 0x80: +#else + /* FreeBSD uses 0x141 for syscalls too */ + case 0x141: + if (bsd_type != target_freebsd) + goto badtrap; + case 0x100: +#endif + syscall_nr = env->gregs[1]; + if (bsd_type == target_freebsd) + ret = do_freebsd_syscall(env, syscall_nr, + env->regwptr[0], env->regwptr[1], + env->regwptr[2], env->regwptr[3], + env->regwptr[4], env->regwptr[5], 0, 0); + else if (bsd_type == target_netbsd) + ret = do_netbsd_syscall(env, syscall_nr, + env->regwptr[0], env->regwptr[1], + env->regwptr[2], env->regwptr[3], + env->regwptr[4], env->regwptr[5]); + else { //if (bsd_type == target_openbsd) +#if defined(TARGET_SPARC64) + syscall_nr &= ~(TARGET_OPENBSD_SYSCALL_G7RFLAG | + TARGET_OPENBSD_SYSCALL_G2RFLAG); +#endif + ret = do_openbsd_syscall(env, syscall_nr, + env->regwptr[0], env->regwptr[1], + env->regwptr[2], env->regwptr[3], + env->regwptr[4], env->regwptr[5]); + } + if ((unsigned int)ret >= (unsigned int)(-515)) { + ret = -ret; +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + env->xcc |= PSR_CARRY; +#else + env->psr |= PSR_CARRY; +#endif + } else { +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + env->xcc &= ~PSR_CARRY; +#else + env->psr &= ~PSR_CARRY; +#endif + } + env->regwptr[0] = ret; + /* next instruction */ +#if defined(TARGET_SPARC64) + if (bsd_type == target_openbsd && + env->gregs[1] & TARGET_OPENBSD_SYSCALL_G2RFLAG) { + env->pc = env->gregs[2]; + env->npc = env->pc + 4; + } else if (bsd_type == target_openbsd && + env->gregs[1] & TARGET_OPENBSD_SYSCALL_G7RFLAG) { + env->pc = env->gregs[7]; + env->npc = env->pc + 4; + } else { + env->pc = env->npc; + env->npc = env->npc + 4; + } +#else + env->pc = env->npc; + env->npc = env->npc + 4; +#endif + break; + case 0x83: /* flush windows */ +#ifdef TARGET_ABI32 + case 0x103: +#endif + flush_windows(env); + /* next instruction */ + env->pc = env->npc; + env->npc = env->npc + 4; + break; +#ifndef TARGET_SPARC64 + case TT_WIN_OVF: /* window overflow */ + save_window(env); + break; + case TT_WIN_UNF: /* window underflow */ + restore_window(env); + break; + case TT_TFAULT: + case TT_DFAULT: +#if 0 + { + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->mmuregs[4]; + queue_signal(env, info.si_signo, &info); + } +#endif + break; +#else + case TT_SPILL: /* window overflow */ + save_window(env); + break; + case TT_FILL: /* window underflow */ + restore_window(env); + break; + case TT_TFAULT: + case TT_DFAULT: +#if 0 + { + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + if (trapnr == TT_DFAULT) + info._sifields._sigfault._addr = env->dmmuregs[4]; + else + info._sifields._sigfault._addr = env->tsptr->tpc; + //queue_signal(env, info.si_signo, &info); + } +#endif + break; +#endif + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); +#if 0 + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + //queue_signal(env, info.si_signo, &info); + } +#endif + } + break; + default: +#ifdef TARGET_SPARC64 + badtrap: +#endif + printf ("Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit (1); + } + process_pending_signals (env); + } +} + +#endif + +static void usage(void) +{ + printf("qemu-" TARGET_NAME " version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n" + "usage: qemu-" TARGET_NAME " [options] program [arguments...]\n" + "BSD CPU emulator (compiled for %s emulation)\n" + "\n" + "Standard options:\n" + "-h print this help\n" + "-g port wait gdb connection to port\n" + "-L path set the elf interpreter prefix (default=%s)\n" + "-s size set the stack size in bytes (default=%ld)\n" + "-cpu model select CPU (-cpu help for list)\n" + "-drop-ld-preload drop LD_PRELOAD for target process\n" + "-E var=value sets/modifies targets environment variable(s)\n" + "-U var unsets targets environment variable(s)\n" +#if defined(CONFIG_USE_GUEST_BASE) + "-B address set guest_base address to address\n" +#endif + "-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n" + "\n" + "Debug options:\n" + "-d item1[,...] enable logging of specified items\n" + " (use '-d help' for a list of log items)\n" + "-D logfile write logs to 'logfile' (default stderr)\n" + "-p pagesize set the host page size to 'pagesize'\n" + "-singlestep always run in singlestep mode\n" + "-strace log system calls\n" + "\n" + "Environment variables:\n" + "QEMU_STRACE Print system calls and arguments similar to the\n" + " 'strace' program. Enable by setting to any value.\n" + "You can use -E and -U options to set/unset environment variables\n" + "for target process. It is possible to provide several variables\n" + "by repeating the option. For example:\n" + " -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n" + "Note that if you provide several changes to single variable\n" + "last change will stay in effect.\n" + , + TARGET_NAME, + interp_prefix, + x86_stack_size); + exit(1); +} + +THREAD CPUState *thread_cpu; + +/* Assumes contents are already zeroed. */ +void init_task_state(TaskState *ts) +{ + int i; + + ts->used = 1; + ts->first_free = ts->sigqueue_table; + for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) { + ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1]; + } + ts->sigqueue_table[i].next = NULL; +} + +int main(int argc, char **argv) +{ + const char *filename; + const char *cpu_model; + const char *log_file = NULL; + const char *log_mask = NULL; + struct target_pt_regs regs1, *regs = ®s1; + struct image_info info1, *info = &info1; + TaskState ts1, *ts = &ts1; + CPUArchState *env; + CPUState *cpu; + int optind; + const char *r; + int gdbstub_port = 0; + char **target_environ, **wrk; + envlist_t *envlist = NULL; + bsd_type = target_openbsd; + + if (argc <= 1) + usage(); + + module_call_init(MODULE_INIT_QOM); + + if ((envlist = envlist_create()) == NULL) { + (void) fprintf(stderr, "Unable to allocate envlist\n"); + exit(1); + } + + /* add current environment into the list */ + for (wrk = environ; *wrk != NULL; wrk++) { + (void) envlist_setenv(envlist, *wrk); + } + + cpu_model = NULL; +#if defined(cpudef_setup) + cpudef_setup(); /* parse cpu definitions in target config file (TBD) */ +#endif + + optind = 1; + for(;;) { + if (optind >= argc) + break; + r = argv[optind]; + if (r[0] != '-') + break; + optind++; + r++; + if (!strcmp(r, "-")) { + break; + } else if (!strcmp(r, "d")) { + if (optind >= argc) { + break; + } + log_mask = argv[optind++]; + } else if (!strcmp(r, "D")) { + if (optind >= argc) { + break; + } + log_file = argv[optind++]; + } else if (!strcmp(r, "E")) { + r = argv[optind++]; + if (envlist_setenv(envlist, r) != 0) + usage(); + } else if (!strcmp(r, "ignore-environment")) { + envlist_free(envlist); + if ((envlist = envlist_create()) == NULL) { + (void) fprintf(stderr, "Unable to allocate envlist\n"); + exit(1); + } + } else if (!strcmp(r, "U")) { + r = argv[optind++]; + if (envlist_unsetenv(envlist, r) != 0) + usage(); + } else if (!strcmp(r, "s")) { + r = argv[optind++]; + x86_stack_size = strtol(r, (char **)&r, 0); + if (x86_stack_size <= 0) + usage(); + if (*r == 'M') + x86_stack_size *= 1024 * 1024; + else if (*r == 'k' || *r == 'K') + x86_stack_size *= 1024; + } else if (!strcmp(r, "L")) { + interp_prefix = argv[optind++]; + } else if (!strcmp(r, "p")) { + qemu_host_page_size = atoi(argv[optind++]); + if (qemu_host_page_size == 0 || + (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) { + fprintf(stderr, "page size must be a power of two\n"); + exit(1); + } + } else if (!strcmp(r, "g")) { + gdbstub_port = atoi(argv[optind++]); + } else if (!strcmp(r, "r")) { + qemu_uname_release = argv[optind++]; + } else if (!strcmp(r, "cpu")) { + cpu_model = argv[optind++]; + if (is_help_option(cpu_model)) { +/* XXX: implement xxx_cpu_list for targets that still miss it */ +#if defined(cpu_list) + cpu_list(stdout, &fprintf); +#endif + exit(1); + } +#if defined(CONFIG_USE_GUEST_BASE) + } else if (!strcmp(r, "B")) { + guest_base = strtol(argv[optind++], NULL, 0); + have_guest_base = 1; +#endif + } else if (!strcmp(r, "drop-ld-preload")) { + (void) envlist_unsetenv(envlist, "LD_PRELOAD"); + } else if (!strcmp(r, "bsd")) { + if (!strcasecmp(argv[optind], "freebsd")) { + bsd_type = target_freebsd; + } else if (!strcasecmp(argv[optind], "netbsd")) { + bsd_type = target_netbsd; + } else if (!strcasecmp(argv[optind], "openbsd")) { + bsd_type = target_openbsd; + } else { + usage(); + } + optind++; + } else if (!strcmp(r, "singlestep")) { + singlestep = 1; + } else if (!strcmp(r, "strace")) { + do_strace = 1; + } else + { + usage(); + } + } + + /* init debug */ + qemu_set_log_filename(log_file); + if (log_mask) { + int mask; + + mask = qemu_str_to_log_mask(log_mask); + if (!mask) { + qemu_print_log_usage(stdout); + exit(1); + } + qemu_set_log(mask); + } + + if (optind >= argc) { + usage(); + } + filename = argv[optind]; + + /* Zero out regs */ + memset(regs, 0, sizeof(struct target_pt_regs)); + + /* Zero out image_info */ + memset(info, 0, sizeof(struct image_info)); + + /* Scan interp_prefix dir for replacement files. */ + init_paths(interp_prefix); + + if (cpu_model == NULL) { +#if defined(TARGET_I386) +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif +#elif defined(TARGET_SPARC) +#ifdef TARGET_SPARC64 + cpu_model = "TI UltraSparc II"; +#else + cpu_model = "Fujitsu MB86904"; +#endif +#else + cpu_model = "any"; +#endif + } + tcg_exec_init(0); + /* NOTE: we need to init the CPU at this stage to get + qemu_host_page_size */ + cpu = cpu_init(cpu_model); + if (!cpu) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + env = cpu->env_ptr; +#if defined(TARGET_SPARC) || defined(TARGET_PPC) + cpu_reset(cpu); +#endif + thread_cpu = cpu; + + if (getenv("QEMU_STRACE")) { + do_strace = 1; + } + + target_environ = envlist_to_environ(envlist, NULL); + envlist_free(envlist); + +#if defined(CONFIG_USE_GUEST_BASE) + /* + * Now that page sizes are configured in cpu_init() we can do + * proper page alignment for guest_base. + */ + guest_base = HOST_PAGE_ALIGN(guest_base); + + /* + * Read in mmap_min_addr kernel parameter. This value is used + * When loading the ELF image to determine whether guest_base + * is needed. + * + * When user has explicitly set the quest base, we skip this + * test. + */ + if (!have_guest_base) { + FILE *fp; + + if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) { + unsigned long tmp; + if (fscanf(fp, "%lu", &tmp) == 1) { + mmap_min_addr = tmp; + qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr); + } + fclose(fp); + } + } +#endif /* CONFIG_USE_GUEST_BASE */ + + if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) { + printf("Error loading %s\n", filename); + _exit(1); + } + + for (wrk = target_environ; *wrk; wrk++) { + free(*wrk); + } + + free(target_environ); + + if (qemu_log_enabled()) { +#if defined(CONFIG_USE_GUEST_BASE) + qemu_log("guest_base 0x%lx\n", guest_base); +#endif + log_page_dump(); + + qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); + qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); + qemu_log("start_code 0x" TARGET_ABI_FMT_lx "\n", + info->start_code); + qemu_log("start_data 0x" TARGET_ABI_FMT_lx "\n", + info->start_data); + qemu_log("end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data); + qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n", + info->start_stack); + qemu_log("brk 0x" TARGET_ABI_FMT_lx "\n", info->brk); + qemu_log("entry 0x" TARGET_ABI_FMT_lx "\n", info->entry); + } + + target_set_brk(info->brk); + syscall_init(); + signal_init(); + +#if defined(CONFIG_USE_GUEST_BASE) + /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay + generating the prologue until now so that the prologue can take + the real value of GUEST_BASE into account. */ + tcg_prologue_init(&tcg_ctx); +#endif + + /* build Task State */ + memset(ts, 0, sizeof(TaskState)); + init_task_state(ts); + ts->info = info; + cpu->opaque = ts; + +#if defined(TARGET_I386) + env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; + env->hflags |= HF_PE_MASK | HF_CPL_MASK; + if (env->features[FEAT_1_EDX] & CPUID_SSE) { + env->cr[4] |= CR4_OSFXSR_MASK; + env->hflags |= HF_OSFXSR_MASK; + } +#ifndef TARGET_ABI32 + /* enable 64 bit mode if possible */ + if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { + fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); + exit(1); + } + env->cr[4] |= CR4_PAE_MASK; + env->efer |= MSR_EFER_LMA | MSR_EFER_LME; + env->hflags |= HF_LMA_MASK; +#endif + + /* flags setup : we activate the IRQs by default as in user mode */ + env->eflags |= IF_MASK; + + /* linux register setup */ +#ifndef TARGET_ABI32 + env->regs[R_EAX] = regs->rax; + env->regs[R_EBX] = regs->rbx; + env->regs[R_ECX] = regs->rcx; + env->regs[R_EDX] = regs->rdx; + env->regs[R_ESI] = regs->rsi; + env->regs[R_EDI] = regs->rdi; + env->regs[R_EBP] = regs->rbp; + env->regs[R_ESP] = regs->rsp; + env->eip = regs->rip; +#else + env->regs[R_EAX] = regs->eax; + env->regs[R_EBX] = regs->ebx; + env->regs[R_ECX] = regs->ecx; + env->regs[R_EDX] = regs->edx; + env->regs[R_ESI] = regs->esi; + env->regs[R_EDI] = regs->edi; + env->regs[R_EBP] = regs->ebp; + env->regs[R_ESP] = regs->esp; + env->eip = regs->eip; +#endif + + /* linux interrupt setup */ +#ifndef TARGET_ABI32 + env->idt.limit = 511; +#else + env->idt.limit = 255; +#endif + env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + idt_table = g2h(env->idt.base); + set_idt(0, 0); + set_idt(1, 0); + set_idt(2, 0); + set_idt(3, 3); + set_idt(4, 3); + set_idt(5, 0); + set_idt(6, 0); + set_idt(7, 0); + set_idt(8, 0); + set_idt(9, 0); + set_idt(10, 0); + set_idt(11, 0); + set_idt(12, 0); + set_idt(13, 0); + set_idt(14, 0); + set_idt(15, 0); + set_idt(16, 0); + set_idt(17, 0); + set_idt(18, 0); + set_idt(19, 0); + set_idt(0x80, 3); + + /* linux segment setup */ + { + uint64_t *gdt_table; + env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; + gdt_table = g2h(env->gdt.base); +#ifdef TARGET_ABI32 + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#else + /* 64 bit code segment */ + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + DESC_L_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#endif + write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); + } + + cpu_x86_load_seg(env, R_CS, __USER_CS); + cpu_x86_load_seg(env, R_SS, __USER_DS); +#ifdef TARGET_ABI32 + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_FS, __USER_DS); + cpu_x86_load_seg(env, R_GS, __USER_DS); + /* This hack makes Wine work... */ + env->segs[R_FS].selector = 0; +#else + cpu_x86_load_seg(env, R_DS, 0); + cpu_x86_load_seg(env, R_ES, 0); + cpu_x86_load_seg(env, R_FS, 0); + cpu_x86_load_seg(env, R_GS, 0); +#endif +#elif defined(TARGET_SPARC) + { + int i; + env->pc = regs->pc; + env->npc = regs->npc; + env->y = regs->y; + for(i = 0; i < 8; i++) + env->gregs[i] = regs->u_regs[i]; + for(i = 0; i < 8; i++) + env->regwptr[i] = regs->u_regs[i + 8]; + } +#else +#error unsupported target CPU +#endif + + if (gdbstub_port) { + gdbserver_start (gdbstub_port); + gdb_handlesig(cpu, 0); + } + cpu_loop(env); + /* never exits */ + return 0; +} diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c new file mode 100644 index 0000000..092bf7f --- /dev/null +++ b/bsd-user/mmap.c @@ -0,0 +1,499 @@ +/* + * mmap support for qemu + * + * Copyright (c) 2003 - 2008 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include +#include +#include +#include +#include +#include +#include + +#include "qemu.h" +#include "qemu-common.h" +#include "bsd-mman.h" + +//#define DEBUG_MMAP + +#if defined(CONFIG_USE_NPTL) +pthread_mutex_t mmap_mutex; +static int __thread mmap_lock_count; + +void mmap_lock(void) +{ + if (mmap_lock_count++ == 0) { + pthread_mutex_lock(&mmap_mutex); + } +} + +void mmap_unlock(void) +{ + if (--mmap_lock_count == 0) { + pthread_mutex_unlock(&mmap_mutex); + } +} + +/* Grab lock to make sure things are in a consistent state after fork(). */ +void mmap_fork_start(void) +{ + if (mmap_lock_count) + abort(); + pthread_mutex_lock(&mmap_mutex); +} + +void mmap_fork_end(int child) +{ + if (child) + pthread_mutex_init(&mmap_mutex, NULL); + else + pthread_mutex_unlock(&mmap_mutex); +} +#else +/* We aren't threadsafe to start with, so no need to worry about locking. */ +void mmap_lock(void) +{ +} + +void mmap_unlock(void) +{ +} +#endif + +/* NOTE: all the constants are the HOST ones, but addresses are target. */ +int target_mprotect(abi_ulong start, abi_ulong len, int prot) +{ + abi_ulong end, host_start, host_end, addr; + int prot1, ret; + +#ifdef DEBUG_MMAP + printf("mprotect: start=0x" TARGET_FMT_lx + " len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len, + prot & PROT_READ ? 'r' : '-', + prot & PROT_WRITE ? 'w' : '-', + prot & PROT_EXEC ? 'x' : '-'); +#endif + + if ((start & ~TARGET_PAGE_MASK) != 0) + return -EINVAL; + len = TARGET_PAGE_ALIGN(len); + end = start + len; + if (end < start) + return -EINVAL; + prot &= PROT_READ | PROT_WRITE | PROT_EXEC; + if (len == 0) + return 0; + + mmap_lock(); + host_start = start & qemu_host_page_mask; + host_end = HOST_PAGE_ALIGN(end); + if (start > host_start) { + /* handle host page containing start */ + prot1 = prot; + for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) { + prot1 |= page_get_flags(addr); + } + if (host_end == host_start + qemu_host_page_size) { + for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { + prot1 |= page_get_flags(addr); + } + end = host_end; + } + ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS); + if (ret != 0) + goto error; + host_start += qemu_host_page_size; + } + if (end < host_end) { + prot1 = prot; + for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { + prot1 |= page_get_flags(addr); + } + ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size, + prot1 & PAGE_BITS); + if (ret != 0) + goto error; + host_end -= qemu_host_page_size; + } + + /* handle the pages in the middle */ + if (host_start < host_end) { + ret = mprotect(g2h(host_start), host_end - host_start, prot); + if (ret != 0) + goto error; + } + page_set_flags(start, start + len, prot | PAGE_VALID); + mmap_unlock(); + return 0; +error: + mmap_unlock(); + return ret; +} + +/* map an incomplete host page */ +static int mmap_frag(abi_ulong real_start, + abi_ulong start, abi_ulong end, + int prot, int flags, int fd, abi_ulong offset) +{ + abi_ulong real_end, addr; + void *host_start; + int prot1, prot_new; + + real_end = real_start + qemu_host_page_size; + host_start = g2h(real_start); + + /* get the protection of the target pages outside the mapping */ + prot1 = 0; + for(addr = real_start; addr < real_end; addr++) { + if (addr < start || addr >= end) + prot1 |= page_get_flags(addr); + } + + if (prot1 == 0) { + /* no page was there, so we allocate one */ + void *p = mmap(host_start, qemu_host_page_size, prot, + flags | MAP_ANON, -1, 0); + if (p == MAP_FAILED) + return -1; + prot1 = prot; + } + prot1 &= PAGE_BITS; + + prot_new = prot | prot1; + if (!(flags & MAP_ANON)) { + /* msync() won't work here, so we return an error if write is + possible while it is a shared mapping */ + if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED && + (prot & PROT_WRITE)) + return -1; + + /* adjust protection to be able to read */ + if (!(prot1 & PROT_WRITE)) + mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); + + /* read the corresponding file data */ + pread(fd, g2h(start), end - start, offset); + + /* put final protection */ + if (prot_new != (prot1 | PROT_WRITE)) + mprotect(host_start, qemu_host_page_size, prot_new); + } else { + /* just update the protection */ + if (prot_new != prot1) { + mprotect(host_start, qemu_host_page_size, prot_new); + } + } + return 0; +} + +#if defined(__CYGWIN__) +/* Cygwin doesn't have a whole lot of address space. */ +static abi_ulong mmap_next_start = 0x18000000; +#else +static abi_ulong mmap_next_start = 0x40000000; +#endif + +unsigned long last_brk; + +/* find a free memory area of size 'size'. The search starts at + 'start'. If 'start' == 0, then a default start address is used. + Return -1 if error. +*/ +/* page_init() marks pages used by the host as reserved to be sure not + to use them. */ +static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) +{ + abi_ulong addr, addr1, addr_start; + int prot; + unsigned long new_brk; + + new_brk = (unsigned long)sbrk(0); + if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) { + /* This is a hack to catch the host allocating memory with brk(). + If it uses mmap then we loose. + FIXME: We really want to avoid the host allocating memory in + the first place, and maybe leave some slack to avoid switching + to mmap. */ + page_set_flags(last_brk & TARGET_PAGE_MASK, + TARGET_PAGE_ALIGN(new_brk), + PAGE_RESERVED); + } + last_brk = new_brk; + + size = HOST_PAGE_ALIGN(size); + start = start & qemu_host_page_mask; + addr = start; + if (addr == 0) + addr = mmap_next_start; + addr_start = addr; + for(;;) { + prot = 0; + for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) { + prot |= page_get_flags(addr1); + } + if (prot == 0) + break; + addr += qemu_host_page_size; + /* we found nothing */ + if (addr == addr_start) + return (abi_ulong)-1; + } + if (start == 0) + mmap_next_start = addr + size; + return addr; +} + +/* NOTE: all the constants are the HOST ones */ +abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, + int flags, int fd, abi_ulong offset) +{ + abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; + unsigned long host_start; + + mmap_lock(); +#ifdef DEBUG_MMAP + { + printf("mmap: start=0x" TARGET_FMT_lx + " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=", + start, len, + prot & PROT_READ ? 'r' : '-', + prot & PROT_WRITE ? 'w' : '-', + prot & PROT_EXEC ? 'x' : '-'); + if (flags & MAP_FIXED) + printf("MAP_FIXED "); + if (flags & MAP_ANON) + printf("MAP_ANON "); + switch(flags & TARGET_BSD_MAP_FLAGMASK) { + case MAP_PRIVATE: + printf("MAP_PRIVATE "); + break; + case MAP_SHARED: + printf("MAP_SHARED "); + break; + default: + printf("[MAP_FLAGMASK=0x%x] ", flags & TARGET_BSD_MAP_FLAGMASK); + break; + } + printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset); + } +#endif + + if (offset & ~TARGET_PAGE_MASK) { + errno = EINVAL; + goto fail; + } + + len = TARGET_PAGE_ALIGN(len); + if (len == 0) + goto the_end; + real_start = start & qemu_host_page_mask; + + if (!(flags & MAP_FIXED)) { + abi_ulong mmap_start; + void *p; + host_offset = offset & qemu_host_page_mask; + host_len = len + offset - host_offset; + host_len = HOST_PAGE_ALIGN(host_len); + mmap_start = mmap_find_vma(real_start, host_len); + if (mmap_start == (abi_ulong)-1) { + errno = ENOMEM; + goto fail; + } + /* Note: we prefer to control the mapping address. It is + especially important if qemu_host_page_size > + qemu_real_host_page_size */ + p = mmap(g2h(mmap_start), + host_len, prot, flags | MAP_FIXED, fd, host_offset); + if (p == MAP_FAILED) + goto fail; + /* update start so that it points to the file position at 'offset' */ + host_start = (unsigned long)p; + if (!(flags & MAP_ANON)) + host_start += offset - host_offset; + start = h2g(host_start); + } else { + int flg; + target_ulong addr; + + if (start & ~TARGET_PAGE_MASK) { + errno = EINVAL; + goto fail; + } + end = start + len; + real_end = HOST_PAGE_ALIGN(end); + + for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) { + flg = page_get_flags(addr); + if (flg & PAGE_RESERVED) { + errno = ENXIO; + goto fail; + } + } + + /* worst case: we cannot map the file because the offset is not + aligned, so we read it */ + if (!(flags & MAP_ANON) && + (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { + /* msync() won't work here, so we return an error if write is + possible while it is a shared mapping */ + if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED && + (prot & PROT_WRITE)) { + errno = EINVAL; + goto fail; + } + retaddr = target_mmap(start, len, prot | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANON, + -1, 0); + if (retaddr == -1) + goto fail; + pread(fd, g2h(start), len, offset); + if (!(prot & PROT_WRITE)) { + ret = target_mprotect(start, len, prot); + if (ret != 0) { + start = ret; + goto the_end; + } + } + goto the_end; + } + + /* handle the start of the mapping */ + if (start > real_start) { + if (real_end == real_start + qemu_host_page_size) { + /* one single host page */ + ret = mmap_frag(real_start, start, end, + prot, flags, fd, offset); + if (ret == -1) + goto fail; + goto the_end1; + } + ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, + prot, flags, fd, offset); + if (ret == -1) + goto fail; + real_start += qemu_host_page_size; + } + /* handle the end of the mapping */ + if (end < real_end) { + ret = mmap_frag(real_end - qemu_host_page_size, + real_end - qemu_host_page_size, real_end, + prot, flags, fd, + offset + real_end - qemu_host_page_size - start); + if (ret == -1) + goto fail; + real_end -= qemu_host_page_size; + } + + /* map the middle (easier) */ + if (real_start < real_end) { + void *p; + unsigned long offset1; + if (flags & MAP_ANON) + offset1 = 0; + else + offset1 = offset + real_start - start; + p = mmap(g2h(real_start), real_end - real_start, + prot, flags, fd, offset1); + if (p == MAP_FAILED) + goto fail; + } + } + the_end1: + page_set_flags(start, start + len, prot | PAGE_VALID); + the_end: +#ifdef DEBUG_MMAP + printf("ret=0x" TARGET_FMT_lx "\n", start); + page_dump(stdout); + printf("\n"); +#endif + mmap_unlock(); + return start; +fail: + mmap_unlock(); + return -1; +} + +int target_munmap(abi_ulong start, abi_ulong len) +{ + abi_ulong end, real_start, real_end, addr; + int prot, ret; + +#ifdef DEBUG_MMAP + printf("munmap: start=0x%lx len=0x%lx\n", start, len); +#endif + if (start & ~TARGET_PAGE_MASK) + return -EINVAL; + len = TARGET_PAGE_ALIGN(len); + if (len == 0) + return -EINVAL; + mmap_lock(); + end = start + len; + real_start = start & qemu_host_page_mask; + real_end = HOST_PAGE_ALIGN(end); + + if (start > real_start) { + /* handle host page containing start */ + prot = 0; + for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { + prot |= page_get_flags(addr); + } + if (real_end == real_start + qemu_host_page_size) { + for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { + prot |= page_get_flags(addr); + } + end = real_end; + } + if (prot != 0) + real_start += qemu_host_page_size; + } + if (end < real_end) { + prot = 0; + for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { + prot |= page_get_flags(addr); + } + if (prot != 0) + real_end -= qemu_host_page_size; + } + + ret = 0; + /* unmap what we can */ + if (real_start < real_end) { + ret = munmap(g2h(real_start), real_end - real_start); + } + + if (ret == 0) + page_set_flags(start, start + len, 0); + mmap_unlock(); + return ret; +} + +int target_msync(abi_ulong start, abi_ulong len, int flags) +{ + abi_ulong end; + + if (start & ~TARGET_PAGE_MASK) + return -EINVAL; + len = TARGET_PAGE_ALIGN(len); + end = start + len; + if (end < start) + return -EINVAL; + if (end == start) + return 0; + + start &= qemu_host_page_mask; + return msync(g2h(start), end - start, flags); +} diff --git a/bsd-user/netbsd/strace.list b/bsd-user/netbsd/strace.list new file mode 100644 index 0000000..5609d70 --- /dev/null +++ b/bsd-user/netbsd/strace.list @@ -0,0 +1,145 @@ +{ TARGET_NETBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR___syscall, "__syscall", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL }, +{ TARGET_NETBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_NETBSD_NR_acct, "acct", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_adjtime, "adjtime", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_bind, "bind", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_break, "break", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL }, +{ TARGET_NETBSD_NR_chflags, "chflags", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_NETBSD_NR_chown, "chown", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_chroot, "chroot", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_clock_getres, "clock_getres", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_clock_gettime, "clock_gettime", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_close, "close", "%s(%d)", NULL, NULL }, +{ TARGET_NETBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_NETBSD_NR_dup, "dup", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_dup2, "dup2", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_execve, "execve", NULL, print_execve, NULL }, +{ TARGET_NETBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL }, +{ TARGET_NETBSD_NR_fchdir, "fchdir", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_fchflags, "fchflags", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL }, +{ TARGET_NETBSD_NR_fchown, "fchown", "%s(\"%s\",%d,%d)", NULL, NULL }, +{ TARGET_NETBSD_NR_fcntl, "fcntl", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_flock, "flock", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_fork, "fork", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_fsync, "fsync", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_futimes, "futimes", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getegid, "getegid", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_getgid, "getgid", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_getgroups, "getgroups", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getitimer, "getitimer", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getpeername, "getpeername", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getpgid, "getpgid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getpgrp, "getpgrp", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_getpid, "getpid", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_getppid, "getppid", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL }, +{ TARGET_NETBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getrusage, "getrusage", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getsid, "getsid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getsockname, "getsockname", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_getuid, "getuid", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_ioctl, "ioctl", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL }, +{ TARGET_NETBSD_NR_kevent, "kevent", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_kill, "kill", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_kqueue, "kqueue", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_ktrace, "ktrace", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_lchown, "lchown", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_lfs_bmapv, "lfs_bmapv", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_lfs_markv, "lfs_markv", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_lfs_segclean, "lfs_segclean", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_lfs_segwait, "lfs_segwait", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_NETBSD_NR_listen, "listen", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_lseek, "lseek", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_madvise, "madvise", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_mincore, "mincore", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_minherit, "minherit", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_NETBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL }, +{ TARGET_NETBSD_NR_mlock, "mlock", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_mlockall, "mlockall", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr }, +{ TARGET_NETBSD_NR_mprotect, "mprotect", "%s(%#x,%#x,%d)", NULL, NULL }, +{ TARGET_NETBSD_NR_msgget, "msgget", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_msgrcv, "msgrcv", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_msgsnd, "msgsnd", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_munlock, "munlock", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_munlockall, "munlockall", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_munmap, "munmap", "%s(%p,%d)", NULL, NULL }, +{ TARGET_NETBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL }, +{ TARGET_NETBSD_NR_pathconf, "pathconf", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_pipe, "pipe", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_poll, "poll", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_pread, "pread", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_preadv, "preadv", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_profil, "profil", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_ptrace, "ptrace", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_pwrite, "pwrite", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_pwritev, "pwritev", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_quotactl, "quotactl", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_read, "read", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_NETBSD_NR_readlink, "readlink", "%s(\"%s\",%p,%d)", NULL, NULL }, +{ TARGET_NETBSD_NR_readv, "readv", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_reboot, "reboot", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_recvfrom, "recvfrom", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_recvmsg, "recvmsg", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_rename, "rename", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_NETBSD_NR_revoke, "revoke", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_rmdir, "rmdir", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_sbrk, "sbrk", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_select, "select", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_semget, "semget", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_semop, "semop", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_sendto, "sendto", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setegid, "setegid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_seteuid, "seteuid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setgid, "setgid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setgroups, "setgroups", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setitimer, "setitimer", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setpgid, "setpgid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setpriority, "setpriority", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setregid, "setregid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setreuid, "setreuid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setrlimit, "setrlimit", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setsid, "setsid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setsockopt, "setsockopt", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_settimeofday, "settimeofday", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_setuid, "setuid", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_shmat, "shmat", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_shmdt, "shmdt", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_shmget, "shmget", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_shutdown, "shutdown", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_socketpair, "socketpair", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_sstk, "sstk", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_swapctl, "swapctl", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_NETBSD_NR_sync, "sync", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_sysarch, "sysarch", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_syscall, "syscall", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_truncate, "truncate", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL }, +{ TARGET_NETBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL }, +{ TARGET_NETBSD_NR_unmount, "unmount", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_utimes, "utimes", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_vfork, "vfork", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_wait4, "wait4", NULL, NULL, NULL }, +{ TARGET_NETBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_NETBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL }, diff --git a/bsd-user/netbsd/syscall_nr.h b/bsd-user/netbsd/syscall_nr.h new file mode 100644 index 0000000..2e9ab53 --- /dev/null +++ b/bsd-user/netbsd/syscall_nr.h @@ -0,0 +1,373 @@ +/* $NetBSD: syscall.h,v 1.215 2008/06/17 16:07:57 tsutsui Exp $ */ + +/* + * System call numbers. + * + * created from NetBSD: syscalls.master,v 1.204 2008/06/17 16:05:23 tsutsui Exp + */ + +#define TARGET_NETBSD_NR_syscall 0 +#define TARGET_NETBSD_NR_exit 1 +#define TARGET_NETBSD_NR_fork 2 +#define TARGET_NETBSD_NR_read 3 +#define TARGET_NETBSD_NR_write 4 +#define TARGET_NETBSD_NR_open 5 +#define TARGET_NETBSD_NR_close 6 +#define TARGET_NETBSD_NR_wait4 7 +#define TARGET_NETBSD_NR_compat_43_ocreat 8 +#define TARGET_NETBSD_NR_link 9 +#define TARGET_NETBSD_NR_unlink 10 +#define TARGET_NETBSD_NR_chdir 12 +#define TARGET_NETBSD_NR_fchdir 13 +#define TARGET_NETBSD_NR_mknod 14 +#define TARGET_NETBSD_NR_chmod 15 +#define TARGET_NETBSD_NR_chown 16 +#define TARGET_NETBSD_NR_break 17 +#define TARGET_NETBSD_NR_compat_20_getfsstat 18 +#define TARGET_NETBSD_NR_compat_43_olseek 19 +#define TARGET_NETBSD_NR_getpid 20 +#define TARGET_NETBSD_NR_getpid 20 +#define TARGET_NETBSD_NR_compat_40_mount 21 +#define TARGET_NETBSD_NR_unmount 22 +#define TARGET_NETBSD_NR_setuid 23 +#define TARGET_NETBSD_NR_getuid 24 +#define TARGET_NETBSD_NR_getuid 24 +#define TARGET_NETBSD_NR_geteuid 25 +#define TARGET_NETBSD_NR_ptrace 26 +#define TARGET_NETBSD_NR_recvmsg 27 +#define TARGET_NETBSD_NR_sendmsg 28 +#define TARGET_NETBSD_NR_recvfrom 29 +#define TARGET_NETBSD_NR_accept 30 +#define TARGET_NETBSD_NR_getpeername 31 +#define TARGET_NETBSD_NR_getsockname 32 +#define TARGET_NETBSD_NR_access 33 +#define TARGET_NETBSD_NR_chflags 34 +#define TARGET_NETBSD_NR_fchflags 35 +#define TARGET_NETBSD_NR_sync 36 +#define TARGET_NETBSD_NR_kill 37 +#define TARGET_NETBSD_NR_compat_43_stat43 38 +#define TARGET_NETBSD_NR_getppid 39 +#define TARGET_NETBSD_NR_compat_43_lstat43 40 +#define TARGET_NETBSD_NR_dup 41 +#define TARGET_NETBSD_NR_pipe 42 +#define TARGET_NETBSD_NR_getegid 43 +#define TARGET_NETBSD_NR_profil 44 +#define TARGET_NETBSD_NR_ktrace 45 +#define TARGET_NETBSD_NR_compat_13_sigaction13 46 +#define TARGET_NETBSD_NR_getgid 47 +#define TARGET_NETBSD_NR_getgid 47 +#define TARGET_NETBSD_NR_compat_13_sigprocmask13 48 +#define TARGET_NETBSD_NR___getlogin 49 +#define TARGET_NETBSD_NR___setlogin 50 +#define TARGET_NETBSD_NR_acct 51 +#define TARGET_NETBSD_NR_compat_13_sigpending13 52 +#define TARGET_NETBSD_NR_compat_13_sigaltstack13 53 +#define TARGET_NETBSD_NR_ioctl 54 +#define TARGET_NETBSD_NR_compat_12_oreboot 55 +#define TARGET_NETBSD_NR_revoke 56 +#define TARGET_NETBSD_NR_symlink 57 +#define TARGET_NETBSD_NR_readlink 58 +#define TARGET_NETBSD_NR_execve 59 +#define TARGET_NETBSD_NR_umask 60 +#define TARGET_NETBSD_NR_chroot 61 +#define TARGET_NETBSD_NR_compat_43_fstat43 62 +#define TARGET_NETBSD_NR_compat_43_ogetkerninfo 63 +#define TARGET_NETBSD_NR_compat_43_ogetpagesize 64 +#define TARGET_NETBSD_NR_compat_12_msync 65 +#define TARGET_NETBSD_NR_vfork 66 +#define TARGET_NETBSD_NR_sbrk 69 +#define TARGET_NETBSD_NR_sstk 70 +#define TARGET_NETBSD_NR_compat_43_ommap 71 +#define TARGET_NETBSD_NR_vadvise 72 +#define TARGET_NETBSD_NR_munmap 73 +#define TARGET_NETBSD_NR_mprotect 74 +#define TARGET_NETBSD_NR_madvise 75 +#define TARGET_NETBSD_NR_mincore 78 +#define TARGET_NETBSD_NR_getgroups 79 +#define TARGET_NETBSD_NR_setgroups 80 +#define TARGET_NETBSD_NR_getpgrp 81 +#define TARGET_NETBSD_NR_setpgid 82 +#define TARGET_NETBSD_NR_setitimer 83 +#define TARGET_NETBSD_NR_compat_43_owait 84 +#define TARGET_NETBSD_NR_compat_12_oswapon 85 +#define TARGET_NETBSD_NR_getitimer 86 +#define TARGET_NETBSD_NR_compat_43_ogethostname 87 +#define TARGET_NETBSD_NR_compat_43_osethostname 88 +#define TARGET_NETBSD_NR_compat_43_ogetdtablesize 89 +#define TARGET_NETBSD_NR_dup2 90 +#define TARGET_NETBSD_NR_fcntl 92 +#define TARGET_NETBSD_NR_select 93 +#define TARGET_NETBSD_NR_fsync 95 +#define TARGET_NETBSD_NR_setpriority 96 +#define TARGET_NETBSD_NR_compat_30_socket 97 +#define TARGET_NETBSD_NR_connect 98 +#define TARGET_NETBSD_NR_compat_43_oaccept 99 +#define TARGET_NETBSD_NR_getpriority 100 +#define TARGET_NETBSD_NR_compat_43_osend 101 +#define TARGET_NETBSD_NR_compat_43_orecv 102 +#define TARGET_NETBSD_NR_compat_13_sigreturn13 103 +#define TARGET_NETBSD_NR_bind 104 +#define TARGET_NETBSD_NR_setsockopt 105 +#define TARGET_NETBSD_NR_listen 106 +#define TARGET_NETBSD_NR_compat_43_osigvec 108 +#define TARGET_NETBSD_NR_compat_43_osigblock 109 +#define TARGET_NETBSD_NR_compat_43_osigsetmask 110 +#define TARGET_NETBSD_NR_compat_13_sigsuspend13 111 +#define TARGET_NETBSD_NR_compat_43_osigstack 112 +#define TARGET_NETBSD_NR_compat_43_orecvmsg 113 +#define TARGET_NETBSD_NR_compat_43_osendmsg 114 +#define TARGET_NETBSD_NR_gettimeofday 116 +#define TARGET_NETBSD_NR_getrusage 117 +#define TARGET_NETBSD_NR_getsockopt 118 +#define TARGET_NETBSD_NR_readv 120 +#define TARGET_NETBSD_NR_writev 121 +#define TARGET_NETBSD_NR_settimeofday 122 +#define TARGET_NETBSD_NR_fchown 123 +#define TARGET_NETBSD_NR_fchmod 124 +#define TARGET_NETBSD_NR_compat_43_orecvfrom 125 +#define TARGET_NETBSD_NR_setreuid 126 +#define TARGET_NETBSD_NR_setregid 127 +#define TARGET_NETBSD_NR_rename 128 +#define TARGET_NETBSD_NR_compat_43_otruncate 129 +#define TARGET_NETBSD_NR_compat_43_oftruncate 130 +#define TARGET_NETBSD_NR_flock 131 +#define TARGET_NETBSD_NR_mkfifo 132 +#define TARGET_NETBSD_NR_sendto 133 +#define TARGET_NETBSD_NR_shutdown 134 +#define TARGET_NETBSD_NR_socketpair 135 +#define TARGET_NETBSD_NR_mkdir 136 +#define TARGET_NETBSD_NR_rmdir 137 +#define TARGET_NETBSD_NR_utimes 138 +#define TARGET_NETBSD_NR_adjtime 140 +#define TARGET_NETBSD_NR_compat_43_ogetpeername 141 +#define TARGET_NETBSD_NR_compat_43_ogethostid 142 +#define TARGET_NETBSD_NR_compat_43_osethostid 143 +#define TARGET_NETBSD_NR_compat_43_ogetrlimit 144 +#define TARGET_NETBSD_NR_compat_43_osetrlimit 145 +#define TARGET_NETBSD_NR_compat_43_okillpg 146 +#define TARGET_NETBSD_NR_setsid 147 +#define TARGET_NETBSD_NR_quotactl 148 +#define TARGET_NETBSD_NR_compat_43_oquota 149 +#define TARGET_NETBSD_NR_compat_43_ogetsockname 150 +#define TARGET_NETBSD_NR_nfssvc 155 +#define TARGET_NETBSD_NR_compat_43_ogetdirentries 156 +#define TARGET_NETBSD_NR_compat_20_statfs 157 +#define TARGET_NETBSD_NR_compat_20_fstatfs 158 +#define TARGET_NETBSD_NR_compat_30_getfh 161 +#define TARGET_NETBSD_NR_compat_09_ogetdomainname 162 +#define TARGET_NETBSD_NR_compat_09_osetdomainname 163 +#define TARGET_NETBSD_NR_compat_09_ouname 164 +#define TARGET_NETBSD_NR_sysarch 165 +#define TARGET_NETBSD_NR_compat_10_osemsys 169 +#define TARGET_NETBSD_NR_compat_10_omsgsys 170 +#define TARGET_NETBSD_NR_compat_10_oshmsys 171 +#define TARGET_NETBSD_NR_pread 173 +#define TARGET_NETBSD_NR_pwrite 174 +#define TARGET_NETBSD_NR_compat_30_ntp_gettime 175 +#define TARGET_NETBSD_NR_ntp_adjtime 176 +#define TARGET_NETBSD_NR_setgid 181 +#define TARGET_NETBSD_NR_setegid 182 +#define TARGET_NETBSD_NR_seteuid 183 +#define TARGET_NETBSD_NR_lfs_bmapv 184 +#define TARGET_NETBSD_NR_lfs_markv 185 +#define TARGET_NETBSD_NR_lfs_segclean 186 +#define TARGET_NETBSD_NR_lfs_segwait 187 +#define TARGET_NETBSD_NR_compat_12_stat12 188 +#define TARGET_NETBSD_NR_compat_12_fstat12 189 +#define TARGET_NETBSD_NR_compat_12_lstat12 190 +#define TARGET_NETBSD_NR_pathconf 191 +#define TARGET_NETBSD_NR_fpathconf 192 +#define TARGET_NETBSD_NR_getrlimit 194 +#define TARGET_NETBSD_NR_setrlimit 195 +#define TARGET_NETBSD_NR_compat_12_getdirentries 196 +#define TARGET_NETBSD_NR_mmap 197 +#define TARGET_NETBSD_NR___syscall 198 +#define TARGET_NETBSD_NR_lseek 199 +#define TARGET_NETBSD_NR_truncate 200 +#define TARGET_NETBSD_NR_ftruncate 201 +#define TARGET_NETBSD_NR___sysctl 202 +#define TARGET_NETBSD_NR_mlock 203 +#define TARGET_NETBSD_NR_munlock 204 +#define TARGET_NETBSD_NR_undelete 205 +#define TARGET_NETBSD_NR_futimes 206 +#define TARGET_NETBSD_NR_getpgid 207 +#define TARGET_NETBSD_NR_reboot 208 +#define TARGET_NETBSD_NR_poll 209 +#define TARGET_NETBSD_NR_compat_14___semctl 220 +#define TARGET_NETBSD_NR_semget 221 +#define TARGET_NETBSD_NR_semop 222 +#define TARGET_NETBSD_NR_semconfig 223 +#define TARGET_NETBSD_NR_compat_14_msgctl 224 +#define TARGET_NETBSD_NR_msgget 225 +#define TARGET_NETBSD_NR_msgsnd 226 +#define TARGET_NETBSD_NR_msgrcv 227 +#define TARGET_NETBSD_NR_shmat 228 +#define TARGET_NETBSD_NR_compat_14_shmctl 229 +#define TARGET_NETBSD_NR_shmdt 230 +#define TARGET_NETBSD_NR_shmget 231 +#define TARGET_NETBSD_NR_clock_gettime 232 +#define TARGET_NETBSD_NR_clock_settime 233 +#define TARGET_NETBSD_NR_clock_getres 234 +#define TARGET_NETBSD_NR_timer_create 235 +#define TARGET_NETBSD_NR_timer_delete 236 +#define TARGET_NETBSD_NR_timer_settime 237 +#define TARGET_NETBSD_NR_timer_gettime 238 +#define TARGET_NETBSD_NR_timer_getoverrun 239 +#define TARGET_NETBSD_NR_nanosleep 240 +#define TARGET_NETBSD_NR_fdatasync 241 +#define TARGET_NETBSD_NR_mlockall 242 +#define TARGET_NETBSD_NR_munlockall 243 +#define TARGET_NETBSD_NR___sigtimedwait 244 +#define TARGET_NETBSD_NR_modctl 246 +#define TARGET_NETBSD_NR__ksem_init 247 +#define TARGET_NETBSD_NR__ksem_open 248 +#define TARGET_NETBSD_NR__ksem_unlink 249 +#define TARGET_NETBSD_NR__ksem_close 250 +#define TARGET_NETBSD_NR__ksem_post 251 +#define TARGET_NETBSD_NR__ksem_wait 252 +#define TARGET_NETBSD_NR__ksem_trywait 253 +#define TARGET_NETBSD_NR__ksem_getvalue 254 +#define TARGET_NETBSD_NR__ksem_destroy 255 +#define TARGET_NETBSD_NR_mq_open 257 +#define TARGET_NETBSD_NR_mq_close 258 +#define TARGET_NETBSD_NR_mq_unlink 259 +#define TARGET_NETBSD_NR_mq_getattr 260 +#define TARGET_NETBSD_NR_mq_setattr 261 +#define TARGET_NETBSD_NR_mq_notify 262 +#define TARGET_NETBSD_NR_mq_send 263 +#define TARGET_NETBSD_NR_mq_receive 264 +#define TARGET_NETBSD_NR_mq_timedsend 265 +#define TARGET_NETBSD_NR_mq_timedreceive 266 +#define TARGET_NETBSD_NR___posix_rename 270 +#define TARGET_NETBSD_NR_swapctl 271 +#define TARGET_NETBSD_NR_compat_30_getdents 272 +#define TARGET_NETBSD_NR_minherit 273 +#define TARGET_NETBSD_NR_lchmod 274 +#define TARGET_NETBSD_NR_lchown 275 +#define TARGET_NETBSD_NR_lutimes 276 +#define TARGET_NETBSD_NR___msync13 277 +#define TARGET_NETBSD_NR_compat_30___stat13 278 +#define TARGET_NETBSD_NR_compat_30___fstat13 279 +#define TARGET_NETBSD_NR_compat_30___lstat13 280 +#define TARGET_NETBSD_NR___sigaltstack14 281 +#define TARGET_NETBSD_NR___vfork14 282 +#define TARGET_NETBSD_NR___posix_chown 283 +#define TARGET_NETBSD_NR___posix_fchown 284 +#define TARGET_NETBSD_NR___posix_lchown 285 +#define TARGET_NETBSD_NR_getsid 286 +#define TARGET_NETBSD_NR___clone 287 +#define TARGET_NETBSD_NR_fktrace 288 +#define TARGET_NETBSD_NR_preadv 289 +#define TARGET_NETBSD_NR_pwritev 290 +#define TARGET_NETBSD_NR_compat_16___sigaction14 291 +#define TARGET_NETBSD_NR___sigpending14 292 +#define TARGET_NETBSD_NR___sigprocmask14 293 +#define TARGET_NETBSD_NR___sigsuspend14 294 +#define TARGET_NETBSD_NR_compat_16___sigreturn14 295 +#define TARGET_NETBSD_NR___getcwd 296 +#define TARGET_NETBSD_NR_fchroot 297 +#define TARGET_NETBSD_NR_compat_30_fhopen 298 +#define TARGET_NETBSD_NR_compat_30_fhstat 299 +#define TARGET_NETBSD_NR_compat_20_fhstatfs 300 +#define TARGET_NETBSD_NR_____semctl13 301 +#define TARGET_NETBSD_NR___msgctl13 302 +#define TARGET_NETBSD_NR___shmctl13 303 +#define TARGET_NETBSD_NR_lchflags 304 +#define TARGET_NETBSD_NR_issetugid 305 +#define TARGET_NETBSD_NR_utrace 306 +#define TARGET_NETBSD_NR_getcontext 307 +#define TARGET_NETBSD_NR_setcontext 308 +#define TARGET_NETBSD_NR__lwp_create 309 +#define TARGET_NETBSD_NR__lwp_exit 310 +#define TARGET_NETBSD_NR__lwp_self 311 +#define TARGET_NETBSD_NR__lwp_wait 312 +#define TARGET_NETBSD_NR__lwp_suspend 313 +#define TARGET_NETBSD_NR__lwp_continue 314 +#define TARGET_NETBSD_NR__lwp_wakeup 315 +#define TARGET_NETBSD_NR__lwp_getprivate 316 +#define TARGET_NETBSD_NR__lwp_setprivate 317 +#define TARGET_NETBSD_NR__lwp_kill 318 +#define TARGET_NETBSD_NR__lwp_detach 319 +#define TARGET_NETBSD_NR__lwp_park 320 +#define TARGET_NETBSD_NR__lwp_unpark 321 +#define TARGET_NETBSD_NR__lwp_unpark_all 322 +#define TARGET_NETBSD_NR__lwp_setname 323 +#define TARGET_NETBSD_NR__lwp_getname 324 +#define TARGET_NETBSD_NR__lwp_ctl 325 +#define TARGET_NETBSD_NR_sa_register 330 +#define TARGET_NETBSD_NR_sa_stacks 331 +#define TARGET_NETBSD_NR_sa_enable 332 +#define TARGET_NETBSD_NR_sa_setconcurrency 333 +#define TARGET_NETBSD_NR_sa_yield 334 +#define TARGET_NETBSD_NR_sa_preempt 335 +#define TARGET_NETBSD_NR_sa_unblockyield 336 +#define TARGET_NETBSD_NR___sigaction_sigtramp 340 +#define TARGET_NETBSD_NR_pmc_get_info 341 +#define TARGET_NETBSD_NR_pmc_control 342 +#define TARGET_NETBSD_NR_rasctl 343 +#define TARGET_NETBSD_NR_kqueue 344 +#define TARGET_NETBSD_NR_kevent 345 +#define TARGET_NETBSD_NR__sched_setparam 346 +#define TARGET_NETBSD_NR__sched_getparam 347 +#define TARGET_NETBSD_NR__sched_setaffinity 348 +#define TARGET_NETBSD_NR__sched_getaffinity 349 +#define TARGET_NETBSD_NR_sched_yield 350 +#define TARGET_NETBSD_NR_fsync_range 354 +#define TARGET_NETBSD_NR_uuidgen 355 +#define TARGET_NETBSD_NR_getvfsstat 356 +#define TARGET_NETBSD_NR_statvfs1 357 +#define TARGET_NETBSD_NR_fstatvfs1 358 +#define TARGET_NETBSD_NR_compat_30_fhstatvfs1 359 +#define TARGET_NETBSD_NR_extattrctl 360 +#define TARGET_NETBSD_NR_extattr_set_file 361 +#define TARGET_NETBSD_NR_extattr_get_file 362 +#define TARGET_NETBSD_NR_extattr_delete_file 363 +#define TARGET_NETBSD_NR_extattr_set_fd 364 +#define TARGET_NETBSD_NR_extattr_get_fd 365 +#define TARGET_NETBSD_NR_extattr_delete_fd 366 +#define TARGET_NETBSD_NR_extattr_set_link 367 +#define TARGET_NETBSD_NR_extattr_get_link 368 +#define TARGET_NETBSD_NR_extattr_delete_link 369 +#define TARGET_NETBSD_NR_extattr_list_fd 370 +#define TARGET_NETBSD_NR_extattr_list_file 371 +#define TARGET_NETBSD_NR_extattr_list_link 372 +#define TARGET_NETBSD_NR_pselect 373 +#define TARGET_NETBSD_NR_pollts 374 +#define TARGET_NETBSD_NR_setxattr 375 +#define TARGET_NETBSD_NR_lsetxattr 376 +#define TARGET_NETBSD_NR_fsetxattr 377 +#define TARGET_NETBSD_NR_getxattr 378 +#define TARGET_NETBSD_NR_lgetxattr 379 +#define TARGET_NETBSD_NR_fgetxattr 380 +#define TARGET_NETBSD_NR_listxattr 381 +#define TARGET_NETBSD_NR_llistxattr 382 +#define TARGET_NETBSD_NR_flistxattr 383 +#define TARGET_NETBSD_NR_removexattr 384 +#define TARGET_NETBSD_NR_lremovexattr 385 +#define TARGET_NETBSD_NR_fremovexattr 386 +#define TARGET_NETBSD_NR___stat30 387 +#define TARGET_NETBSD_NR___fstat30 388 +#define TARGET_NETBSD_NR___lstat30 389 +#define TARGET_NETBSD_NR___getdents30 390 +#define TARGET_NETBSD_NR_compat_30___fhstat30 392 +#define TARGET_NETBSD_NR___ntp_gettime30 393 +#define TARGET_NETBSD_NR___socket30 394 +#define TARGET_NETBSD_NR___getfh30 395 +#define TARGET_NETBSD_NR___fhopen40 396 +#define TARGET_NETBSD_NR___fhstatvfs140 397 +#define TARGET_NETBSD_NR___fhstat40 398 +#define TARGET_NETBSD_NR_aio_cancel 399 +#define TARGET_NETBSD_NR_aio_error 400 +#define TARGET_NETBSD_NR_aio_fsync 401 +#define TARGET_NETBSD_NR_aio_read 402 +#define TARGET_NETBSD_NR_aio_return 403 +#define TARGET_NETBSD_NR_aio_suspend 404 +#define TARGET_NETBSD_NR_aio_write 405 +#define TARGET_NETBSD_NR_lio_listio 406 +#define TARGET_NETBSD_NR___mount50 410 +#define TARGET_NETBSD_NR_mremap 411 +#define TARGET_NETBSD_NR_pset_create 412 +#define TARGET_NETBSD_NR_pset_destroy 413 +#define TARGET_NETBSD_NR_pset_assign 414 +#define TARGET_NETBSD_NR__pset_bind 415 +#define TARGET_NETBSD_NR___posix_fadvise50 416 diff --git a/bsd-user/openbsd/strace.list b/bsd-user/openbsd/strace.list new file mode 100644 index 0000000..1f0a331 --- /dev/null +++ b/bsd-user/openbsd/strace.list @@ -0,0 +1,187 @@ +{ TARGET_OPENBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR___semctl, "__semctl", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR___syscall, "__syscall", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL }, +{ TARGET_OPENBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_OPENBSD_NR_acct, "acct", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_adjfreq, "adjfreq", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_adjtime, "adjtime", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_bind, "bind", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_break, "break", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL }, +{ TARGET_OPENBSD_NR_chflags, "chflags", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_OPENBSD_NR_chown, "chown", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_chroot, "chroot", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_clock_getres, "clock_getres", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_clock_gettime, "clock_gettime", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_close, "close", "%s(%d)", NULL, NULL }, +{ TARGET_OPENBSD_NR_closefrom, "closefrom", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_OPENBSD_NR_dup, "dup", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_dup2, "dup2", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_execve, "execve", NULL, print_execve, NULL }, +{ TARGET_OPENBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL }, +{ TARGET_OPENBSD_NR_fchdir, "fchdir", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_fchflags, "fchflags", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL }, +{ TARGET_OPENBSD_NR_fchown, "fchown", "%s(\"%s\",%d,%d)", NULL, NULL }, +{ TARGET_OPENBSD_NR_fcntl, "fcntl", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_fhopen, "fhopen", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_fhstat, "fhstat", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_fhstatfs, "fhstatfs", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_flock, "flock", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_fork, "fork", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_fstat, "fstat", "%s(%d,%p)", NULL, NULL }, +{ TARGET_OPENBSD_NR_fstatfs, "fstatfs", "%s(%d,%p)", NULL, NULL }, +{ TARGET_OPENBSD_NR_fsync, "fsync", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_futimes, "futimes", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getdirentries, "getdirentries", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getegid, "getegid", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_getfh, "getfh", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getfsstat, "getfsstat", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getgid, "getgid", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_getgroups, "getgroups", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getitimer, "getitimer", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getlogin, "getlogin", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getpeereid, "getpeereid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getpeername, "getpeername", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getpgid, "getpgid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getpgrp, "getpgrp", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_getpid, "getpid", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_getppid, "getppid", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL }, +{ TARGET_OPENBSD_NR_getresgid, "getresgid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getresuid, "getresuid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getrusage, "getrusage", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getsid, "getsid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getsockname, "getsockname", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getthrid, "getthrid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_getuid, "getuid", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_ioctl, "ioctl", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL }, +{ TARGET_OPENBSD_NR_kevent, "kevent", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_kill, "kill", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_kqueue, "kqueue", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_ktrace, "ktrace", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_lchown, "lchown", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_lfs_bmapv, "lfs_bmapv", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_lfs_markv, "lfs_markv", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_lfs_segclean, "lfs_segclean", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_lfs_segwait, "lfs_segwait", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_OPENBSD_NR_listen, "listen", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_lseek, "lseek", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_lstat, "lstat", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_OPENBSD_NR_madvise, "madvise", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_mincore, "mincore", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_minherit, "minherit", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL }, +{ TARGET_OPENBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL }, +{ TARGET_OPENBSD_NR_mlock, "mlock", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_mlockall, "mlockall", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr }, +{ TARGET_OPENBSD_NR_mount, "mount", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_mprotect, "mprotect", "%s(%#x,%#x,%d)", NULL, NULL }, +{ TARGET_OPENBSD_NR_mquery, "mquery", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_msgctl, "msgctl", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_msgget, "msgget", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_msgrcv, "msgrcv", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_msgsnd, "msgsnd", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_msync, "msync", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_munlock, "munlock", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_munlockall, "munlockall", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_munmap, "munmap", "%s(%p,%d)", NULL, NULL }, +{ TARGET_OPENBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL }, +{ TARGET_OPENBSD_NR_opipe, "opipe", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_osigaltstack, "osigaltstack", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_pathconf, "pathconf", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_pipe, "pipe", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_poll, "poll", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_pread, "pread", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_preadv, "preadv", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_profil, "profil", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_ptrace, "ptrace", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_pwrite, "pwrite", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_pwritev, "pwritev", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_quotactl, "quotactl", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_read, "read", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_OPENBSD_NR_readlink, "readlink", "%s(\"%s\",%p,%d)", NULL, NULL }, +{ TARGET_OPENBSD_NR_readv, "readv", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_reboot, "reboot", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_recvfrom, "recvfrom", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_recvmsg, "recvmsg", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_rename, "rename", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_OPENBSD_NR_revoke, "revoke", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_rfork, "rfork", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_rmdir, "rmdir", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sbrk, "sbrk", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_select, "select", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_semget, "semget", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_semop, "semop", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sendto, "sendto", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setegid, "setegid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_seteuid, "seteuid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setgid, "setgid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setgroups, "setgroups", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setitimer, "setitimer", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setlogin, "setlogin", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setpgid, "setpgid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setpriority, "setpriority", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setregid, "setregid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setresgid, "setresgid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setresuid, "setresuid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setreuid, "setreuid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setrlimit, "setrlimit", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setsid, "setsid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setsockopt, "setsockopt", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_settimeofday, "settimeofday", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_setuid, "setuid", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_shmat, "shmat", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_shmctl, "shmctl", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_shmdt, "shmdt", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_shmget, "shmget", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_shutdown, "shutdown", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sigaction, "sigaction", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sigaltstack, "sigaltstack", "%s(%p,%p)", NULL, NULL }, +{ TARGET_OPENBSD_NR_sigpending, "sigpending", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sigprocmask, "sigprocmask", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sigreturn, "sigreturn", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sigsuspend, "sigsuspend", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_socket, "socket", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_socketpair, "socketpair", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sstk, "sstk", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_stat, "stat", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_OPENBSD_NR_statfs, "statfs", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_OPENBSD_NR_swapctl, "swapctl", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL }, +{ TARGET_OPENBSD_NR_sync, "sync", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_sysarch, "sysarch", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_syscall, "syscall", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_threxit, "threxit", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_thrsigdivert, "thrsigdivert", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_thrsleep, "thrsleep", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_thrwakeup, "thrwakeup", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_truncate, "truncate", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL }, +{ TARGET_OPENBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL }, +{ TARGET_OPENBSD_NR_unmount, "unmount", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_utimes, "utimes", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_vfork, "vfork", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_wait4, "wait4", NULL, NULL, NULL }, +{ TARGET_OPENBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_OPENBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL }, +{ TARGET_OPENBSD_NR_xfspioctl, "xfspioctl", NULL, NULL, NULL }, diff --git a/bsd-user/openbsd/syscall_nr.h b/bsd-user/openbsd/syscall_nr.h new file mode 100644 index 0000000..dececfd --- /dev/null +++ b/bsd-user/openbsd/syscall_nr.h @@ -0,0 +1,225 @@ +/* $OpenBSD: syscall.h,v 1.101 2008/03/16 19:43:41 otto Exp $ */ + +/* + * System call numbers. + * + * created from; OpenBSD: syscalls.master,v 1.90 2008/03/16 19:42:57 otto Exp + */ + +#define TARGET_OPENBSD_NR_syscall 0 +#define TARGET_OPENBSD_NR_exit 1 +#define TARGET_OPENBSD_NR_fork 2 +#define TARGET_OPENBSD_NR_read 3 +#define TARGET_OPENBSD_NR_write 4 +#define TARGET_OPENBSD_NR_open 5 +#define TARGET_OPENBSD_NR_close 6 +#define TARGET_OPENBSD_NR_wait4 7 +#define TARGET_OPENBSD_NR_link 9 +#define TARGET_OPENBSD_NR_unlink 10 +#define TARGET_OPENBSD_NR_chdir 12 +#define TARGET_OPENBSD_NR_fchdir 13 +#define TARGET_OPENBSD_NR_mknod 14 +#define TARGET_OPENBSD_NR_chmod 15 +#define TARGET_OPENBSD_NR_chown 16 +#define TARGET_OPENBSD_NR_break 17 +#define TARGET_OPENBSD_NR_getpid 20 +#define TARGET_OPENBSD_NR_mount 21 +#define TARGET_OPENBSD_NR_unmount 22 +#define TARGET_OPENBSD_NR_setuid 23 +#define TARGET_OPENBSD_NR_getuid 24 +#define TARGET_OPENBSD_NR_geteuid 25 +#define TARGET_OPENBSD_NR_ptrace 26 +#define TARGET_OPENBSD_NR_recvmsg 27 +#define TARGET_OPENBSD_NR_sendmsg 28 +#define TARGET_OPENBSD_NR_recvfrom 29 +#define TARGET_OPENBSD_NR_accept 30 +#define TARGET_OPENBSD_NR_getpeername 31 +#define TARGET_OPENBSD_NR_getsockname 32 +#define TARGET_OPENBSD_NR_access 33 +#define TARGET_OPENBSD_NR_chflags 34 +#define TARGET_OPENBSD_NR_fchflags 35 +#define TARGET_OPENBSD_NR_sync 36 +#define TARGET_OPENBSD_NR_kill 37 +#define TARGET_OPENBSD_NR_getppid 39 +#define TARGET_OPENBSD_NR_dup 41 +#define TARGET_OPENBSD_NR_opipe 42 +#define TARGET_OPENBSD_NR_getegid 43 +#define TARGET_OPENBSD_NR_profil 44 +#define TARGET_OPENBSD_NR_ktrace 45 +#define TARGET_OPENBSD_NR_sigaction 46 +#define TARGET_OPENBSD_NR_getgid 47 +#define TARGET_OPENBSD_NR_sigprocmask 48 +#define TARGET_OPENBSD_NR_getlogin 49 +#define TARGET_OPENBSD_NR_setlogin 50 +#define TARGET_OPENBSD_NR_acct 51 +#define TARGET_OPENBSD_NR_sigpending 52 +#define TARGET_OPENBSD_NR_osigaltstack 53 +#define TARGET_OPENBSD_NR_ioctl 54 +#define TARGET_OPENBSD_NR_reboot 55 +#define TARGET_OPENBSD_NR_revoke 56 +#define TARGET_OPENBSD_NR_symlink 57 +#define TARGET_OPENBSD_NR_readlink 58 +#define TARGET_OPENBSD_NR_execve 59 +#define TARGET_OPENBSD_NR_umask 60 +#define TARGET_OPENBSD_NR_chroot 61 +#define TARGET_OPENBSD_NR_vfork 66 +#define TARGET_OPENBSD_NR_sbrk 69 +#define TARGET_OPENBSD_NR_sstk 70 +#define TARGET_OPENBSD_NR_munmap 73 +#define TARGET_OPENBSD_NR_mprotect 74 +#define TARGET_OPENBSD_NR_madvise 75 +#define TARGET_OPENBSD_NR_mincore 78 +#define TARGET_OPENBSD_NR_getgroups 79 +#define TARGET_OPENBSD_NR_setgroups 80 +#define TARGET_OPENBSD_NR_getpgrp 81 +#define TARGET_OPENBSD_NR_setpgid 82 +#define TARGET_OPENBSD_NR_setitimer 83 +#define TARGET_OPENBSD_NR_getitimer 86 +#define TARGET_OPENBSD_NR_dup2 90 +#define TARGET_OPENBSD_NR_fcntl 92 +#define TARGET_OPENBSD_NR_select 93 +#define TARGET_OPENBSD_NR_fsync 95 +#define TARGET_OPENBSD_NR_setpriority 96 +#define TARGET_OPENBSD_NR_socket 97 +#define TARGET_OPENBSD_NR_connect 98 +#define TARGET_OPENBSD_NR_getpriority 100 +#define TARGET_OPENBSD_NR_sigreturn 103 +#define TARGET_OPENBSD_NR_bind 104 +#define TARGET_OPENBSD_NR_setsockopt 105 +#define TARGET_OPENBSD_NR_listen 106 +#define TARGET_OPENBSD_NR_sigsuspend 111 +#define TARGET_OPENBSD_NR_gettimeofday 116 +#define TARGET_OPENBSD_NR_getrusage 117 +#define TARGET_OPENBSD_NR_getsockopt 118 +#define TARGET_OPENBSD_NR_readv 120 +#define TARGET_OPENBSD_NR_writev 121 +#define TARGET_OPENBSD_NR_settimeofday 122 +#define TARGET_OPENBSD_NR_fchown 123 +#define TARGET_OPENBSD_NR_fchmod 124 +#define TARGET_OPENBSD_NR_setreuid 126 +#define TARGET_OPENBSD_NR_setregid 127 +#define TARGET_OPENBSD_NR_rename 128 +#define TARGET_OPENBSD_NR_flock 131 +#define TARGET_OPENBSD_NR_mkfifo 132 +#define TARGET_OPENBSD_NR_sendto 133 +#define TARGET_OPENBSD_NR_shutdown 134 +#define TARGET_OPENBSD_NR_socketpair 135 +#define TARGET_OPENBSD_NR_mkdir 136 +#define TARGET_OPENBSD_NR_rmdir 137 +#define TARGET_OPENBSD_NR_utimes 138 +#define TARGET_OPENBSD_NR_adjtime 140 +#define TARGET_OPENBSD_NR_setsid 147 +#define TARGET_OPENBSD_NR_quotactl 148 +#define TARGET_OPENBSD_NR_nfssvc 155 +#define TARGET_OPENBSD_NR_getfh 161 +#define TARGET_OPENBSD_NR_sysarch 165 +#define TARGET_OPENBSD_NR_pread 173 +#define TARGET_OPENBSD_NR_pwrite 174 +#define TARGET_OPENBSD_NR_setgid 181 +#define TARGET_OPENBSD_NR_setegid 182 +#define TARGET_OPENBSD_NR_seteuid 183 +#define TARGET_OPENBSD_NR_lfs_bmapv 184 +#define TARGET_OPENBSD_NR_lfs_markv 185 +#define TARGET_OPENBSD_NR_lfs_segclean 186 +#define TARGET_OPENBSD_NR_lfs_segwait 187 +#define TARGET_OPENBSD_NR_pathconf 191 +#define TARGET_OPENBSD_NR_fpathconf 192 +#define TARGET_OPENBSD_NR_swapctl 193 +#define TARGET_OPENBSD_NR_getrlimit 194 +#define TARGET_OPENBSD_NR_setrlimit 195 +#define TARGET_OPENBSD_NR_getdirentries 196 +#define TARGET_OPENBSD_NR_mmap 197 +#define TARGET_OPENBSD_NR___syscall 198 +#define TARGET_OPENBSD_NR_lseek 199 +#define TARGET_OPENBSD_NR_truncate 200 +#define TARGET_OPENBSD_NR_ftruncate 201 +#define TARGET_OPENBSD_NR___sysctl 202 +#define TARGET_OPENBSD_NR_mlock 203 +#define TARGET_OPENBSD_NR_munlock 204 +#define TARGET_OPENBSD_NR_futimes 206 +#define TARGET_OPENBSD_NR_getpgid 207 +#define TARGET_OPENBSD_NR_xfspioctl 208 +#define TARGET_OPENBSD_NR_semget 221 +#define TARGET_OPENBSD_NR_msgget 225 +#define TARGET_OPENBSD_NR_msgsnd 226 +#define TARGET_OPENBSD_NR_msgrcv 227 +#define TARGET_OPENBSD_NR_shmat 228 +#define TARGET_OPENBSD_NR_shmdt 230 +#define TARGET_OPENBSD_NR_clock_gettime 232 +#define TARGET_OPENBSD_NR_clock_settime 233 +#define TARGET_OPENBSD_NR_clock_getres 234 +#define TARGET_OPENBSD_NR_nanosleep 240 +#define TARGET_OPENBSD_NR_minherit 250 +#define TARGET_OPENBSD_NR_rfork 251 +#define TARGET_OPENBSD_NR_poll 252 +#define TARGET_OPENBSD_NR_issetugid 253 +#define TARGET_OPENBSD_NR_lchown 254 +#define TARGET_OPENBSD_NR_getsid 255 +#define TARGET_OPENBSD_NR_msync 256 +#define TARGET_OPENBSD_NR_pipe 263 +#define TARGET_OPENBSD_NR_fhopen 264 +#define TARGET_OPENBSD_NR_preadv 267 +#define TARGET_OPENBSD_NR_pwritev 268 +#define TARGET_OPENBSD_NR_kqueue 269 +#define TARGET_OPENBSD_NR_kevent 270 +#define TARGET_OPENBSD_NR_mlockall 271 +#define TARGET_OPENBSD_NR_munlockall 272 +#define TARGET_OPENBSD_NR_getpeereid 273 +#define TARGET_OPENBSD_NR_getresuid 281 +#define TARGET_OPENBSD_NR_setresuid 282 +#define TARGET_OPENBSD_NR_getresgid 283 +#define TARGET_OPENBSD_NR_setresgid 284 +#define TARGET_OPENBSD_NR_mquery 286 +#define TARGET_OPENBSD_NR_closefrom 287 +#define TARGET_OPENBSD_NR_sigaltstack 288 +#define TARGET_OPENBSD_NR_shmget 289 +#define TARGET_OPENBSD_NR_semop 290 +#define TARGET_OPENBSD_NR_stat 291 +#define TARGET_OPENBSD_NR_fstat 292 +#define TARGET_OPENBSD_NR_lstat 293 +#define TARGET_OPENBSD_NR_fhstat 294 +#define TARGET_OPENBSD_NR___semctl 295 +#define TARGET_OPENBSD_NR_shmctl 296 +#define TARGET_OPENBSD_NR_msgctl 297 +#define TARGET_OPENBSD_NR_sched_yield 298 +#define TARGET_OPENBSD_NR_getthrid 299 +#define TARGET_OPENBSD_NR_thrsleep 300 +#define TARGET_OPENBSD_NR_thrwakeup 301 +#define TARGET_OPENBSD_NR_threxit 302 +#define TARGET_OPENBSD_NR_thrsigdivert 303 +#define TARGET_OPENBSD_NR___getcwd 304 +#define TARGET_OPENBSD_NR_adjfreq 305 +#define TARGET_OPENBSD_NR_getfsstat 306 +#define TARGET_OPENBSD_NR_statfs 307 +#define TARGET_OPENBSD_NR_fstatfs 308 +#define TARGET_OPENBSD_NR_fhstatfs 309 + +/* syscall flags from machine/trap.h */ + +/* $OpenBSD: trap.h,v 1.4 2008/07/04 22:04:37 kettenis Exp $ */ +/* $NetBSD: trap.h,v 1.4 1999/06/07 05:28:04 eeh Exp $ */ + +/* + * Copyright (c) 1996-1999 Eduardo Horvath + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#define TARGET_OPENBSD_SYSCALL_G2RFLAG 0x400 /* on success, return to %g2 rather than npc */ +#define TARGET_OPENBSD_SYSCALL_G7RFLAG 0x800 /* use %g7 as above (deprecated) */ diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h new file mode 100644 index 0000000..5362297 --- /dev/null +++ b/bsd-user/qemu.h @@ -0,0 +1,421 @@ +/* + * qemu bsd user mode definition + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef QEMU_H +#define QEMU_H + +#include +#include + +#include "cpu.h" +#include "exec/cpu_ldst.h" + +#undef DEBUG_REMAP +#ifdef DEBUG_REMAP +#include +#endif /* DEBUG_REMAP */ + +#include "exec/user/abitypes.h" + +enum BSDType { + target_freebsd, + target_netbsd, + target_openbsd, +}; +extern enum BSDType bsd_type; + +#include "syscall_defs.h" +#include "syscall.h" +#include "target_signal.h" +#include "exec/gdbstub.h" + +#if defined(CONFIG_USE_NPTL) +#define THREAD __thread +#else +#define THREAD +#endif + +/* This struct is used to hold certain information about the image. + * Basically, it replicates in user space what would be certain + * task_struct fields in the kernel + */ +struct image_info { + abi_ulong load_addr; + abi_ulong start_code; + abi_ulong end_code; + abi_ulong start_data; + abi_ulong end_data; + abi_ulong start_brk; + abi_ulong brk; + abi_ulong start_mmap; + abi_ulong mmap; + abi_ulong rss; + abi_ulong start_stack; + abi_ulong entry; + abi_ulong code_offset; + abi_ulong data_offset; + int personality; +}; + +#define MAX_SIGQUEUE_SIZE 1024 + +struct sigqueue { + struct sigqueue *next; + //target_siginfo_t info; +}; + +struct emulated_sigtable { + int pending; /* true if signal is pending */ + struct sigqueue *first; + struct sigqueue info; /* in order to always have memory for the + first signal, we put it here */ +}; + +/* NOTE: we force a big alignment so that the stack stored after is + aligned too */ +typedef struct TaskState { + struct TaskState *next; + int used; /* non zero if used */ + struct image_info *info; + + struct emulated_sigtable sigtab[TARGET_NSIG]; + struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ + struct sigqueue *first_free; /* first free siginfo queue entry */ + int signal_pending; /* non zero if a signal may be pending */ + + uint8_t stack[0]; +} __attribute__((aligned(16))) TaskState; + +void init_task_state(TaskState *ts); +extern const char *qemu_uname_release; +#if defined(CONFIG_USE_GUEST_BASE) +extern unsigned long mmap_min_addr; +#endif + +/* ??? See if we can avoid exposing so much of the loader internals. */ +/* + * MAX_ARG_PAGES defines the number of pages allocated for arguments + * and envelope for the new program. 32 should suffice, this gives + * a maximum env+arg of 128kB w/4KB pages! + */ +#define MAX_ARG_PAGES 32 + +/* + * This structure is used to hold the arguments that are + * used when loading binaries. + */ +struct linux_binprm { + char buf[128]; + void *page[MAX_ARG_PAGES]; + abi_ulong p; + int fd; + int e_uid, e_gid; + int argc, envc; + char **argv; + char **envp; + char * filename; /* Name of binary */ +}; + +void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); +abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, + abi_ulong stringp, int push_ptr); +int loader_exec(const char * filename, char ** argv, char ** envp, + struct target_pt_regs * regs, struct image_info *infop); + +int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, + struct image_info * info); +int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, + struct image_info * info); + +abi_long memcpy_to_target(abi_ulong dest, const void *src, + unsigned long len); +void target_set_brk(abi_ulong new_brk); +abi_long do_brk(abi_ulong new_brk); +void syscall_init(void); +abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8); +abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6); +abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6); +void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +extern THREAD CPUState *thread_cpu; +void cpu_loop(CPUArchState *env); +char *target_strerror(int err); +int get_osversion(void); +void fork_start(void); +void fork_end(int child); + +#include "qemu/log.h" + +/* strace.c */ +struct syscallname { + int nr; + const char *name; + const char *format; + void (*call)(const struct syscallname *, + abi_long, abi_long, abi_long, + abi_long, abi_long, abi_long); + void (*result)(const struct syscallname *, abi_long); +}; + +void +print_freebsd_syscall(int num, + abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6); +void print_freebsd_syscall_ret(int num, abi_long ret); +void +print_netbsd_syscall(int num, + abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6); +void print_netbsd_syscall_ret(int num, abi_long ret); +void +print_openbsd_syscall(int num, + abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6); +void print_openbsd_syscall_ret(int num, abi_long ret); +extern int do_strace; + +/* signal.c */ +void process_pending_signals(CPUArchState *cpu_env); +void signal_init(void); +//int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); +//void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info); +//void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo); +long do_sigreturn(CPUArchState *env); +long do_rt_sigreturn(CPUArchState *env); +abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); + +/* mmap.c */ +int target_mprotect(abi_ulong start, abi_ulong len, int prot); +abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, + int flags, int fd, abi_ulong offset); +int target_munmap(abi_ulong start, abi_ulong len); +abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, + abi_ulong new_size, unsigned long flags, + abi_ulong new_addr); +int target_msync(abi_ulong start, abi_ulong len, int flags); +extern unsigned long last_brk; +void mmap_lock(void); +void mmap_unlock(void); +void cpu_list_lock(void); +void cpu_list_unlock(void); +#if defined(CONFIG_USE_NPTL) +void mmap_fork_start(void); +void mmap_fork_end(int child); +#endif + +/* main.c */ +extern unsigned long x86_stack_size; + +/* user access */ + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 /* implies read access */ + +static inline int access_ok(int type, abi_ulong addr, abi_ulong size) +{ + return page_check_range((target_ulong)addr, size, + (type == VERIFY_READ) ? PAGE_READ : (PAGE_READ | PAGE_WRITE)) == 0; +} + +/* NOTE __get_user and __put_user use host pointers and don't check access. */ +/* These are usually used to access struct data members once the + * struct has been locked - usually with lock_user_struct(). + */ +#define __put_user(x, hptr)\ +({\ + int size = sizeof(*hptr);\ + switch(size) {\ + case 1:\ + *(uint8_t *)(hptr) = (uint8_t)(typeof(*hptr))(x);\ + break;\ + case 2:\ + *(uint16_t *)(hptr) = tswap16((typeof(*hptr))(x));\ + break;\ + case 4:\ + *(uint32_t *)(hptr) = tswap32((typeof(*hptr))(x));\ + break;\ + case 8:\ + *(uint64_t *)(hptr) = tswap64((typeof(*hptr))(x));\ + break;\ + default:\ + abort();\ + }\ + 0;\ +}) + +#define __get_user(x, hptr) \ +({\ + int size = sizeof(*hptr);\ + switch(size) {\ + case 1:\ + x = (typeof(*hptr))*(uint8_t *)(hptr);\ + break;\ + case 2:\ + x = (typeof(*hptr))tswap16(*(uint16_t *)(hptr));\ + break;\ + case 4:\ + x = (typeof(*hptr))tswap32(*(uint32_t *)(hptr));\ + break;\ + case 8:\ + x = (typeof(*hptr))tswap64(*(uint64_t *)(hptr));\ + break;\ + default:\ + /* avoid warning */\ + x = 0;\ + abort();\ + }\ + 0;\ +}) + +/* put_user()/get_user() take a guest address and check access */ +/* These are usually used to access an atomic data type, such as an int, + * that has been passed by address. These internally perform locking + * and unlocking on the data type. + */ +#define put_user(x, gaddr, target_type) \ +({ \ + abi_ulong __gaddr = (gaddr); \ + target_type *__hptr; \ + abi_long __ret; \ + if ((__hptr = lock_user(VERIFY_WRITE, __gaddr, sizeof(target_type), 0))) { \ + __ret = __put_user((x), __hptr); \ + unlock_user(__hptr, __gaddr, sizeof(target_type)); \ + } else \ + __ret = -TARGET_EFAULT; \ + __ret; \ +}) + +#define get_user(x, gaddr, target_type) \ +({ \ + abi_ulong __gaddr = (gaddr); \ + target_type *__hptr; \ + abi_long __ret; \ + if ((__hptr = lock_user(VERIFY_READ, __gaddr, sizeof(target_type), 1))) { \ + __ret = __get_user((x), __hptr); \ + unlock_user(__hptr, __gaddr, 0); \ + } else { \ + /* avoid warning */ \ + (x) = 0; \ + __ret = -TARGET_EFAULT; \ + } \ + __ret; \ +}) + +#define put_user_ual(x, gaddr) put_user((x), (gaddr), abi_ulong) +#define put_user_sal(x, gaddr) put_user((x), (gaddr), abi_long) +#define put_user_u64(x, gaddr) put_user((x), (gaddr), uint64_t) +#define put_user_s64(x, gaddr) put_user((x), (gaddr), int64_t) +#define put_user_u32(x, gaddr) put_user((x), (gaddr), uint32_t) +#define put_user_s32(x, gaddr) put_user((x), (gaddr), int32_t) +#define put_user_u16(x, gaddr) put_user((x), (gaddr), uint16_t) +#define put_user_s16(x, gaddr) put_user((x), (gaddr), int16_t) +#define put_user_u8(x, gaddr) put_user((x), (gaddr), uint8_t) +#define put_user_s8(x, gaddr) put_user((x), (gaddr), int8_t) + +#define get_user_ual(x, gaddr) get_user((x), (gaddr), abi_ulong) +#define get_user_sal(x, gaddr) get_user((x), (gaddr), abi_long) +#define get_user_u64(x, gaddr) get_user((x), (gaddr), uint64_t) +#define get_user_s64(x, gaddr) get_user((x), (gaddr), int64_t) +#define get_user_u32(x, gaddr) get_user((x), (gaddr), uint32_t) +#define get_user_s32(x, gaddr) get_user((x), (gaddr), int32_t) +#define get_user_u16(x, gaddr) get_user((x), (gaddr), uint16_t) +#define get_user_s16(x, gaddr) get_user((x), (gaddr), int16_t) +#define get_user_u8(x, gaddr) get_user((x), (gaddr), uint8_t) +#define get_user_s8(x, gaddr) get_user((x), (gaddr), int8_t) + +/* copy_from_user() and copy_to_user() are usually used to copy data + * buffers between the target and host. These internally perform + * locking/unlocking of the memory. + */ +abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len); +abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len); + +/* Functions for accessing guest memory. The tget and tput functions + read/write single values, byteswapping as necessary. The lock_user function + gets a pointer to a contiguous area of guest memory, but does not perform + any byteswapping. lock_user may return either a pointer to the guest + memory, or a temporary buffer. */ + +/* Lock an area of guest memory into the host. If copy is true then the + host area will have the same contents as the guest. */ +static inline void *lock_user(int type, abi_ulong guest_addr, long len, int copy) +{ + if (!access_ok(type, guest_addr, len)) + return NULL; +#ifdef DEBUG_REMAP + { + void *addr; + addr = malloc(len); + if (copy) + memcpy(addr, g2h(guest_addr), len); + else + memset(addr, 0, len); + return addr; + } +#else + return g2h(guest_addr); +#endif +} + +/* Unlock an area of guest memory. The first LEN bytes must be + flushed back to guest memory. host_ptr = NULL is explicitly + allowed and does nothing. */ +static inline void unlock_user(void *host_ptr, abi_ulong guest_addr, + long len) +{ + +#ifdef DEBUG_REMAP + if (!host_ptr) + return; + if (host_ptr == g2h(guest_addr)) + return; + if (len > 0) + memcpy(g2h(guest_addr), host_ptr, len); + free(host_ptr); +#endif +} + +/* Return the length of a string in target memory or -TARGET_EFAULT if + access error. */ +abi_long target_strlen(abi_ulong gaddr); + +/* Like lock_user but for null terminated strings. */ +static inline void *lock_user_string(abi_ulong guest_addr) +{ + abi_long len; + len = target_strlen(guest_addr); + if (len < 0) + return NULL; + return lock_user(VERIFY_READ, guest_addr, (long)(len + 1), 1); +} + +/* Helper macros for locking/unlocking a target struct. */ +#define lock_user_struct(type, host_ptr, guest_addr, copy) \ + (host_ptr = lock_user(type, guest_addr, sizeof(*host_ptr), copy)) +#define unlock_user_struct(host_ptr, guest_addr, copy) \ + unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0) + +#if defined(CONFIG_USE_NPTL) +#include +#endif + +#endif /* QEMU_H */ diff --git a/bsd-user/signal.c b/bsd-user/signal.c new file mode 100644 index 0000000..445f69e --- /dev/null +++ b/bsd-user/signal.c @@ -0,0 +1,38 @@ +/* + * Emulation of BSD signals + * + * Copyright (c) 2003 - 2008 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include +#include +#include +#include +#include +#include +#include + +#include "qemu.h" +#include "target_signal.h" + +//#define DEBUG_SIGNAL + +void signal_init(void) +{ +} + +void process_pending_signals(CPUArchState *cpu_env) +{ +} diff --git a/bsd-user/sparc/syscall.h b/bsd-user/sparc/syscall.h new file mode 100644 index 0000000..5a9bb7e --- /dev/null +++ b/bsd-user/sparc/syscall.h @@ -0,0 +1,9 @@ +struct target_pt_regs { + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; +}; + +#define UNAME_MACHINE "sun4" diff --git a/bsd-user/sparc/target_signal.h b/bsd-user/sparc/target_signal.h new file mode 100644 index 0000000..5b2abba --- /dev/null +++ b/bsd-user/sparc/target_signal.h @@ -0,0 +1,27 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_long ss_flags; + abi_ulong ss_size; +} target_stack_t; + + +#ifndef UREG_I6 +#define UREG_I6 6 +#endif +#ifndef UREG_FP +#define UREG_FP UREG_I6 +#endif + +static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) +{ + return state->regwptr[UREG_FP]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/bsd-user/sparc64/syscall.h b/bsd-user/sparc64/syscall.h new file mode 100644 index 0000000..81a816d --- /dev/null +++ b/bsd-user/sparc64/syscall.h @@ -0,0 +1,10 @@ +struct target_pt_regs { + abi_ulong u_regs[16]; + abi_ulong tstate; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong fprs; +}; + +#define UNAME_MACHINE "sun4u" diff --git a/bsd-user/sparc64/target_signal.h b/bsd-user/sparc64/target_signal.h new file mode 100644 index 0000000..5b2abba --- /dev/null +++ b/bsd-user/sparc64/target_signal.h @@ -0,0 +1,27 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_long ss_flags; + abi_ulong ss_size; +} target_stack_t; + + +#ifndef UREG_I6 +#define UREG_I6 6 +#endif +#ifndef UREG_FP +#define UREG_FP UREG_I6 +#endif + +static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) +{ + return state->regwptr[UREG_FP]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/bsd-user/strace.c b/bsd-user/strace.c new file mode 100644 index 0000000..e33dd4d --- /dev/null +++ b/bsd-user/strace.c @@ -0,0 +1,242 @@ +/* + * System call tracing and debugging + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qemu.h" + +int do_strace; + +/* + * Utility functions + */ + +static void print_sysctl(const struct syscallname *name, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, + abi_long arg6) +{ + uint32_t i; + int32_t *namep; + + gemu_log("%s({ ", name->name); + namep = lock_user(VERIFY_READ, arg1, sizeof(int32_t) * arg2, 1); + if (namep) { + int32_t *p = namep; + + for (i = 0; i < (uint32_t)arg2; i++) { + gemu_log("%d ", tswap32(*p++)); + } + unlock_user(namep, arg1, 0); + } + gemu_log("}, %u, 0x" TARGET_ABI_FMT_lx ", 0x" TARGET_ABI_FMT_lx ", 0x" + TARGET_ABI_FMT_lx ", 0x" TARGET_ABI_FMT_lx ")", + (uint32_t)arg2, arg3, arg4, arg5, arg6); +} + +static void print_execve(const struct syscallname *name, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, + abi_long arg6) +{ + abi_ulong arg_ptr_addr; + char *s; + + s = lock_user_string(arg1); + if (s == NULL) { + return; + } + gemu_log("%s(\"%s\",{", name->name, s); + unlock_user(s, arg1, 0); + + for (arg_ptr_addr = arg2; ; arg_ptr_addr += sizeof(abi_ulong)) { + abi_ulong *arg_ptr, arg_addr; + + arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1); + if (!arg_ptr) { + return; + } + arg_addr = tswapl(*arg_ptr); + unlock_user(arg_ptr, arg_ptr_addr, 0); + if (!arg_addr) { + break; + } + if ((s = lock_user_string(arg_addr))) { + gemu_log("\"%s\",", s); + unlock_user(s, arg_addr, 0); + } + } + gemu_log("NULL})"); +} + +static void print_ioctl(const struct syscallname *name, + abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6) +{ + /* Decode the ioctl request */ + gemu_log("%s(%d, 0x%0lx { IO%s%s GRP:0x%x('%c') CMD:%d LEN:%d }, 0x" + TARGET_ABI_FMT_lx ", ...)", + name->name, + (int)arg1, + (unsigned long)arg2, + arg2 & IOC_OUT ? "R" : "", + arg2 & IOC_IN ? "W" : "", + (unsigned)IOCGROUP(arg2), + isprint(IOCGROUP(arg2)) ? (char)IOCGROUP(arg2) : '?', + (int)arg2 & 0xFF, + (int)IOCPARM_LEN(arg2), + arg3); +} + +/* + * Variants for the return value output function + */ + +static void print_syscall_ret_addr(const struct syscallname *name, abi_long ret) +{ + if (ret == -1) { + gemu_log(" = -1 errno=%d (%s)\n", errno, strerror(errno)); + } else { + gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret); + } +} + +#if 0 /* currently unused */ +static void +print_syscall_ret_raw(struct syscallname *name, abi_long ret) +{ + gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret); +} +#endif + +/* + * An array of all of the syscalls we know about + */ + +static const struct syscallname freebsd_scnames[] = { +#include "freebsd/strace.list" +}; +static const struct syscallname netbsd_scnames[] = { +#include "netbsd/strace.list" +}; +static const struct syscallname openbsd_scnames[] = { +#include "openbsd/strace.list" +}; + +static void print_syscall(int num, const struct syscallname *scnames, + unsigned int nscnames, abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6) +{ + unsigned int i; + const char *format="%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," + TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," + TARGET_ABI_FMT_ld ")"; + + gemu_log("%d ", getpid() ); + + for (i = 0; i < nscnames; i++) { + if (scnames[i].nr == num) { + if (scnames[i].call != NULL) { + scnames[i].call(&scnames[i], arg1, arg2, arg3, arg4, arg5, + arg6); + } else { + /* XXX: this format system is broken because it uses + host types and host pointers for strings */ + if (scnames[i].format != NULL) { + format = scnames[i].format; + } + gemu_log(format, scnames[i].name, arg1, arg2, arg3, arg4, arg5, + arg6); + } + return; + } + } + gemu_log("Unknown syscall %d\n", num); +} + +static void print_syscall_ret(int num, abi_long ret, + const struct syscallname *scnames, unsigned int nscnames) +{ + unsigned int i; + + for (i = 0; i < nscnames; i++) { + if (scnames[i].nr == num) { + if (scnames[i].result != NULL) { + scnames[i].result(&scnames[i], ret); + } else { + if (ret < 0) { + gemu_log(" = -1 errno=" TARGET_ABI_FMT_ld " (%s)\n", -ret, + strerror(-ret)); + } else { + gemu_log(" = " TARGET_ABI_FMT_ld "\n", ret); + } + } + break; + } + } +} + +/* + * The public interface to this module. + */ +void print_freebsd_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6) +{ + + print_syscall(num, freebsd_scnames, ARRAY_SIZE(freebsd_scnames), arg1, arg2, + arg3, arg4, arg5, arg6); +} + +void print_freebsd_syscall_ret(int num, abi_long ret) +{ + + print_syscall_ret(num, ret, freebsd_scnames, ARRAY_SIZE(freebsd_scnames)); +} + +void print_netbsd_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6) +{ + + print_syscall(num, netbsd_scnames, ARRAY_SIZE(netbsd_scnames), + arg1, arg2, arg3, arg4, arg5, arg6); +} + +void print_netbsd_syscall_ret(int num, abi_long ret) +{ + + print_syscall_ret(num, ret, netbsd_scnames, ARRAY_SIZE(netbsd_scnames)); +} + +void print_openbsd_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6) +{ + + print_syscall(num, openbsd_scnames, ARRAY_SIZE(openbsd_scnames), arg1, arg2, + arg3, arg4, arg5, arg6); +} + +void print_openbsd_syscall_ret(int num, abi_long ret) +{ + + print_syscall_ret(num, ret, openbsd_scnames, ARRAY_SIZE(openbsd_scnames)); +} diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c new file mode 100644 index 0000000..a4d1583 --- /dev/null +++ b/bsd-user/syscall.c @@ -0,0 +1,564 @@ +/* + * BSD syscalls + * + * Copyright (c) 2003 - 2008 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qemu.h" +#include "qemu-common.h" + +//#define DEBUG + +static abi_ulong target_brk; +static abi_ulong target_original_brk; + +static inline abi_long get_errno(abi_long ret) +{ + if (ret == -1) + /* XXX need to translate host -> target errnos here */ + return -(errno); + else + return ret; +} + +#define target_to_host_bitmask(x, tbl) (x) + +static inline int is_error(abi_long ret) +{ + return (abi_ulong)ret >= (abi_ulong)(-4096); +} + +void target_set_brk(abi_ulong new_brk) +{ + target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk); +} + +/* do_obreak() must return target errnos. */ +static abi_long do_obreak(abi_ulong new_brk) +{ + abi_ulong brk_page; + abi_long mapped_addr; + int new_alloc_size; + + if (!new_brk) + return 0; + if (new_brk < target_original_brk) + return -TARGET_EINVAL; + + brk_page = HOST_PAGE_ALIGN(target_brk); + + /* If the new brk is less than this, set it and we're done... */ + if (new_brk < brk_page) { + target_brk = new_brk; + return 0; + } + + /* We need to allocate more memory after the brk... */ + new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1); + mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size, + PROT_READ|PROT_WRITE, + MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0)); + + if (!is_error(mapped_addr)) + target_brk = new_brk; + else + return mapped_addr; + + return 0; +} + +#if defined(TARGET_I386) +static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms) +{ + abi_long ret = 0; + abi_ulong val; + int idx; + + switch(op) { +#ifdef TARGET_ABI32 + case TARGET_FREEBSD_I386_SET_GSBASE: + case TARGET_FREEBSD_I386_SET_FSBASE: + if (op == TARGET_FREEBSD_I386_SET_GSBASE) +#else + case TARGET_FREEBSD_AMD64_SET_GSBASE: + case TARGET_FREEBSD_AMD64_SET_FSBASE: + if (op == TARGET_FREEBSD_AMD64_SET_GSBASE) +#endif + idx = R_GS; + else + idx = R_FS; + if (get_user(val, parms, abi_ulong)) + return -TARGET_EFAULT; + cpu_x86_load_seg(env, idx, 0); + env->segs[idx].base = val; + break; +#ifdef TARGET_ABI32 + case TARGET_FREEBSD_I386_GET_GSBASE: + case TARGET_FREEBSD_I386_GET_FSBASE: + if (op == TARGET_FREEBSD_I386_GET_GSBASE) +#else + case TARGET_FREEBSD_AMD64_GET_GSBASE: + case TARGET_FREEBSD_AMD64_GET_FSBASE: + if (op == TARGET_FREEBSD_AMD64_GET_GSBASE) +#endif + idx = R_GS; + else + idx = R_FS; + val = env->segs[idx].base; + if (put_user(val, parms, abi_ulong)) + return -TARGET_EFAULT; + break; + /* XXX handle the others... */ + default: + ret = -TARGET_EINVAL; + break; + } + return ret; +} +#endif + +#ifdef TARGET_SPARC +static abi_long do_freebsd_sysarch(void *env, int op, abi_ulong parms) +{ + /* XXX handle + * TARGET_FREEBSD_SPARC_UTRAP_INSTALL, + * TARGET_FREEBSD_SPARC_SIGTRAMP_INSTALL + */ + return -TARGET_EINVAL; +} +#endif + +#ifdef __FreeBSD__ +/* + * XXX this uses the undocumented oidfmt interface to find the kind of + * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt() + * (this is mostly copied from src/sbin/sysctl/sysctl.c) + */ +static int +oidfmt(int *oid, int len, char *fmt, uint32_t *kind) +{ + int qoid[CTL_MAXNAME+2]; + uint8_t buf[BUFSIZ]; + int i; + size_t j; + + qoid[0] = 0; + qoid[1] = 4; + memcpy(qoid + 2, oid, len * sizeof(int)); + + j = sizeof(buf); + i = sysctl(qoid, len + 2, buf, &j, 0, 0); + if (i) + return i; + + if (kind) + *kind = *(uint32_t *)buf; + + if (fmt) + strcpy(fmt, (char *)(buf + sizeof(uint32_t))); + return (0); +} + +/* + * try and convert sysctl return data for the target. + * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT. + */ +static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind) +{ + switch (kind & CTLTYPE) { + case CTLTYPE_INT: + case CTLTYPE_UINT: + *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp); + break; +#ifdef TARGET_ABI32 + case CTLTYPE_LONG: + case CTLTYPE_ULONG: + *(uint32_t *)holdp = tswap32(*(long *)holdp); + break; +#else + case CTLTYPE_LONG: + *(uint64_t *)holdp = tswap64(*(long *)holdp); + case CTLTYPE_ULONG: + *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp); + break; +#endif +#ifdef CTLTYPE_U64 + case CTLTYPE_S64: + case CTLTYPE_U64: +#else + case CTLTYPE_QUAD: +#endif + *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp); + break; + case CTLTYPE_STRING: + break; + default: + /* XXX unhandled */ + return -1; + } + return 0; +} + +/* XXX this needs to be emulated on non-FreeBSD hosts... */ +static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp, + abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen) +{ + abi_long ret; + void *hnamep, *holdp, *hnewp = NULL; + size_t holdlen; + abi_ulong oldlen = 0; + int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i; + uint32_t kind = 0; + + if (oldlenp) + get_user_ual(oldlen, oldlenp); + if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1))) + return -TARGET_EFAULT; + if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1))) + return -TARGET_EFAULT; + if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0))) + return -TARGET_EFAULT; + holdlen = oldlen; + for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++) + *q++ = tswap32(*p); + oidfmt(snamep, namelen, NULL, &kind); + /* XXX swap hnewp */ + ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen)); + if (!ret) + sysctl_oldcvt(holdp, holdlen, kind); + put_user_ual(holdlen, oldlenp); + unlock_user(hnamep, namep, 0); + unlock_user(holdp, oldp, holdlen); + if (hnewp) + unlock_user(hnewp, newp, 0); + g_free(snamep); + return ret; +} +#endif + +/* FIXME + * lock_iovec()/unlock_iovec() have a return code of 0 for success where + * other lock functions have a return code of 0 for failure. + */ +static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr, + int count, int copy) +{ + struct target_iovec *target_vec; + abi_ulong base; + int i; + + target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1); + if (!target_vec) + return -TARGET_EFAULT; + for(i = 0;i < count; i++) { + base = tswapl(target_vec[i].iov_base); + vec[i].iov_len = tswapl(target_vec[i].iov_len); + if (vec[i].iov_len != 0) { + vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy); + /* Don't check lock_user return value. We must call writev even + if a element has invalid base address. */ + } else { + /* zero length pointer is ignored */ + vec[i].iov_base = NULL; + } + } + unlock_user (target_vec, target_addr, 0); + return 0; +} + +static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr, + int count, int copy) +{ + struct target_iovec *target_vec; + abi_ulong base; + int i; + + target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1); + if (!target_vec) + return -TARGET_EFAULT; + for(i = 0;i < count; i++) { + if (target_vec[i].iov_base) { + base = tswapl(target_vec[i].iov_base); + unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0); + } + } + unlock_user (target_vec, target_addr, 0); + + return 0; +} + +/* do_syscall() should always have a single exit point at the end so + that actions, such as logging of syscall results, can be performed. + All errnos that do_syscall() returns must be -TARGET_. */ +abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) +{ + abi_long ret; + void *p; + +#ifdef DEBUG + gemu_log("freebsd syscall %d\n", num); +#endif + if(do_strace) + print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + + switch(num) { + case TARGET_FREEBSD_NR_exit: +#ifdef TARGET_GPROF + _mcleanup(); +#endif + gdb_exit(cpu_env, arg1); + /* XXX: should free thread stack and CPU env */ + _exit(arg1); + ret = 0; /* avoid warning */ + break; + case TARGET_FREEBSD_NR_read: + if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) + goto efault; + ret = get_errno(read(arg1, p, arg3)); + unlock_user(p, arg2, ret); + break; + case TARGET_FREEBSD_NR_write: + if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) + goto efault; + ret = get_errno(write(arg1, p, arg3)); + unlock_user(p, arg2, 0); + break; + case TARGET_FREEBSD_NR_writev: + { + int count = arg3; + struct iovec *vec; + + vec = alloca(count * sizeof(struct iovec)); + if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) + goto efault; + ret = get_errno(writev(arg1, vec, count)); + unlock_iovec(vec, arg2, count, 0); + } + break; + case TARGET_FREEBSD_NR_open: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(open(path(p), + target_to_host_bitmask(arg2, fcntl_flags_tbl), + arg3)); + unlock_user(p, arg1, 0); + break; + case TARGET_FREEBSD_NR_mmap: + ret = get_errno(target_mmap(arg1, arg2, arg3, + target_to_host_bitmask(arg4, mmap_flags_tbl), + arg5, + arg6)); + break; + case TARGET_FREEBSD_NR_mprotect: + ret = get_errno(target_mprotect(arg1, arg2, arg3)); + break; + case TARGET_FREEBSD_NR_break: + ret = do_obreak(arg1); + break; +#ifdef __FreeBSD__ + case TARGET_FREEBSD_NR___sysctl: + ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6); + break; +#endif + case TARGET_FREEBSD_NR_sysarch: + ret = do_freebsd_sysarch(cpu_env, arg1, arg2); + break; + case TARGET_FREEBSD_NR_syscall: + case TARGET_FREEBSD_NR___syscall: + ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0); + break; + default: + ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); + break; + } + fail: +#ifdef DEBUG + gemu_log(" = %ld\n", ret); +#endif + if (do_strace) + print_freebsd_syscall_ret(num, ret); + return ret; + efault: + ret = -TARGET_EFAULT; + goto fail; +} + +abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6) +{ + abi_long ret; + void *p; + +#ifdef DEBUG + gemu_log("netbsd syscall %d\n", num); +#endif + if(do_strace) + print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + + switch(num) { + case TARGET_NETBSD_NR_exit: +#ifdef TARGET_GPROF + _mcleanup(); +#endif + gdb_exit(cpu_env, arg1); + /* XXX: should free thread stack and CPU env */ + _exit(arg1); + ret = 0; /* avoid warning */ + break; + case TARGET_NETBSD_NR_read: + if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) + goto efault; + ret = get_errno(read(arg1, p, arg3)); + unlock_user(p, arg2, ret); + break; + case TARGET_NETBSD_NR_write: + if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) + goto efault; + ret = get_errno(write(arg1, p, arg3)); + unlock_user(p, arg2, 0); + break; + case TARGET_NETBSD_NR_open: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(open(path(p), + target_to_host_bitmask(arg2, fcntl_flags_tbl), + arg3)); + unlock_user(p, arg1, 0); + break; + case TARGET_NETBSD_NR_mmap: + ret = get_errno(target_mmap(arg1, arg2, arg3, + target_to_host_bitmask(arg4, mmap_flags_tbl), + arg5, + arg6)); + break; + case TARGET_NETBSD_NR_mprotect: + ret = get_errno(target_mprotect(arg1, arg2, arg3)); + break; + case TARGET_NETBSD_NR_syscall: + case TARGET_NETBSD_NR___syscall: + ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); + break; + default: + ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + break; + } + fail: +#ifdef DEBUG + gemu_log(" = %ld\n", ret); +#endif + if (do_strace) + print_netbsd_syscall_ret(num, ret); + return ret; + efault: + ret = -TARGET_EFAULT; + goto fail; +} + +abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6) +{ + abi_long ret; + void *p; + +#ifdef DEBUG + gemu_log("openbsd syscall %d\n", num); +#endif + if(do_strace) + print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + + switch(num) { + case TARGET_OPENBSD_NR_exit: +#ifdef TARGET_GPROF + _mcleanup(); +#endif + gdb_exit(cpu_env, arg1); + /* XXX: should free thread stack and CPU env */ + _exit(arg1); + ret = 0; /* avoid warning */ + break; + case TARGET_OPENBSD_NR_read: + if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) + goto efault; + ret = get_errno(read(arg1, p, arg3)); + unlock_user(p, arg2, ret); + break; + case TARGET_OPENBSD_NR_write: + if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) + goto efault; + ret = get_errno(write(arg1, p, arg3)); + unlock_user(p, arg2, 0); + break; + case TARGET_OPENBSD_NR_open: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(open(path(p), + target_to_host_bitmask(arg2, fcntl_flags_tbl), + arg3)); + unlock_user(p, arg1, 0); + break; + case TARGET_OPENBSD_NR_mmap: + ret = get_errno(target_mmap(arg1, arg2, arg3, + target_to_host_bitmask(arg4, mmap_flags_tbl), + arg5, + arg6)); + break; + case TARGET_OPENBSD_NR_mprotect: + ret = get_errno(target_mprotect(arg1, arg2, arg3)); + break; + case TARGET_OPENBSD_NR_syscall: + case TARGET_OPENBSD_NR___syscall: + ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); + break; + default: + ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + break; + } + fail: +#ifdef DEBUG + gemu_log(" = %ld\n", ret); +#endif + if (do_strace) + print_openbsd_syscall_ret(num, ret); + return ret; + efault: + ret = -TARGET_EFAULT; + goto fail; +} + +void syscall_init(void) +{ +} diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h new file mode 100644 index 0000000..207ddee --- /dev/null +++ b/bsd-user/syscall_defs.h @@ -0,0 +1,114 @@ +/* $OpenBSD: signal.h,v 1.19 2006/01/08 14:20:16 millert Exp $ */ +/* $NetBSD: signal.h,v 1.21 1996/02/09 18:25:32 christos Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)signal.h 8.2 (Berkeley) 1/21/94 + */ + +#define TARGET_NSIG 32 /* counting 0; could be 33 (mask is 1-32) */ + +#define TARGET_SIGHUP 1 /* hangup */ +#define TARGET_SIGINT 2 /* interrupt */ +#define TARGET_SIGQUIT 3 /* quit */ +#define TARGET_SIGILL 4 /* illegal instruction (not reset when caught) */ +#define TARGET_SIGTRAP 5 /* trace trap (not reset when caught) */ +#define TARGET_SIGABRT 6 /* abort() */ +#define TARGET_SIGIOT SIGABRT /* compatibility */ +#define TARGET_SIGEMT 7 /* EMT instruction */ +#define TARGET_SIGFPE 8 /* floating point exception */ +#define TARGET_SIGKILL 9 /* kill (cannot be caught or ignored) */ +#define TARGET_SIGBUS 10 /* bus error */ +#define TARGET_SIGSEGV 11 /* segmentation violation */ +#define TARGET_SIGSYS 12 /* bad argument to system call */ +#define TARGET_SIGPIPE 13 /* write on a pipe with no one to read it */ +#define TARGET_SIGALRM 14 /* alarm clock */ +#define TARGET_SIGTERM 15 /* software termination signal from kill */ +#define TARGET_SIGURG 16 /* urgent condition on IO channel */ +#define TARGET_SIGSTOP 17 /* sendable stop signal not from tty */ +#define TARGET_SIGTSTP 18 /* stop signal from tty */ +#define TARGET_SIGCONT 19 /* continue a stopped process */ +#define TARGET_SIGCHLD 20 /* to parent on child stop or exit */ +#define TARGET_SIGTTIN 21 /* to readers pgrp upon background tty read */ +#define TARGET_SIGTTOU 22 /* like TTIN for output if (tp->t_local<OSTOP) */ +#define TARGET_SIGIO 23 /* input/output possible signal */ +#define TARGET_SIGXCPU 24 /* exceeded CPU time limit */ +#define TARGET_SIGXFSZ 25 /* exceeded file size limit */ +#define TARGET_SIGVTALRM 26 /* virtual time alarm */ +#define TARGET_SIGPROF 27 /* profiling time alarm */ +#define TARGET_SIGWINCH 28 /* window size changes */ +#define TARGET_SIGINFO 29 /* information request */ +#define TARGET_SIGUSR1 30 /* user defined signal 1 */ +#define TARGET_SIGUSR2 31 /* user defined signal 2 */ + +/* + * Language spec says we must list exactly one parameter, even though we + * actually supply three. Ugh! + */ +#define TARGET_SIG_DFL (void (*)(int))0 +#define TARGET_SIG_IGN (void (*)(int))1 +#define TARGET_SIG_ERR (void (*)(int))-1 + +#define TARGET_SA_ONSTACK 0x0001 /* take signal on signal stack */ +#define TARGET_SA_RESTART 0x0002 /* restart system on signal return */ +#define TARGET_SA_RESETHAND 0x0004 /* reset to SIG_DFL when taking signal */ +#define TARGET_SA_NODEFER 0x0010 /* don't mask the signal we're delivering */ +#define TARGET_SA_NOCLDWAIT 0x0020 /* don't create zombies (assign to pid 1) */ +#define TARGET_SA_USERTRAMP 0x0100 /* do not bounce off kernel's sigtramp */ +#define TARGET_SA_NOCLDSTOP 0x0008 /* do not generate SIGCHLD on child stop */ +#define TARGET_SA_SIGINFO 0x0040 /* generate siginfo_t */ + +/* + * Flags for sigprocmask: + */ +#define TARGET_SIG_BLOCK 1 /* block specified signal set */ +#define TARGET_SIG_UNBLOCK 2 /* unblock specified signal set */ +#define TARGET_SIG_SETMASK 3 /* set specified signal set */ + +#define TARGET_BADSIG SIG_ERR + +#define TARGET_SS_ONSTACK 0x0001 /* take signals on alternate stack */ +#define TARGET_SS_DISABLE 0x0004 /* disable taking signals on alternate stack */ + +#include "errno_defs.h" + +#include "freebsd/syscall_nr.h" +#include "netbsd/syscall_nr.h" +#include "openbsd/syscall_nr.h" + +struct target_iovec { + abi_long iov_base; /* Starting address */ + abi_long iov_len; /* Number of bytes */ +}; + diff --git a/bsd-user/uaccess.c b/bsd-user/uaccess.c new file mode 100644 index 0000000..677f19c --- /dev/null +++ b/bsd-user/uaccess.c @@ -0,0 +1,65 @@ +/* User memory access */ +#include +#include + +#include "qemu.h" + +/* copy_from_user() and copy_to_user() are usually used to copy data + * buffers between the target and host. These internally perform + * locking/unlocking of the memory. + */ +abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len) +{ + abi_long ret = 0; + void *ghptr; + + if ((ghptr = lock_user(VERIFY_READ, gaddr, len, 1))) { + memcpy(hptr, ghptr, len); + unlock_user(ghptr, gaddr, 0); + } else + ret = -TARGET_EFAULT; + + return ret; +} + + +abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len) +{ + abi_long ret = 0; + void *ghptr; + + if ((ghptr = lock_user(VERIFY_WRITE, gaddr, len, 0))) { + memcpy(ghptr, hptr, len); + unlock_user(ghptr, gaddr, len); + } else + ret = -TARGET_EFAULT; + + return ret; +} + +/* Return the length of a string in target memory or -TARGET_EFAULT if + access error */ +abi_long target_strlen(abi_ulong guest_addr1) +{ + uint8_t *ptr; + abi_ulong guest_addr; + int max_len, len; + + guest_addr = guest_addr1; + for(;;) { + max_len = TARGET_PAGE_SIZE - (guest_addr & ~TARGET_PAGE_MASK); + ptr = lock_user(VERIFY_READ, guest_addr, max_len, 1); + if (!ptr) + return -TARGET_EFAULT; + len = qemu_strnlen((char *)ptr, max_len); + unlock_user(ptr, guest_addr, 0); + guest_addr += len; + /* we don't allow wrapping or integer overflow */ + if (guest_addr == 0 || + (guest_addr - guest_addr1) > 0x7fffffff) + return -TARGET_EFAULT; + if (len != max_len) + break; + } + return guest_addr - guest_addr1; +} diff --git a/bsd-user/x86_64/syscall.h b/bsd-user/x86_64/syscall.h new file mode 100644 index 0000000..630514a --- /dev/null +++ b/bsd-user/x86_64/syscall.h @@ -0,0 +1,116 @@ +#define __USER_CS (0x33) +#define __USER_DS (0x2B) + +struct target_pt_regs { + abi_ulong r15; + abi_ulong r14; + abi_ulong r13; + abi_ulong r12; + abi_ulong rbp; + abi_ulong rbx; +/* arguments: non interrupts/non tracing syscalls only save up to here */ + abi_ulong r11; + abi_ulong r10; + abi_ulong r9; + abi_ulong r8; + abi_ulong rax; + abi_ulong rcx; + abi_ulong rdx; + abi_ulong rsi; + abi_ulong rdi; + abi_ulong orig_rax; +/* end of arguments */ +/* cpu exception frame or undefined */ + abi_ulong rip; + abi_ulong cs; + abi_ulong eflags; + abi_ulong rsp; + abi_ulong ss; +/* top of stack page */ +}; + +/* Maximum number of LDT entries supported. */ +#define TARGET_LDT_ENTRIES 8192 +/* The size of each LDT entry. */ +#define TARGET_LDT_ENTRY_SIZE 8 + +#define TARGET_GDT_ENTRIES 16 +#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 +#define TARGET_GDT_ENTRY_TLS_MIN 12 +#define TARGET_GDT_ENTRY_TLS_MAX 14 + +#if 0 // Redefine this +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; + unsigned int useable:1; + unsigned int lm:1; +}; +#else +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; +#endif + +struct target_ipc64_perm +{ + int key; + uint32_t uid; + uint32_t gid; + uint32_t cuid; + uint32_t cgid; + unsigned short mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_msqid64_ds { + struct target_ipc64_perm msg_perm; + unsigned int msg_stime; /* last msgsnd time */ + unsigned int msg_rtime; /* last msgrcv time */ + unsigned int msg_ctime; /* last change time */ + abi_ulong msg_cbytes; /* current number of bytes on queue */ + abi_ulong msg_qnum; /* number of messages in queue */ + abi_ulong msg_qbytes; /* max number of bytes on queue */ + unsigned int msg_lspid; /* pid of last msgsnd */ + unsigned int msg_lrpid; /* last receive pid */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +/* FreeBSD sysarch(2) */ +#define TARGET_FREEBSD_I386_GET_LDT 0 +#define TARGET_FREEBSD_I386_SET_LDT 1 + /* I386_IOPL */ +#define TARGET_FREEBSD_I386_GET_IOPERM 3 +#define TARGET_FREEBSD_I386_SET_IOPERM 4 + /* xxxxx */ +#define TARGET_FREEBSD_I386_GET_FSBASE 7 +#define TARGET_FREEBSD_I386_SET_FSBASE 8 +#define TARGET_FREEBSD_I386_GET_GSBASE 9 +#define TARGET_FREEBSD_I386_SET_GSBASE 10 + +#define TARGET_FREEBSD_AMD64_GET_FSBASE 128 +#define TARGET_FREEBSD_AMD64_SET_FSBASE 129 +#define TARGET_FREEBSD_AMD64_GET_GSBASE 130 +#define TARGET_FREEBSD_AMD64_SET_GSBASE 131 + + +#define UNAME_MACHINE "x86_64" + +#define TARGET_ARCH_SET_GS 0x1001 +#define TARGET_ARCH_SET_FS 0x1002 +#define TARGET_ARCH_GET_FS 0x1003 +#define TARGET_ARCH_GET_GS 0x1004 diff --git a/bsd-user/x86_64/target_signal.h b/bsd-user/x86_64/target_signal.h new file mode 100644 index 0000000..659cd40 --- /dev/null +++ b/bsd-user/x86_64/target_signal.h @@ -0,0 +1,19 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_long ss_flags; + abi_ulong ss_size; +} target_stack_t; + +static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) +{ + return state->regs[R_ESP]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/bt-host.c b/bt-host.c new file mode 100644 index 0000000..49205bf --- /dev/null +++ b/bt-host.c @@ -0,0 +1,198 @@ +/* + * Wrap a host Bluetooth HCI socket in a struct HCIInfo. + * + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu-common.h" +#include "sysemu/bt.h" +#include "qemu/main-loop.h" + +#ifndef _WIN32 +# include +# include +# include +# ifdef CONFIG_BLUEZ +# include +# include +# include +# else +# include "hw/bt.h" +# define HCI_MAX_FRAME_SIZE 1028 +# endif + +struct bt_host_hci_s { + struct HCIInfo hci; + int fd; + + uint8_t hdr[HCI_MAX_FRAME_SIZE]; + int len; +}; + +static void bt_host_send(struct HCIInfo *hci, + int type, const uint8_t *data, int len) +{ + struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci; + uint8_t pkt = type; + struct iovec iv[2]; + + iv[0].iov_base = (void *)&pkt; + iv[0].iov_len = 1; + iv[1].iov_base = (void *) data; + iv[1].iov_len = len; + + while (writev(s->fd, iv, 2) < 0) { + if (errno != EAGAIN && errno != EINTR) { + fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", + errno); + return; + } + } +} + +static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len) +{ + bt_host_send(hci, HCI_COMMAND_PKT, data, len); +} + +static void bt_host_acl(struct HCIInfo *hci, const uint8_t *data, int len) +{ + bt_host_send(hci, HCI_ACLDATA_PKT, data, len); +} + +static void bt_host_sco(struct HCIInfo *hci, const uint8_t *data, int len) +{ + bt_host_send(hci, HCI_SCODATA_PKT, data, len); +} + +static void bt_host_read(void *opaque) +{ + struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; + uint8_t *pkt; + int pktlen; + + /* Seems that we can't read only the header first and then the amount + * of data indicated in the header because Linux will discard everything + * that's not been read in one go. */ + s->len = read(s->fd, s->hdr, sizeof(s->hdr)); + + if (s->len < 0) { + fprintf(stderr, "qemu: error %i reading HCI frame\n", errno); + return; + } + + pkt = s->hdr; + while (s->len --) + switch (*pkt ++) { + case HCI_EVENT_PKT: + if (s->len < 2) + goto bad_pkt; + + pktlen = MIN(pkt[1] + 2, s->len); + s->hci.evt_recv(s->hci.opaque, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + + /* TODO: if this is an Inquiry Result event, it's also + * interpreted by Linux kernel before we received it, possibly + * we should clean the kernel Inquiry cache through + * ioctl(s->fd, HCI_INQUIRY, ...). */ + break; + + case HCI_ACLDATA_PKT: + if (s->len < 4) + goto bad_pkt; + + pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); + s->hci.acl_recv(s->hci.opaque, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + break; + + case HCI_SCODATA_PKT: + if (s->len < 3) + goto bad_pkt; + + pktlen = MIN(pkt[2] + 3, s->len); + s->len -= pktlen; + pkt += pktlen; + break; + + default: + bad_pkt: + fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); + } +} + +static int bt_host_bdaddr_set(struct HCIInfo *hci, const uint8_t *bd_addr) +{ + return -ENOTSUP; +} + +struct HCIInfo *bt_host_hci(const char *id) +{ + struct bt_host_hci_s *s; + int fd = -1; +# ifdef CONFIG_BLUEZ + int dev_id = hci_devid(id); + struct hci_filter flt; + + if (dev_id < 0) { + fprintf(stderr, "qemu: `%s' not available\n", id); + return 0; + } + + fd = hci_open_dev(dev_id); + + /* XXX: can we ensure nobody else has the device opened? */ +# endif + + if (fd < 0) { + fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", + id, strerror(errno), errno); + return NULL; + } + +# ifdef CONFIG_BLUEZ + hci_filter_clear(&flt); + hci_filter_all_ptypes(&flt); + hci_filter_all_events(&flt); + + if (qemu_setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { + fprintf(stderr, "qemu: Can't set HCI filter on socket (%i)\n", errno); + return 0; + } +# endif + + s = g_malloc0(sizeof(struct bt_host_hci_s)); + s->fd = fd; + s->hci.cmd_send = bt_host_cmd; + s->hci.sco_send = bt_host_sco; + s->hci.acl_send = bt_host_acl; + s->hci.bdaddr_set = bt_host_bdaddr_set; + + qemu_set_fd_handler(s->fd, bt_host_read, NULL, s); + + return &s->hci; +} +#else +struct HCIInfo *bt_host_hci(const char *id) +{ + fprintf(stderr, "qemu: bluetooth passthrough not supported (yet)\n"); + + return 0; +} +#endif diff --git a/bt-vhci.c b/bt-vhci.c new file mode 100644 index 0000000..e267c8a --- /dev/null +++ b/bt-vhci.c @@ -0,0 +1,167 @@ +/* + * Support for host VHCIs inside qemu scatternets. + * + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu-common.h" +#include "sysemu/bt.h" +#include "hw/bt.h" +#include "qemu/main-loop.h" + +#define VHCI_DEV "/dev/vhci" +#define VHCI_UDEV "/dev/hci_vhci" + +struct bt_vhci_s { + int fd; + struct HCIInfo *info; + + uint8_t hdr[4096]; + int len; +}; + +static void vhci_read(void *opaque) +{ + struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; + uint8_t *pkt; + int pktlen; + + /* Seems that we can't read only the header first and then the amount + * of data indicated in the header because Linux will discard everything + * that's not been read in one go. */ + s->len = read(s->fd, s->hdr, sizeof(s->hdr)); + + if (s->len < 0) { + fprintf(stderr, "qemu: error %i reading the PDU\n", errno); + return; + } + + pkt = s->hdr; + while (s->len --) + switch (*pkt ++) { + case HCI_COMMAND_PKT: + if (s->len < 3) + goto bad_pkt; + + pktlen = MIN(pkt[2] + 3, s->len); + s->info->cmd_send(s->info, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + break; + + case HCI_ACLDATA_PKT: + if (s->len < 4) + goto bad_pkt; + + pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); + s->info->acl_send(s->info, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + break; + + case HCI_SCODATA_PKT: + if (s->len < 3) + goto bad_pkt; + + pktlen = MIN(pkt[2] + 3, s->len); + s->info->sco_send(s->info, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + break; + + default: + bad_pkt: + fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); + } +} + +static void vhci_host_send(void *opaque, + int type, const uint8_t *data, int len) +{ + struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; +#if 0 + uint8_t pkt = type; + struct iovec iv[2]; + + iv[0].iov_base = &pkt; + iv[0].iov_len = 1; + iv[1].iov_base = (void *) data; + iv[1].iov_len = len; + + while (writev(s->fd, iv, 2) < 0) + if (errno != EAGAIN && errno != EINTR) { + fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", + errno); + return; + } +#else + /* Apparently VHCI wants us to write everything in one chunk :-( */ + static uint8_t buf[4096]; + + buf[0] = type; + memcpy(buf + 1, data, len); + + while (write(s->fd, buf, len + 1) < 0) + if (errno != EAGAIN && errno != EINTR) { + fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", + errno); + return; + } +#endif +} + +static void vhci_out_hci_packet_event(void *opaque, + const uint8_t *data, int len) +{ + vhci_host_send(opaque, HCI_EVENT_PKT, data, len); +} + +static void vhci_out_hci_packet_acl(void *opaque, + const uint8_t *data, int len) +{ + vhci_host_send(opaque, HCI_ACLDATA_PKT, data, len); +} + +void bt_vhci_init(struct HCIInfo *info) +{ + struct bt_vhci_s *s; + int err[2]; + int fd; + + fd = open(VHCI_DEV, O_RDWR); + err[0] = errno; + if (fd < 0) { + fd = open(VHCI_UDEV, O_RDWR); + err[1] = errno; + } + + if (fd < 0) { + fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", + VHCI_DEV, strerror(err[0]), err[0]); + fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", + VHCI_UDEV, strerror(err[1]), err[1]); + exit(-1); + } + + s = g_malloc0(sizeof(struct bt_vhci_s)); + s->fd = fd; + s->info = info ?: qemu_next_hci(); + s->info->opaque = s; + s->info->evt_recv = vhci_out_hci_packet_event; + s->info->acl_recv = vhci_out_hci_packet_acl; + + qemu_set_fd_handler(s->fd, vhci_read, NULL, s); +} diff --git a/configure b/configure new file mode 100755 index 0000000..9852aef --- /dev/null +++ b/configure @@ -0,0 +1,5608 @@ +#!/bin/sh +# +# qemu configure script (c) 2003 Fabrice Bellard +# + +# Unset some variables known to interfere with behavior of common tools, +# just as autoconf does. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +# Temporary directory used for files created while +# configure runs. Since it is in the build directory +# we can safely blow away any previous version of it +# (and we need not jump through hoops to try to delete +# it when configure exits.) +TMPDIR1="config-temp" +rm -rf "${TMPDIR1}" +mkdir -p "${TMPDIR1}" +if [ $? -ne 0 ]; then + echo "ERROR: failed to create temporary directory" + exit 1 +fi + +TMPB="qemu-conf" +TMPC="${TMPDIR1}/${TMPB}.c" +TMPO="${TMPDIR1}/${TMPB}.o" +TMPCXX="${TMPDIR1}/${TMPB}.cxx" +TMPL="${TMPDIR1}/${TMPB}.lo" +TMPA="${TMPDIR1}/lib${TMPB}.la" +TMPE="${TMPDIR1}/${TMPB}.exe" + +rm -f config.log + +# Print a helpful header at the top of config.log +echo "# QEMU configure log $(date)" >> config.log +printf "# Configured with:" >> config.log +printf " '%s'" "$0" "$@" >> config.log +echo >> config.log +echo "#" >> config.log + +error_exit() { + echo + echo "ERROR: $1" + while test -n "$2"; do + echo " $2" + shift + done + echo + exit 1 +} + +do_compiler() { + # Run the compiler, capturing its output to the log. First argument + # is compiler binary to execute. + local compiler="$1" + shift + echo $compiler "$@" >> config.log + $compiler "$@" >> config.log 2>&1 || return $? + # Test passed. If this is an --enable-werror build, rerun + # the test with -Werror and bail out if it fails. This + # makes warning-generating-errors in configure test code + # obvious to developers. + if test "$werror" != "yes"; then + return 0 + fi + # Don't bother rerunning the compile if we were already using -Werror + case "$*" in + *-Werror*) + return 0 + ;; + esac + echo $compiler -Werror "$@" >> config.log + $compiler -Werror "$@" >> config.log 2>&1 && return $? + error_exit "configure test passed without -Werror but failed with -Werror." \ + "This is probably a bug in the configure script. The failing command" \ + "will be at the bottom of config.log." \ + "You can run configure with --disable-werror to bypass this check." +} + +do_cc() { + do_compiler "$cc" "$@" +} + +do_cxx() { + do_compiler "$cxx" "$@" +} + +update_cxxflags() { + # Set QEMU_CXXFLAGS from QEMU_CFLAGS by filtering out those + # options which some versions of GCC's C++ compiler complain about + # because they only make sense for C programs. + QEMU_CXXFLAGS= + for arg in $QEMU_CFLAGS; do + case $arg in + -Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\ + -Wold-style-declaration|-Wold-style-definition|-Wredundant-decls) + ;; + *) + QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }$arg + ;; + esac + done +} + +compile_object() { + local_cflags="$1" + do_cc $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC +} + +compile_prog() { + local_cflags="$1" + local_ldflags="$2" + do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags +} + +do_libtool() { + local mode=$1 + shift + # Run the compiler, capturing its output to the log. + echo $libtool $mode --tag=CC $cc "$@" >> config.log + $libtool $mode --tag=CC $cc "$@" >> config.log 2>&1 || return $? + # Test passed. If this is an --enable-werror build, rerun + # the test with -Werror and bail out if it fails. This + # makes warning-generating-errors in configure test code + # obvious to developers. + if test "$werror" != "yes"; then + return 0 + fi + # Don't bother rerunning the compile if we were already using -Werror + case "$*" in + *-Werror*) + return 0 + ;; + esac + echo $libtool $mode --tag=CC $cc -Werror "$@" >> config.log + $libtool $mode --tag=CC $cc -Werror "$@" >> config.log 2>&1 && return $? + error_exit "configure test passed without -Werror but failed with -Werror." \ + "This is probably a bug in the configure script. The failing command" \ + "will be at the bottom of config.log." \ + "You can run configure with --disable-werror to bypass this check." +} + +libtool_prog() { + do_libtool --mode=compile $QEMU_CFLAGS -c -fPIE -DPIE -o $TMPO $TMPC || return $? + do_libtool --mode=link $LDFLAGS -o $TMPA $TMPL -rpath /usr/local/lib +} + +# symbolically link $1 to $2. Portable version of "ln -sf". +symlink() { + rm -rf "$2" + mkdir -p "$(dirname "$2")" + ln -s "$1" "$2" +} + +# check whether a command is available to this shell (may be either an +# executable or a builtin) +has() { + type "$1" >/dev/null 2>&1 +} + +# search for an executable in PATH +path_of() { + local_command="$1" + local_ifs="$IFS" + local_dir="" + + # pathname has a dir component? + if [ "${local_command#*/}" != "$local_command" ]; then + if [ -x "$local_command" ] && [ ! -d "$local_command" ]; then + echo "$local_command" + return 0 + fi + fi + if [ -z "$local_command" ]; then + return 1 + fi + + IFS=: + for local_dir in $PATH; do + if [ -x "$local_dir/$local_command" ] && [ ! -d "$local_dir/$local_command" ]; then + echo "$local_dir/$local_command" + IFS="${local_ifs:-$(printf ' \t\n')}" + return 0 + fi + done + # not found + IFS="${local_ifs:-$(printf ' \t\n')}" + return 1 +} + +have_backend () { + echo "$trace_backends" | grep "$1" >/dev/null +} + +# default parameters +source_path=`dirname "$0"` +cpu="" +iasl="iasl" +interp_prefix="/usr/gnemul/qemu-%M" +static="no" +cross_prefix="" +audio_drv_list="" +block_drv_rw_whitelist="" +block_drv_ro_whitelist="" +host_cc="cc" +libs_softmmu="" +libs_tools="" +audio_pt_int="" +audio_win_int="" +cc_i386=i386-pc-linux-gnu-gcc +libs_qga="" +debug_info="yes" +stack_protector="" + +# Don't accept a target_list environment variable. +unset target_list + +# Default value for a variable defining feature "foo". +# * foo="no" feature will only be used if --enable-foo arg is given +# * foo="" feature will be searched for, and if found, will be used +# unless --disable-foo is given +# * foo="yes" this value will only be set by --enable-foo flag. +# feature will searched for, +# if not found, configure exits with error +# +# Always add --enable-foo and --disable-foo command line args. +# Distributions want to ensure that several features are compiled in, and it +# is impossible without a --enable-foo that exits if a feature is not found. + +bluez="" +brlapi="" +curl="" +curses="" +docs="" +fdt="" +netmap="no" +pixman="" +sdl="" +sdlabi="1.2" +virtfs="" +vnc="yes" +sparse="no" +uuid="" +vde="" +vnc_tls="" +vnc_sasl="" +vnc_jpeg="" +vnc_png="" +vnc_ws="" +xen="" +xen_ctrl_version="" +xen_pci_passthrough="" +linux_aio="" +cap_ng="" +attr="" +libattr="" +xfs="" + +vhost_net="no" +vhost_scsi="no" +kvm="no" +rdma="" +gprof="no" +debug_tcg="no" +debug="no" +strip_opt="yes" +tcg_interpreter="no" +bigendian="no" +mingw32="no" +gcov="no" +gcov_tool="gcov" +EXESUF="" +DSOSUF=".so" +LDFLAGS_SHARED="-shared" +modules="no" +prefix="/usr/local" +mandir="\${prefix}/share/man" +datadir="\${prefix}/share" +qemu_docdir="\${prefix}/share/doc/qemu" +bindir="\${prefix}/bin" +libdir="\${prefix}/lib" +libexecdir="\${prefix}/libexec" +includedir="\${prefix}/include" +sysconfdir="\${prefix}/etc" +local_statedir="\${prefix}/var" +confsuffix="/qemu" +slirp="yes" +fmod_lib="" +fmod_inc="" +oss_lib="" +bsd="no" +linux="no" +solaris="no" +profiler="no" +cocoa="no" +softmmu="yes" +linux_user="no" +bsd_user="no" +guest_base="yes" +aix="no" +blobs="yes" +pkgversion="" +pie="" +zero_malloc="" +qom_cast_debug="yes" +trace_backends="nop" +trace_file="trace" +spice="" +rbd="" +smartcard_nss="" +libusb="" +usb_redir="" +opengl="" +zlib="yes" +lzo="" +snappy="" +bzip2="" +guest_agent="" +guest_agent_with_vss="no" +vss_win32_sdk="" +win_sdk="no" +want_tools="yes" +libiscsi="" +libnfs="" +coroutine="" +coroutine_pool="" +seccomp="" +glusterfs="" +glusterfs_discard="no" +glusterfs_zerofill="no" +archipelago="no" +gtk="" +gtkabi="" +vte="" +tpm="yes" +libssh2="" +vhdx="" +quorum="" +numa="" +tcmalloc="no" + +# parse CC options first +for opt do + optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'` + case "$opt" in + --cross-prefix=*) cross_prefix="$optarg" + ;; + --cc=*) CC="$optarg" + ;; + --cxx=*) CXX="$optarg" + ;; + --source-path=*) source_path="$optarg" + ;; + --cpu=*) cpu="$optarg" + ;; + --extra-cflags=*) QEMU_CFLAGS="$optarg $QEMU_CFLAGS" + EXTRA_CFLAGS="$optarg" + ;; + --extra-ldflags=*) LDFLAGS="$optarg $LDFLAGS" + EXTRA_LDFLAGS="$optarg" + ;; + --enable-debug-info) debug_info="yes" + ;; + --disable-debug-info) debug_info="no" + ;; + esac +done +# OS specific +# Using uname is really, really broken. Once we have the right set of checks +# we can eliminate its usage altogether. + +# Preferred compiler: +# ${CC} (if set) +# ${cross_prefix}gcc (if cross-prefix specified) +# system compiler +if test -z "${CC}${cross_prefix}"; then + cc="$host_cc" +else + cc="${CC-${cross_prefix}gcc}" +fi + +if test -z "${CXX}${cross_prefix}"; then + cxx="c++" +else + cxx="${CXX-${cross_prefix}g++}" +fi + +ar="${AR-${cross_prefix}ar}" +as="${AS-${cross_prefix}as}" +cpp="${CPP-$cc -E}" +objcopy="${OBJCOPY-${cross_prefix}objcopy}" +ld="${LD-${cross_prefix}ld}" +libtool="${LIBTOOL-${cross_prefix}libtool}" +nm="${NM-${cross_prefix}nm}" +strip="${STRIP-${cross_prefix}strip}" +windres="${WINDRES-${cross_prefix}windres}" +pkg_config_exe="${PKG_CONFIG-${cross_prefix}pkg-config}" +query_pkg_config() { + "${pkg_config_exe}" ${QEMU_PKG_CONFIG_FLAGS} "$@" +} +pkg_config=query_pkg_config +sdl_config="${SDL_CONFIG-${cross_prefix}sdl-config}" +sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" + +# If the user hasn't specified ARFLAGS, default to 'rv', just as make does. +ARFLAGS="${ARFLAGS-rv}" + +# default flags for all hosts +QEMU_CFLAGS="-fno-strict-aliasing -fno-common $QEMU_CFLAGS" +QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" +QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" +QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" +QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/include" +if test "$debug_info" = "yes"; then + CFLAGS="-g $CFLAGS" + LDFLAGS="-g $LDFLAGS" +fi + +# make source path absolute +source_path=`cd "$source_path"; pwd` + +# running configure in the source tree? +# we know that's the case if configure is there. +if test -f "./configure"; then + pwd_is_source_path="y" +else + pwd_is_source_path="n" +fi + +check_define() { +cat > $TMPC < $TMPC <&2 + ;; + --disable-gtk) gtk="no" + ;; + --enable-gtk) gtk="yes" + ;; + --enable-rdma) rdma="yes" + ;; + --disable-rdma) rdma="no" + ;; + --with-gtkabi=*) gtkabi="$optarg" + ;; + --disable-vte) vte="no" + ;; + --enable-vte) vte="yes" + ;; + --disable-tpm) tpm="no" + ;; + --enable-tpm) tpm="yes" + ;; + --disable-libssh2) libssh2="no" + ;; + --enable-libssh2) libssh2="yes" + ;; + --enable-vhdx) vhdx="yes" + ;; + --disable-vhdx) vhdx="no" + ;; + --disable-quorum) quorum="no" + ;; + --enable-quorum) quorum="yes" + ;; + --disable-numa) numa="no" + ;; + --enable-numa) numa="yes" + ;; + --disable-tcmalloc) tcmalloc="no" + ;; + --enable-tcmalloc) tcmalloc="yes" + ;; + *) + echo "ERROR: unknown option $opt" + echo "Try '$0 --help' for more information" + exit 1 + ;; + esac +done + +if ! has $python; then + error_exit "Python not found. Use --python=/path/to/python" +fi + +# Note that if the Python conditional here evaluates True we will exit +# with status 1 which is a shell 'false' value. +if ! $python -c 'import sys; sys.exit(sys.version_info < (2,4) or sys.version_info >= (3,))'; then + error_exit "Cannot use '$python', Python 2.4 or later is required." \ + "Note that Python 3 or later is not yet supported." \ + "Use --python=/path/to/python to specify a supported Python." +fi + +# The -B switch was added in Python 2.6. +# If it is supplied, compiled files are not written. +# Use it for Python versions which support it. +if $python -B -c 'import sys; sys.exit(0)' 2>/dev/null; then + python="$python -B" +fi + +case "$cpu" in + ppc) + CPU_CFLAGS="-m32" + LDFLAGS="-m32 $LDFLAGS" + ;; + ppc64) + CPU_CFLAGS="-m64" + LDFLAGS="-m64 $LDFLAGS" + ;; + sparc) + LDFLAGS="-m32 $LDFLAGS" + CPU_CFLAGS="-m32 -mcpu=ultrasparc" + ;; + sparc64) + LDFLAGS="-m64 $LDFLAGS" + CPU_CFLAGS="-m64 -mcpu=ultrasparc" + ;; + s390) + CPU_CFLAGS="-m31" + LDFLAGS="-m31 $LDFLAGS" + ;; + s390x) + CPU_CFLAGS="-m64" + LDFLAGS="-m64 $LDFLAGS" + ;; + i386) + CPU_CFLAGS="-m32" + LDFLAGS="-m32 $LDFLAGS" + cc_i386='$(CC) -m32' + ;; + x86_64) + CPU_CFLAGS="-m64" + LDFLAGS="-m64 $LDFLAGS" + cc_i386='$(CC) -m32' + ;; + x32) + CPU_CFLAGS="-mx32" + LDFLAGS="-mx32 $LDFLAGS" + cc_i386='$(CC) -m32' + ;; + # No special flags required for other host CPUs +esac + +QEMU_CFLAGS="$CPU_CFLAGS $QEMU_CFLAGS" +EXTRA_CFLAGS="$CPU_CFLAGS $EXTRA_CFLAGS" + +default_target_list="" + +mak_wilds="" + +if [ "$softmmu" = "yes" ]; then + mak_wilds="${mak_wilds} $source_path/default-configs/*-softmmu.mak" +fi +if [ "$linux_user" = "yes" ]; then + mak_wilds="${mak_wilds} $source_path/default-configs/*-linux-user.mak" +fi +if [ "$bsd_user" = "yes" ]; then + mak_wilds="${mak_wilds} $source_path/default-configs/*-bsd-user.mak" +fi + +for config in $mak_wilds; do + default_target_list="${default_target_list} $(basename "$config" .mak)" +done + +if test x"$show_help" = x"yes" ; then +cat << EOF + +Usage: configure [options] +Options: [defaults in brackets after descriptions] + +Standard options: + --help print this message + --prefix=PREFIX install in PREFIX [$prefix] + --interp-prefix=PREFIX where to find shared libraries, etc. + use %M for cpu name [$interp_prefix] + --target-list=LIST set target list (default: build everything) +$(echo Available targets: $default_target_list | \ + fold -s -w 53 | sed -e 's/^/ /') + +Advanced options (experts only): + --source-path=PATH path of source code [$source_path] + --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix] + --cc=CC use C compiler CC [$cc] + --iasl=IASL use ACPI compiler IASL [$iasl] + --host-cc=CC use C compiler CC [$host_cc] for code run at + build time + --cxx=CXX use C++ compiler CXX [$cxx] + --objcc=OBJCC use Objective-C compiler OBJCC [$objcc] + --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS + --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS + --make=MAKE use specified make [$make] + --install=INSTALL use specified install [$install] + --python=PYTHON use specified python [$python] + --smbd=SMBD use specified smbd [$smbd] + --static enable static build [$static] + --mandir=PATH install man pages in PATH + --datadir=PATH install firmware in PATH$confsuffix + --docdir=PATH install documentation in PATH$confsuffix + --bindir=PATH install binaries in PATH + --libdir=PATH install libraries in PATH + --sysconfdir=PATH install config in PATH$confsuffix + --localstatedir=PATH install local state in PATH (set at runtime on win32) + --with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [$confsuffix] + --enable-modules enable modules support + --enable-debug-tcg enable TCG debugging + --disable-debug-tcg disable TCG debugging (default) + --enable-debug-info enable debugging information (default) + --disable-debug-info disable debugging information + --enable-debug enable common debug build options + --enable-sparse enable sparse checker + --disable-sparse disable sparse checker (default) + --disable-strip disable stripping binaries + --disable-werror disable compilation abort on warning + --disable-stack-protector disable compiler-provided stack protection + --disable-sdl disable SDL + --enable-sdl enable SDL + --with-sdlabi select preferred SDL ABI 1.2 or 2.0 + --disable-gtk disable gtk UI + --enable-gtk enable gtk UI + --with-gtkabi select preferred GTK ABI 2.0 or 3.0 + --disable-virtfs disable VirtFS + --enable-virtfs enable VirtFS + --disable-vnc disable VNC + --enable-vnc enable VNC + --disable-cocoa disable Cocoa (Mac OS X only) + --enable-cocoa enable Cocoa (default on Mac OS X) + --audio-drv-list=LIST set audio drivers list: + Available drivers: $audio_possible_drivers + --block-drv-whitelist=L Same as --block-drv-rw-whitelist=L + --block-drv-rw-whitelist=L + set block driver read-write whitelist + (affects only QEMU, not qemu-img) + --block-drv-ro-whitelist=L + set block driver read-only whitelist + (affects only QEMU, not qemu-img) + --disable-xen disable xen backend driver support + --enable-xen enable xen backend driver support + --disable-xen-pci-passthrough + --enable-xen-pci-passthrough + --disable-brlapi disable BrlAPI + --enable-brlapi enable BrlAPI + --disable-vnc-tls disable TLS encryption for VNC server + --enable-vnc-tls enable TLS encryption for VNC server + --disable-vnc-sasl disable SASL encryption for VNC server + --enable-vnc-sasl enable SASL encryption for VNC server + --disable-vnc-jpeg disable JPEG lossy compression for VNC server + --enable-vnc-jpeg enable JPEG lossy compression for VNC server + --disable-vnc-png disable PNG compression for VNC server (default) + --enable-vnc-png enable PNG compression for VNC server + --disable-vnc-ws disable Websockets support for VNC server + --enable-vnc-ws enable Websockets support for VNC server + --disable-curses disable curses output + --enable-curses enable curses output + --disable-curl disable curl connectivity + --enable-curl enable curl connectivity + --disable-fdt disable fdt device tree + --enable-fdt enable fdt device tree + --disable-bluez disable bluez stack connectivity + --enable-bluez enable bluez stack connectivity + --disable-slirp disable SLIRP userspace network connectivity + --disable-kvm disable KVM acceleration support + --enable-kvm enable KVM acceleration support + --disable-rdma disable RDMA-based migration support + --enable-rdma enable RDMA-based migration support + --enable-tcg-interpreter enable TCG with bytecode interpreter (TCI) + --enable-system enable all system emulation targets + --disable-system disable all system emulation targets + --enable-user enable supported user emulation targets + --disable-user disable all user emulation targets + --enable-linux-user enable all linux usermode emulation targets + --disable-linux-user disable all linux usermode emulation targets + --enable-bsd-user enable all BSD usermode emulation targets + --disable-bsd-user disable all BSD usermode emulation targets + --enable-guest-base enable GUEST_BASE support for usermode + emulation targets + --disable-guest-base disable GUEST_BASE support + --enable-pie build Position Independent Executables + --disable-pie do not build Position Independent Executables + --fmod-lib path to FMOD library + --fmod-inc path to FMOD includes + --oss-lib path to OSS library + --cpu=CPU Build for host CPU [$cpu] + --disable-uuid disable uuid support + --enable-uuid enable uuid support + --disable-vde disable support for vde network + --enable-vde enable support for vde network + --disable-netmap disable support for netmap network + --enable-netmap enable support for netmap network + --disable-linux-aio disable Linux AIO support + --enable-linux-aio enable Linux AIO support + --disable-cap-ng disable libcap-ng support + --enable-cap-ng enable libcap-ng support + --disable-attr disable attr and xattr support + --enable-attr enable attr and xattr support + --disable-blobs disable installing provided firmware blobs + --enable-docs enable documentation build + --disable-docs disable documentation build + --disable-vhost-net disable vhost-net acceleration support + --enable-vhost-net enable vhost-net acceleration support + --enable-trace-backends=B Set trace backend + Available backends: $($python $source_path/scripts/tracetool.py --list-backends) + --with-trace-file=NAME Full PATH,NAME of file to store traces + Default:trace- + --disable-spice disable spice + --enable-spice enable spice + --enable-rbd enable building the rados block device (rbd) + --disable-libiscsi disable iscsi support + --enable-libiscsi enable iscsi support + --disable-libnfs disable nfs support + --enable-libnfs enable nfs support + --disable-smartcard-nss disable smartcard nss support + --enable-smartcard-nss enable smartcard nss support + --disable-libusb disable libusb (for usb passthrough) + --enable-libusb enable libusb (for usb passthrough) + --disable-usb-redir disable usb network redirection support + --enable-usb-redir enable usb network redirection support + --enable-lzo enable the support of lzo compression library + --enable-snappy enable the support of snappy compression library + --enable-bzip2 enable the support of bzip2 compression library (for + reading bzip2-compressed dmg images) + --disable-guest-agent disable building of the QEMU Guest Agent + --enable-guest-agent enable building of the QEMU Guest Agent + --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent + --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb) + --disable-seccomp disable seccomp support + --enable-seccomp enable seccomp support + --with-coroutine=BACKEND coroutine backend. Supported options: + gthread, ucontext, sigaltstack, windows + --disable-coroutine-pool disable coroutine freelist (worse performance) + --enable-coroutine-pool enable coroutine freelist (better performance) + --enable-glusterfs enable GlusterFS backend + --disable-glusterfs disable GlusterFS backend + --enable-archipelago enable Archipelago backend + --disable-archipelago disable Archipelago backend + --enable-gcov enable test coverage analysis with gcov + --gcov=GCOV use specified gcov [$gcov_tool] + --disable-tpm disable TPM support + --enable-tpm enable TPM support + --disable-libssh2 disable ssh block device support + --enable-libssh2 enable ssh block device support + --disable-vhdx disable support for the Microsoft VHDX image format + --enable-vhdx enable support for the Microsoft VHDX image format + --disable-quorum disable quorum block filter support + --enable-quorum enable quorum block filter support + --disable-numa disable libnuma support + --enable-numa enable libnuma support + --disable-tcmalloc disable tcmalloc support + --enable-tcmalloc enable tcmalloc support + +NOTE: The object files are built at the place where configure is launched +EOF +exit 0 +fi + +# Now we have handled --enable-tcg-interpreter and know we're not just +# printing the help message, bail out if the host CPU isn't supported. +if test "$ARCH" = "unknown"; then + if test "$tcg_interpreter" = "yes" ; then + echo "Unsupported CPU = $cpu, will use TCG with TCI (experimental)" + ARCH=tci + else + error_exit "Unsupported CPU = $cpu, try --enable-tcg-interpreter" + fi +fi + +# Consult white-list to determine whether to enable werror +# by default. Only enable by default for git builds +z_version=`cut -f3 -d. $source_path/VERSION` + +if test -z "$werror" ; then + if test -d "$source_path/.git" -a \ + "$linux" = "yes" ; then + werror="yes" + else + werror="no" + fi +fi + +# check that the C compiler works. +write_c_skeleton; +if compile_object ; then + : C compiler works ok +else + error_exit "\"$cc\" either does not exist or does not work" +fi + +# Check that the C++ compiler exists and works with the C compiler +if has $cxx; then + cat > $TMPC < $TMPCXX <.o compile and some only at link time + if do_cc $QEMU_CFLAGS -Werror $flag -c -o $TMPO $TMPC && + compile_prog "-Werror $flag" ""; then + QEMU_CFLAGS="$QEMU_CFLAGS $flag" + LIBTOOLFLAGS="$LIBTOOLFLAGS -Wc,$flag" + sp_on=1 + break + fi + done + if test "$stack_protector" = yes; then + if test $sp_on = 0; then + error_exit "Stack protector not supported" + fi + fi +fi + +# Workaround for http://gcc.gnu.org/PR55489. Happens with -fPIE/-fPIC and +# large functions that use global variables. The bug is in all releases of +# GCC, but it became particularly acute in 4.6.x and 4.7.x. It is fixed in +# 4.7.3 and 4.8.0. We should be able to delete this at the end of 2013. +cat > $TMPC << EOF +#if __GNUC__ == 4 && (__GNUC_MINOR__ == 6 || (__GNUC_MINOR__ == 7 && __GNUC_PATCHLEVEL__ <= 2)) +int main(void) { return 0; } +#else +#error No bug in this compiler. +#endif +EOF +if compile_prog "-Werror -fno-gcse" "" ; then + TRANSLATE_OPT_CFLAGS=-fno-gcse +fi + +if test "$static" = "yes" ; then + if test "$modules" = "yes" ; then + error_exit "static and modules are mutually incompatible" + fi + if test "$pie" = "yes" ; then + error_exit "static and pie are mutually incompatible" + else + pie="no" + fi +fi + +# Unconditional check for compiler __thread support + cat > $TMPC << EOF +static __thread int tls_var; +int main(void) { return tls_var; } +EOF + +if ! compile_prog "-Werror" "" ; then + error_exit "Your compiler does not support the __thread specifier for " \ + "Thread-Local Storage (TLS). Please upgrade to a version that does." +fi + +if test "$pie" = ""; then + case "$cpu-$targetos" in + i386-Linux|x86_64-Linux|x32-Linux|i386-OpenBSD|x86_64-OpenBSD) + ;; + *) + pie="no" + ;; + esac +fi + +if test "$pie" != "no" ; then + cat > $TMPC << EOF + +#ifdef __linux__ +# define THREAD __thread +#else +# define THREAD +#endif + +static THREAD int tls_var; + +int main(void) { return tls_var; } + +EOF + if compile_prog "-fPIE -DPIE" "-pie"; then + QEMU_CFLAGS="-fPIE -DPIE $QEMU_CFLAGS" + LDFLAGS="-pie $LDFLAGS" + pie="yes" + if compile_prog "" "-Wl,-z,relro -Wl,-z,now" ; then + LDFLAGS="-Wl,-z,relro -Wl,-z,now $LDFLAGS" + fi + else + if test "$pie" = "yes"; then + error_exit "PIE not available due to missing toolchain support" + else + echo "Disabling PIE due to missing toolchain support" + pie="no" + fi + fi + + if compile_prog "-Werror -fno-pie" "-nopie"; then + CFLAGS_NOPIE="-fno-pie" + LDFLAGS_NOPIE="-nopie" + fi +fi + +# check for broken gcc and libtool in RHEL5 +if test -n "$libtool" -a "$pie" != "no" ; then + cat > $TMPC < $TMPC << EOF +static int sfaa(int *ptr) +{ + return __sync_fetch_and_and(ptr, 0); +} + +int main(void) +{ + int val = 42; + val = __sync_val_compare_and_swap(&val, 0, 1); + sfaa(&val); + return val; +} +EOF + if ! compile_prog "" "" ; then + QEMU_CFLAGS="-march=i486 $QEMU_CFLAGS" + fi +fi + +######################################### +# Solaris specific configure tool chain decisions + +if test "$solaris" = "yes" ; then + if has $install; then + : + else + error_exit "Solaris install program not found. Use --install=/usr/ucb/install or" \ + "install fileutils from www.blastwave.org using pkg-get -i fileutils" \ + "to get ginstall which is used by default (which lives in /opt/csw/bin)" + fi + if test "`path_of $install`" = "/usr/sbin/install" ; then + error_exit "Solaris /usr/sbin/install is not an appropriate install program." \ + "try ginstall from the GNU fileutils available from www.blastwave.org" \ + "using pkg-get -i fileutils, or use --install=/usr/ucb/install" + fi + if has ar; then + : + else + if test -f /usr/ccs/bin/ar ; then + error_exit "No path includes ar" \ + "Add /usr/ccs/bin to your path and rerun configure" + fi + error_exit "No path includes ar" + fi +fi + +if test -z "${target_list+xxx}" ; then + target_list="$default_target_list" +else + target_list=`echo "$target_list" | sed -e 's/,/ /g'` +fi + +# Check that we recognised the target name; this allows a more +# friendly error message than if we let it fall through. +for target in $target_list; do + case " $default_target_list " in + *" $target "*) + ;; + *) + error_exit "Unknown target name '$target'" + ;; + esac +done + +# see if system emulation was really requested +case " $target_list " in + *"-softmmu "*) softmmu=yes + ;; + *) softmmu=no + ;; +esac + +feature_not_found() { + feature=$1 + remedy=$2 + + error_exit "User requested feature $feature" \ + "configure was not able to find it." \ + "$remedy" +} + +# --- +# big/little endian test +cat > $TMPC << EOF +short big_endian[] = { 0x4269, 0x4765, 0x4e64, 0x4961, 0x4e00, 0, }; +short little_endian[] = { 0x694c, 0x7454, 0x654c, 0x6e45, 0x6944, 0x6e41, 0, }; +extern int foo(short *, short *); +int main(int argc, char *argv[]) { + return foo(big_endian, little_endian); +} +EOF + +if compile_object ; then + if grep -q BiGeNdIaN $TMPO ; then + bigendian="yes" + elif grep -q LiTtLeEnDiAn $TMPO ; then + bigendian="no" + else + echo big/little test failed + fi +else + echo big/little test failed +fi + +########################################## +# L2TPV3 probe + +cat > $TMPC < +#include +int main(void) { return sizeof(struct mmsghdr); } +EOF +if compile_prog "" "" ; then + l2tpv3=yes +else + l2tpv3=no +fi + +########################################## +# pkg-config probe + +if ! has "$pkg_config_exe"; then + error_exit "pkg-config binary '$pkg_config_exe' not found" +fi + +########################################## +# NPTL probe + +if test "$linux_user" = "yes"; then + cat > $TMPC < +#include +int main(void) { +#if !defined(CLONE_SETTLS) || !defined(FUTEX_WAIT) +#error bork +#endif + return 0; +} +EOF + if ! compile_object ; then + feature_not_found "nptl" "Install glibc and linux kernel headers." + fi +fi + +########################################## +# zlib check + +if test "$zlib" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { zlibVersion(); return 0; } +EOF + if compile_prog "" "-lz" ; then + : + else + error_exit "zlib check failed" \ + "Make sure to have the zlib libs and headers installed." + fi +fi +LIBS="$LIBS -lz" + +########################################## +# lzo check + +if test "$lzo" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { lzo_version(); return 0; } +EOF + if compile_prog "" "-llzo2" ; then + libs_softmmu="$libs_softmmu -llzo2" + lzo="yes" + else + if test "$lzo" = "yes"; then + feature_not_found "liblzo2" "Install liblzo2 devel" + fi + lzo="no" + fi +fi + +########################################## +# snappy check + +if test "$snappy" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { snappy_max_compressed_length(4096); return 0; } +EOF + if compile_prog "" "-lsnappy" ; then + libs_softmmu="$libs_softmmu -lsnappy" + snappy="yes" + else + if test "$snappy" = "yes"; then + feature_not_found "libsnappy" "Install libsnappy devel" + fi + snappy="no" + fi +fi + +########################################## +# bzip2 check + +if test "$bzip2" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { BZ2_bzlibVersion(); return 0; } +EOF + if compile_prog "" "-lbz2" ; then + bzip2="yes" + else + if test "$bzip2" = "yes"; then + feature_not_found "libbzip2" "Install libbzip2 devel" + fi + bzip2="no" + fi +fi + +########################################## +# libseccomp check + +if test "$seccomp" != "no" ; then + if test "$cpu" = "i386" || test "$cpu" = "x86_64" && + $pkg_config --atleast-version=2.1.1 libseccomp; then + libs_softmmu="$libs_softmmu `$pkg_config --libs libseccomp`" + QEMU_CFLAGS="$QEMU_CFLAGS `$pkg_config --cflags libseccomp`" + seccomp="yes" + else + if test "$seccomp" = "yes"; then + feature_not_found "libseccomp" "Install libseccomp devel >= 2.1.1" + fi + seccomp="no" + fi +fi +########################################## +# xen probe + +if test "$xen" != "no" ; then + xen_libs="-lxenstore -lxenctrl -lxenguest" + + # First we test whether Xen headers and libraries are available. + # If no, we are done and there is no Xen support. + # If yes, more tests are run to detect the Xen version. + + # Xen (any) + cat > $TMPC < +int main(void) { + return 0; +} +EOF + if ! compile_prog "" "$xen_libs" ; then + # Xen not found + if test "$xen" = "yes" ; then + feature_not_found "xen" "Install xen devel" + fi + xen=no + + # Xen unstable + elif + cat > $TMPC < +#include +#include +#include +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + xc_interface *xc; + xs_daemon_open(); + xc = xc_interface_open(0, 0, 0); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_gnttab_open(NULL, 0); + xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); + xc_hvm_create_ioreq_server(xc, 0, 0, NULL); + return 0; +} +EOF + compile_prog "" "$xen_libs" + then + xen_ctrl_version=450 + xen=yes + + elif + cat > $TMPC < +#include +#include +#include +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + xc_interface *xc; + xs_daemon_open(); + xc = xc_interface_open(0, 0, 0); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_gnttab_open(NULL, 0); + xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); + return 0; +} +EOF + compile_prog "" "$xen_libs" + then + xen_ctrl_version=420 + xen=yes + + elif + cat > $TMPC < +#include +#include +#include +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + xs_daemon_open(); + xc_interface_open(0, 0, 0); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_gnttab_open(NULL, 0); + xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + return 0; +} +EOF + compile_prog "" "$xen_libs" + then + xen_ctrl_version=410 + xen=yes + + # Xen 4.0.0 + elif + cat > $TMPC < +#include +#include +#include +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + struct xen_add_to_physmap xatp = { + .domid = 0, .space = XENMAPSPACE_gmfn, .idx = 0, .gpfn = 0, + }; + xs_daemon_open(); + xc_interface_open(); + xc_gnttab_open(); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_memory_op(0, XENMEM_add_to_physmap, &xatp); + return 0; +} +EOF + compile_prog "" "$xen_libs" + then + xen_ctrl_version=400 + xen=yes + + # Xen 3.4.0 + elif + cat > $TMPC < +#include +int main(void) { + struct xen_add_to_physmap xatp = { + .domid = 0, .space = XENMAPSPACE_gmfn, .idx = 0, .gpfn = 0, + }; + xs_daemon_open(); + xc_interface_open(); + xc_gnttab_open(); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_memory_op(0, XENMEM_add_to_physmap, &xatp); + return 0; +} +EOF + compile_prog "" "$xen_libs" + then + xen_ctrl_version=340 + xen=yes + + # Xen 3.3.0 + elif + cat > $TMPC < +#include +int main(void) { + xs_daemon_open(); + xc_interface_open(); + xc_gnttab_open(); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + return 0; +} +EOF + compile_prog "" "$xen_libs" + then + xen_ctrl_version=330 + xen=yes + + # Xen version unsupported + else + if test "$xen" = "yes" ; then + feature_not_found "xen (unsupported version)" "Install supported xen (e.g. 4.0, 3.4, 3.3)" + fi + xen=no + fi + + if test "$xen" = yes; then + libs_softmmu="$xen_libs $libs_softmmu" + fi +fi + +if test "$xen_pci_passthrough" != "no"; then + if test "$xen" = "yes" && test "$linux" = "yes" && + test "$xen_ctrl_version" -ge 340; then + xen_pci_passthrough=yes + else + if test "$xen_pci_passthrough" = "yes"; then + if test "$xen_ctrl_version" -lt 340; then + error_exit "User requested feature Xen PCI Passthrough" \ + "This feature does not work with Xen 3.3" + fi + error_exit "User requested feature Xen PCI Passthrough" \ + " but this feature requires /sys from Linux" + fi + xen_pci_passthrough=no + fi +fi + +########################################## +# libtool probe + +if ! has $libtool; then + libtool= +fi + +# MacOSX ships with a libtool which isn't the GNU one; weed this +# out by checking whether libtool supports the --version switch +if test -n "$libtool"; then + if ! "$libtool" --version >/dev/null 2>&1; then + libtool= + fi +fi + +########################################## +# Sparse probe +if test "$sparse" != "no" ; then + if has cgcc; then + sparse=yes + else + if test "$sparse" = "yes" ; then + feature_not_found "sparse" "Install sparse binary" + fi + sparse=no + fi +fi + +########################################## +# X11 probe +x11_cflags= +x11_libs=-lX11 +if $pkg_config --exists "x11"; then + x11_cflags=`$pkg_config --cflags x11` + x11_libs=`$pkg_config --libs x11` +fi + +########################################## +# GTK probe + +if test "$gtkabi" = ""; then + # The GTK ABI was not specified explicitly, so try whether 2.0 is available. + # Use 3.0 as a fallback if that is available. + if $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then + gtkabi=2.0 + elif $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then + gtkabi=3.0 + else + gtkabi=2.0 + fi +fi + +if test "$gtk" != "no"; then + gtkpackage="gtk+-$gtkabi" + gtkx11package="gtk+-x11-$gtkabi" + if test "$gtkabi" = "3.0" ; then + gtkversion="3.0.0" + else + gtkversion="2.18.0" + fi + if $pkg_config --exists "$gtkpackage >= $gtkversion"; then + gtk_cflags=`$pkg_config --cflags $gtkpackage` + gtk_libs=`$pkg_config --libs $gtkpackage` + if $pkg_config --exists "$gtkx11package >= $gtkversion"; then + gtk_cflags="$gtk_cflags $x11_cflags" + gtk_libs="$gtk_libs $x11_libs" + fi + libs_softmmu="$gtk_libs $libs_softmmu" + gtk="yes" + elif test "$gtk" = "yes"; then + feature_not_found "gtk" "Install gtk2 or gtk3 devel" + else + gtk="no" + fi +fi + +########################################## +# VTE probe + +if test "$vte" != "no"; then + if test "$gtkabi" = "3.0"; then + vtepackage="vte-2.90" + vteversion="0.32.0" + else + vtepackage="vte" + vteversion="0.24.0" + fi + if $pkg_config --exists "$vtepackage >= $vteversion"; then + vte_cflags=`$pkg_config --cflags $vtepackage` + vte_libs=`$pkg_config --libs $vtepackage` + libs_softmmu="$vte_libs $libs_softmmu" + vte="yes" + elif test "$vte" = "yes"; then + if test "$gtkabi" = "3.0"; then + feature_not_found "vte" "Install libvte-2.90 devel" + else + feature_not_found "vte" "Install libvte devel" + fi + else + vte="no" + fi +fi + +########################################## +# SDL probe + +# Look for sdl configuration program (pkg-config or sdl-config). Try +# sdl-config even without cross prefix, and favour pkg-config over sdl-config. + +if test $sdlabi = "2.0"; then + sdl_config=$sdl2_config + sdlname=sdl2 + sdlconfigname=sdl2_config +else + sdlname=sdl + sdlconfigname=sdl_config +fi + +if test "`basename $sdl_config`" != $sdlconfigname && ! has ${sdl_config}; then + sdl_config=$sdlconfigname +fi + +if $pkg_config $sdlname --exists; then + sdlconfig="$pkg_config $sdlname" + _sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'` +elif has ${sdl_config}; then + sdlconfig="$sdl_config" + _sdlversion=`$sdlconfig --version | sed 's/[^0-9]//g'` +else + if test "$sdl" = "yes" ; then + feature_not_found "sdl" "Install SDL devel" + fi + sdl=no +fi +if test -n "$cross_prefix" && test "$(basename "$sdlconfig")" = sdl-config; then + echo warning: using "\"$sdlconfig\"" to detect cross-compiled sdl >&2 +fi + +sdl_too_old=no +if test "$sdl" != "no" ; then + cat > $TMPC << EOF +#include +#undef main /* We don't want SDL to override our main() */ +int main( void ) { return SDL_Init (SDL_INIT_VIDEO); } +EOF + sdl_cflags=`$sdlconfig --cflags 2> /dev/null` + if test "$static" = "yes" ; then + sdl_libs=`$sdlconfig --static-libs 2>/dev/null` + else + sdl_libs=`$sdlconfig --libs 2> /dev/null` + fi + if compile_prog "$sdl_cflags" "$sdl_libs" ; then + if test "$_sdlversion" -lt 121 ; then + sdl_too_old=yes + else + if test "$cocoa" = "no" ; then + sdl=yes + fi + fi + + # static link with sdl ? (note: sdl.pc's --static --libs is broken) + if test "$sdl" = "yes" -a "$static" = "yes" ; then + if test $? = 0 && echo $sdl_libs | grep -- -laa > /dev/null; then + sdl_libs="$sdl_libs `aalib-config --static-libs 2>/dev/null`" + sdl_cflags="$sdl_cflags `aalib-config --cflags 2>/dev/null`" + fi + if compile_prog "$sdl_cflags" "$sdl_libs" ; then + : + else + sdl=no + fi + fi # static link + else # sdl not found + if test "$sdl" = "yes" ; then + feature_not_found "sdl" "Install SDL devel" + fi + sdl=no + fi # sdl compile test +fi + +if test "$sdl" = "yes" ; then + cat > $TMPC < +#if defined(SDL_VIDEO_DRIVER_X11) +#include +#else +#error No x11 support +#endif +int main(void) { return 0; } +EOF + if compile_prog "$sdl_cflags $x11_cflags" "$sdl_libs $x11_libs" ; then + sdl_cflags="$sdl_cflags $x11_cflags" + sdl_libs="$sdl_libs $x11_libs" + fi + libs_softmmu="$sdl_libs $libs_softmmu" +fi + +########################################## +# RDMA needs OpenFabrics libraries +if test "$rdma" != "no" ; then + cat > $TMPC < +int main(void) { return 0; } +EOF + rdma_libs="-lrdmacm -libverbs" + if compile_prog "" "$rdma_libs" ; then + rdma="yes" + libs_softmmu="$libs_softmmu $rdma_libs" + else + if test "$rdma" = "yes" ; then + error_exit \ + " OpenFabrics librdmacm/libibverbs not present." \ + " Your options:" \ + " (1) Fast: Install infiniband packages from your distro." \ + " (2) Cleanest: Install libraries from www.openfabrics.org" \ + " (3) Also: Install softiwarp if you don't have RDMA hardware" + fi + rdma="no" + fi +fi + +########################################## +# VNC TLS/WS detection +if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then + cat > $TMPC < +int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } +EOF + vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` + vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` + if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then + if test "$vnc_tls" != "no" ; then + vnc_tls=yes + fi + if test "$vnc_ws" != "no" ; then + vnc_ws=yes + fi + libs_softmmu="$vnc_tls_libs $libs_softmmu" + QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" + else + if test "$vnc_tls" = "yes" ; then + feature_not_found "vnc-tls" "Install gnutls devel" + fi + if test "$vnc_ws" = "yes" ; then + feature_not_found "vnc-ws" "Install gnutls devel" + fi + vnc_tls=no + vnc_ws=no + fi +fi + +########################################## +# Quorum probe (check for gnutls) +if test "$quorum" != "no" ; then +cat > $TMPC < +#include +int main(void) {char data[4096], digest[32]; +gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest); +return 0; +} +EOF +quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` +quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` +if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then + qcow_tls=yes + libs_softmmu="$quorum_tls_libs $libs_softmmu" + libs_tools="$quorum_tls_libs $libs_softmmu" + QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags" + quorum="yes" +else + if test "$quorum" = "yes"; then + feature_not_found "gnutls" "gnutls > 2.10.0 required to compile Quorum" + fi + quorum="no" +fi +fi + +########################################## +# VNC SASL detection +if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then + cat > $TMPC < +#include +int main(void) { sasl_server_init(NULL, "qemu"); return 0; } +EOF + # Assuming Cyrus-SASL installed in /usr prefix + vnc_sasl_cflags="" + vnc_sasl_libs="-lsasl2" + if compile_prog "$vnc_sasl_cflags" "$vnc_sasl_libs" ; then + vnc_sasl=yes + libs_softmmu="$vnc_sasl_libs $libs_softmmu" + QEMU_CFLAGS="$QEMU_CFLAGS $vnc_sasl_cflags" + else + if test "$vnc_sasl" = "yes" ; then + feature_not_found "vnc-sasl" "Install Cyrus SASL devel" + fi + vnc_sasl=no + fi +fi + +########################################## +# VNC JPEG detection +if test "$vnc" = "yes" -a "$vnc_jpeg" != "no" ; then +cat > $TMPC < +#include +int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; } +EOF + vnc_jpeg_cflags="" + vnc_jpeg_libs="-ljpeg" + if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then + vnc_jpeg=yes + libs_softmmu="$vnc_jpeg_libs $libs_softmmu" + QEMU_CFLAGS="$QEMU_CFLAGS $vnc_jpeg_cflags" + else + if test "$vnc_jpeg" = "yes" ; then + feature_not_found "vnc-jpeg" "Install libjpeg-turbo devel" + fi + vnc_jpeg=no + fi +fi + +########################################## +# VNC PNG detection +if test "$vnc" = "yes" -a "$vnc_png" != "no" ; then +cat > $TMPC < +#include +#include +int main(void) { + png_structp png_ptr; + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + return png_ptr != 0; +} +EOF + if $pkg_config libpng --exists; then + vnc_png_cflags=`$pkg_config libpng --cflags` + vnc_png_libs=`$pkg_config libpng --libs` + else + vnc_png_cflags="" + vnc_png_libs="-lpng" + fi + if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then + vnc_png=yes + libs_softmmu="$vnc_png_libs $libs_softmmu" + QEMU_CFLAGS="$QEMU_CFLAGS $vnc_png_cflags" + else + if test "$vnc_png" = "yes" ; then + feature_not_found "vnc-png" "Install libpng devel" + fi + vnc_png=no + fi +fi + +########################################## +# fnmatch() probe, used for ACL routines +fnmatch="no" +cat > $TMPC << EOF +#include +int main(void) +{ + fnmatch("foo", "foo", 0); + return 0; +} +EOF +if compile_prog "" "" ; then + fnmatch="yes" +fi + +########################################## +# uuid_generate() probe, used for vdi block driver +# Note that on some systems (notably MacOSX) no extra library +# need be linked to get the uuid functions. +if test "$uuid" != "no" ; then + uuid_libs="-luuid" + cat > $TMPC << EOF +#include +int main(void) +{ + uuid_t my_uuid; + uuid_generate(my_uuid); + return 0; +} +EOF + if compile_prog "" "" ; then + uuid="yes" + elif compile_prog "" "$uuid_libs" ; then + uuid="yes" + libs_softmmu="$uuid_libs $libs_softmmu" + libs_tools="$uuid_libs $libs_tools" + else + if test "$uuid" = "yes" ; then + feature_not_found "uuid" "Install libuuid devel" + fi + uuid=no + fi +fi + +if test "$vhdx" = "yes" ; then + if test "$uuid" = "no" ; then + error_exit "uuid required for VHDX support" + fi +elif test "$vhdx" != "no" ; then + if test "$uuid" = "yes" ; then + vhdx=yes + else + vhdx=no + fi +fi + +########################################## +# xfsctl() probe, used for raw-posix +if test "$xfs" != "no" ; then + cat > $TMPC << EOF +#include /* NULL */ +#include +int main(void) +{ + xfsctl(NULL, 0, 0, NULL); + return 0; +} +EOF + if compile_prog "" "" ; then + xfs="yes" + else + if test "$xfs" = "yes" ; then + feature_not_found "xfs" "Instal xfsprogs/xfslibs devel" + fi + xfs=no + fi +fi + +########################################## +# vde libraries probe +if test "$vde" != "no" ; then + vde_libs="-lvdeplug" + cat > $TMPC << EOF +#include +int main(void) +{ + struct vde_open_args a = {0, 0, 0}; + char s[] = ""; + vde_open(s, s, &a); + return 0; +} +EOF + if compile_prog "" "$vde_libs" ; then + vde=yes + libs_softmmu="$vde_libs $libs_softmmu" + libs_tools="$vde_libs $libs_tools" + else + if test "$vde" = "yes" ; then + feature_not_found "vde" "Install vde (Virtual Distributed Ethernet) devel" + fi + vde=no + fi +fi + +########################################## +# netmap support probe +# Apart from looking for netmap headers, we make sure that the host API version +# supports the netmap backend (>=11). The upper bound (15) is meant to simulate +# a minor/major version number. Minor new features will be marked with values up +# to 15, and if something happens that requires a change to the backend we will +# move above 15, submit the backend fixes and modify this two bounds. +if test "$netmap" != "no" ; then + cat > $TMPC << EOF +#include +#include +#include +#include +#if (NETMAP_API < 11) || (NETMAP_API > 15) +#error +#endif +int main(void) { return 0; } +EOF + if compile_prog "" "" ; then + netmap=yes + else + if test "$netmap" = "yes" ; then + feature_not_found "netmap" + fi + netmap=no + fi +fi + +########################################## +# libcap-ng library probe +if test "$cap_ng" != "no" ; then + cap_libs="-lcap-ng" + cat > $TMPC << EOF +#include +int main(void) +{ + capng_capability_to_name(CAPNG_EFFECTIVE); + return 0; +} +EOF + if compile_prog "" "$cap_libs" ; then + cap_ng=yes + libs_tools="$cap_libs $libs_tools" + else + if test "$cap_ng" = "yes" ; then + feature_not_found "cap_ng" "Install libcap-ng devel" + fi + cap_ng=no + fi +fi + +########################################## +# Sound support libraries probe + +audio_drv_probe() +{ + drv=$1 + hdr=$2 + lib=$3 + exp=$4 + cfl=$5 + cat > $TMPC << EOF +#include <$hdr> +int main(void) { $exp } +EOF + if compile_prog "$cfl" "$lib" ; then + : + else + error_exit "$drv check failed" \ + "Make sure to have the $drv libs and headers installed." + fi +} + +audio_drv_list=`echo "$audio_drv_list" | sed -e 's/,/ /g'` +for drv in $audio_drv_list; do + case $drv in + alsa) + audio_drv_probe $drv alsa/asoundlib.h -lasound \ + "return snd_pcm_close((snd_pcm_t *)0);" + libs_softmmu="-lasound $libs_softmmu" + ;; + + fmod) + if test -z $fmod_lib || test -z $fmod_inc; then + error_exit "You must specify path to FMOD library and headers" \ + "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so" + fi + audio_drv_probe $drv fmod.h $fmod_lib "return FSOUND_GetVersion();" "-I $fmod_inc" + libs_softmmu="$fmod_lib $libs_softmmu" + ;; + + esd) + audio_drv_probe $drv esd.h -lesd 'return esd_play_stream(0, 0, "", 0);' + libs_softmmu="-lesd $libs_softmmu" + audio_pt_int="yes" + ;; + + pa) + audio_drv_probe $drv pulse/mainloop.h "-lpulse" \ + "pa_mainloop *m = 0; pa_mainloop_free (m); return 0;" + libs_softmmu="-lpulse $libs_softmmu" + audio_pt_int="yes" + ;; + + coreaudio) + libs_softmmu="-framework CoreAudio $libs_softmmu" + ;; + + dsound) + libs_softmmu="-lole32 -ldxguid $libs_softmmu" + audio_win_int="yes" + ;; + + oss) + libs_softmmu="$oss_lib $libs_softmmu" + ;; + + sdl|wav) + # XXX: Probes for CoreAudio, DirectSound, SDL(?) + ;; + + winwave) + libs_softmmu="-lwinmm $libs_softmmu" + audio_win_int="yes" + ;; + + *) + echo "$audio_possible_drivers" | grep -q "\<$drv\>" || { + error_exit "Unknown driver '$drv' selected" \ + "Possible drivers are: $audio_possible_drivers" + } + ;; + esac +done + +########################################## +# BrlAPI probe + +if test "$brlapi" != "no" ; then + brlapi_libs="-lbrlapi" + cat > $TMPC << EOF +#include +#include +int main( void ) { return brlapi__openConnection (NULL, NULL, NULL); } +EOF + if compile_prog "" "$brlapi_libs" ; then + brlapi=yes + libs_softmmu="$brlapi_libs $libs_softmmu" + else + if test "$brlapi" = "yes" ; then + feature_not_found "brlapi" "Install brlapi devel" + fi + brlapi=no + fi +fi + +########################################## +# curses probe +if test "$curses" != "no" ; then + if test "$mingw32" = "yes" ; then + curses_list="-lpdcurses" + else + curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lncurses:-lcurses" + fi + curses_found=no + cat > $TMPC << EOF +#include +int main(void) { + const char *s = curses_version(); + resize_term(0, 0); + return s != 0; +} +EOF + IFS=: + for curses_lib in $curses_list; do + unset IFS + if compile_prog "" "$curses_lib" ; then + curses_found=yes + libs_softmmu="$curses_lib $libs_softmmu" + break + fi + done + unset IFS + if test "$curses_found" = "yes" ; then + curses=yes + else + if test "$curses" = "yes" ; then + feature_not_found "curses" "Install ncurses devel" + fi + curses=no + fi +fi + +########################################## +# curl probe +if test "$curl" != "no" ; then + if $pkg_config libcurl --exists; then + curlconfig="$pkg_config libcurl" + else + curlconfig=curl-config + fi + cat > $TMPC << EOF +#include +int main(void) { curl_easy_init(); curl_multi_setopt(0, 0, 0); return 0; } +EOF + curl_cflags=`$curlconfig --cflags 2>/dev/null` + curl_libs=`$curlconfig --libs 2>/dev/null` + if compile_prog "$curl_cflags" "$curl_libs" ; then + curl=yes + else + if test "$curl" = "yes" ; then + feature_not_found "curl" "Install libcurl devel" + fi + curl=no + fi +fi # test "$curl" + +########################################## +# bluez support probe +if test "$bluez" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { return bt_error(0); } +EOF + bluez_cflags=`$pkg_config --cflags bluez 2> /dev/null` + bluez_libs=`$pkg_config --libs bluez 2> /dev/null` + if compile_prog "$bluez_cflags" "$bluez_libs" ; then + bluez=yes + libs_softmmu="$bluez_libs $libs_softmmu" + else + if test "$bluez" = "yes" ; then + feature_not_found "bluez" "Install bluez-libs/libbluetooth devel" + fi + bluez="no" + fi +fi + +########################################## +# glib support probe + +glib_req_ver=2.22 +glib_modules=gthread-2.0 +if test "$modules" = yes; then + glib_modules="$glib_modules gmodule-2.0" +fi + +for i in $glib_modules; do + if $pkg_config --atleast-version=$glib_req_ver $i; then + glib_cflags=`$pkg_config --cflags $i` + glib_libs=`$pkg_config --libs $i` + CFLAGS="$glib_cflags $CFLAGS" + LIBS="$glib_libs $LIBS" + libs_qga="$glib_libs $libs_qga" + else + error_exit "glib-$glib_req_ver $i is required to compile QEMU" + fi +done + +# g_test_trap_subprocess added in 2.38. Used by some tests. +glib_subprocess=yes +if ! $pkg_config --atleast-version=2.38 glib-2.0; then + glib_subprocess=no +fi + +# Silence clang 3.5.0 warnings about glib attribute __alloc_size__ usage +cat > $TMPC << EOF +#include +int main(void) { return 0; } +EOF +if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then + if cc_has_warning_flag "-Wno-unknown-attributes"; then + glib_cflags="-Wno-unknown-attributes $glib_cflags" + CFLAGS="-Wno-unknown-attributes $CFLAGS" + fi +fi + +########################################## +# SHA command probe for modules +if test "$modules" = yes; then + shacmd_probe="sha1sum sha1 shasum" + for c in $shacmd_probe; do + if has $c; then + shacmd="$c" + break + fi + done + if test "$shacmd" = ""; then + error_exit "one of the checksum commands is required to enable modules: $shacmd_probe" + fi +fi + +########################################## +# pixman support probe + +if test "$pixman" = ""; then + if test "$want_tools" = "no" -a "$softmmu" = "no"; then + pixman="none" + elif $pkg_config --atleast-version=0.21.8 pixman-1 > /dev/null 2>&1; then + pixman="system" + else + pixman="internal" + fi +fi +if test "$pixman" = "none"; then + if test "$want_tools" != "no" -o "$softmmu" != "no"; then + error_exit "pixman disabled but system emulation or tools build" \ + "enabled. You can turn off pixman only if you also" \ + "disable all system emulation targets and the tools" \ + "build with '--disable-tools --disable-system'." + fi + pixman_cflags= + pixman_libs= +elif test "$pixman" = "system"; then + # pixman version has been checked above + pixman_cflags=`$pkg_config --cflags pixman-1` + pixman_libs=`$pkg_config --libs pixman-1` +else + if test ! -d ${source_path}/pixman/pixman; then + error_exit "pixman >= 0.21.8 not present. Your options:" \ + " (1) Preferred: Install the pixman devel package (any recent" \ + " distro should have packages as Xorg needs pixman too)." \ + " (2) Fetch the pixman submodule, using:" \ + " git submodule update --init pixman" + fi + mkdir -p pixman/pixman + pixman_cflags="-I\$(SRC_PATH)/pixman/pixman -I\$(BUILD_DIR)/pixman/pixman" + pixman_libs="-L\$(BUILD_DIR)/pixman/pixman/.libs -lpixman-1" +fi + +########################################## +# libcap probe + +if test "$cap" != "no" ; then + cat > $TMPC < +#include +int main(void) { cap_t caps; caps = cap_init(); return caps != NULL; } +EOF + if compile_prog "" "-lcap" ; then + cap=yes + else + cap=no + fi +fi + +########################################## +# pthread probe +PTHREADLIBS_LIST="-pthread -lpthread -lpthreadGC2" + +pthread=no +cat > $TMPC << EOF +#include +static void *f(void *p) { return NULL; } +int main(void) { + pthread_t thread; + pthread_create(&thread, 0, f, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + pthread=yes +else + for pthread_lib in $PTHREADLIBS_LIST; do + if compile_prog "" "$pthread_lib" ; then + pthread=yes + found=no + for lib_entry in $LIBS; do + if test "$lib_entry" = "$pthread_lib"; then + found=yes + break + fi + done + if test "$found" = "no"; then + LIBS="$pthread_lib $LIBS" + fi + break + fi + done +fi + +if test "$mingw32" != yes -a "$pthread" = no; then + error_exit "pthread check failed" \ + "Make sure to have the pthread libs and headers installed." +fi + +# check for pthread_setname_np +pthread_setname_np=no +cat > $TMPC << EOF +#include + +static void *f(void *p) { return NULL; } +int main(void) +{ + pthread_t thread; + pthread_create(&thread, 0, f, 0); + pthread_setname_np(thread, "QEMU"); + return 0; +} +EOF +if compile_prog "" "$pthread_lib" ; then + pthread_setname_np=yes +fi + +########################################## +# rbd probe +if test "$rbd" != "no" ; then + cat > $TMPC < +#include +int main(void) { + rados_t cluster; + rados_create(&cluster, NULL); + return 0; +} +EOF + rbd_libs="-lrbd -lrados" + if compile_prog "" "$rbd_libs" ; then + rbd=yes + else + if test "$rbd" = "yes" ; then + feature_not_found "rados block device" "Install librbd/ceph devel" + fi + rbd=no + fi +fi + +########################################## +# libssh2 probe +min_libssh2_version=1.2.8 +if test "$libssh2" != "no" ; then + if $pkg_config --atleast-version=$min_libssh2_version libssh2; then + libssh2_cflags=`$pkg_config libssh2 --cflags` + libssh2_libs=`$pkg_config libssh2 --libs` + libssh2=yes + else + if test "$libssh2" = "yes" ; then + error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2" + fi + libssh2=no + fi +fi + +########################################## +# libssh2_sftp_fsync probe + +if test "$libssh2" = "yes"; then + cat > $TMPC < +#include +#include +int main(void) { + LIBSSH2_SESSION *session; + LIBSSH2_SFTP *sftp; + LIBSSH2_SFTP_HANDLE *sftp_handle; + session = libssh2_session_init (); + sftp = libssh2_sftp_init (session); + sftp_handle = libssh2_sftp_open (sftp, "/", 0, 0); + libssh2_sftp_fsync (sftp_handle); + return 0; +} +EOF + # libssh2_cflags/libssh2_libs defined in previous test. + if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then + QEMU_CFLAGS="-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS" + fi +fi + +########################################## +# linux-aio probe + +if test "$linux_aio" != "no" ; then + cat > $TMPC < +#include +#include +int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); return 0; } +EOF + if compile_prog "" "-laio" ; then + linux_aio=yes + else + if test "$linux_aio" = "yes" ; then + feature_not_found "linux AIO" "Install libaio devel" + fi + linux_aio=no + fi +fi + +########################################## +# TPM passthrough is only on x86 Linux + +if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" = x86_64; then + tpm_passthrough=$tpm +else + tpm_passthrough=no +fi + +########################################## +# attr probe + +if test "$attr" != "no" ; then + cat > $TMPC < +#include +#ifdef CONFIG_LIBATTR +#include +#else +#include +#endif +int main(void) { getxattr(NULL, NULL, NULL, 0); setxattr(NULL, NULL, NULL, 0, 0); return 0; } +EOF + if compile_prog "" "" ; then + attr=yes + # Older distros have , and need -lattr: + elif compile_prog "-DCONFIG_LIBATTR" "-lattr" ; then + attr=yes + LIBS="-lattr $LIBS" + libattr=yes + else + if test "$attr" = "yes" ; then + feature_not_found "ATTR" "Install libc6 or libattr devel" + fi + attr=no + fi +fi + +########################################## +# iovec probe +cat > $TMPC < +#include +#include +int main(void) { return sizeof(struct iovec); } +EOF +iovec=no +if compile_prog "" "" ; then + iovec=yes +fi + +########################################## +# preadv probe +cat > $TMPC < +#include +#include +int main(void) { return preadv(0, 0, 0, 0); } +EOF +preadv=no +if compile_prog "" "" ; then + preadv=yes +fi + +########################################## +# fdt probe +# fdt support is mandatory for at least some target architectures, +# so insist on it if we're building those system emulators. +fdt_required=no +for target in $target_list; do + case $target in + aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu) + fdt_required=yes + ;; + esac +done + +if test "$fdt_required" = "yes"; then + if test "$fdt" = "no"; then + error_exit "fdt disabled but some requested targets require it." \ + "You can turn off fdt only if you also disable all the system emulation" \ + "targets which need it (by specifying a cut down --target-list)." + fi + fdt=yes +fi + +if test "$fdt" != "no" ; then + fdt_libs="-lfdt" + # explicitly check for libfdt_env.h as it is missing in some stable installs + cat > $TMPC << EOF +#include +int main(void) { return 0; } +EOF + if compile_prog "" "$fdt_libs" ; then + # system DTC is good - use it + fdt=yes + elif test -d ${source_path}/dtc/libfdt ; then + # have submodule DTC - use it + fdt=yes + dtc_internal="yes" + mkdir -p dtc + if [ "$pwd_is_source_path" != "y" ] ; then + symlink "$source_path/dtc/Makefile" "dtc/Makefile" + symlink "$source_path/dtc/scripts" "dtc/scripts" + fi + fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt" + fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs" + elif test "$fdt" = "yes" ; then + # have neither and want - prompt for system/submodule install + error_exit "DTC (libfdt) not present. Your options:" \ + " (1) Preferred: Install the DTC (libfdt) devel package" \ + " (2) Fetch the DTC submodule, using:" \ + " git submodule update --init dtc" + else + # don't have and don't want + fdt_libs= + fdt=no + fi +fi + +libs_softmmu="$libs_softmmu $fdt_libs" + +########################################## +# opengl probe (for sdl2, milkymist-tmu2) + +# GLX probe, used by milkymist-tmu2 +# this is temporary, code will be switched to egl mid-term. +cat > $TMPC << EOF +#include +#include +#include +int main(void) { glBegin(0); glXQueryVersion(0,0,0); return 0; } +EOF +if compile_prog "" "-lGL -lX11" ; then + have_glx=yes +else + have_glx=no +fi + +if test "$opengl" != "no" ; then + opengl_pkgs="gl glesv2" + if $pkg_config $opengl_pkgs x11 && test "$have_glx" = "yes"; then + opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags" + opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs" + opengl=yes + else + if test "$opengl" = "yes" ; then + feature_not_found "opengl" "Install GL devel (e.g. MESA)" + fi + opengl_cflags="" + opengl_libs="" + opengl=no + fi +fi + + +########################################## +# archipelago probe +if test "$archipelago" != "no" ; then + cat > $TMPC < +#include +#include +int main(void) { + xseg_initialize(); + return 0; +} +EOF + archipelago_libs=-lxseg + if compile_prog "" "$archipelago_libs"; then + archipelago="yes" + libs_tools="$archipelago_libs $libs_tools" + libs_softmmu="$archipelago_libs $libs_softmmu" + + echo "WARNING: Please check the licenses of QEMU and libxseg carefully." + echo "GPLv3 versions of libxseg may not be compatible with QEMU's" + echo "license and therefore prevent redistribution." + echo + echo "To disable Archipelago, use --disable-archipelago" + else + if test "$archipelago" = "yes" ; then + feature_not_found "Archipelago backend support" "Install libxseg devel" + fi + archipelago="no" + fi +fi + + +########################################## +# glusterfs probe +if test "$glusterfs" != "no" ; then + if $pkg_config --atleast-version=3 glusterfs-api; then + glusterfs="yes" + glusterfs_cflags=`$pkg_config --cflags glusterfs-api` + glusterfs_libs=`$pkg_config --libs glusterfs-api` + if $pkg_config --atleast-version=5 glusterfs-api; then + glusterfs_discard="yes" + fi + if $pkg_config --atleast-version=6 glusterfs-api; then + glusterfs_zerofill="yes" + fi + else + if test "$glusterfs" = "yes" ; then + feature_not_found "GlusterFS backend support" \ + "Install glusterfs-api devel >= 3" + fi + glusterfs="no" + fi +fi + +# Check for inotify functions when we are building linux-user +# emulator. This is done because older glibc versions don't +# have syscall stubs for these implemented. In that case we +# don't provide them even if kernel supports them. +# +inotify=no +cat > $TMPC << EOF +#include + +int +main(void) +{ + /* try to start inotify */ + return inotify_init(); +} +EOF +if compile_prog "" "" ; then + inotify=yes +fi + +inotify1=no +cat > $TMPC << EOF +#include + +int +main(void) +{ + /* try to start inotify */ + return inotify_init1(0); +} +EOF +if compile_prog "" "" ; then + inotify1=yes +fi + +# check if utimensat and futimens are supported +utimens=no +cat > $TMPC << EOF +#define _ATFILE_SOURCE +#include +#include +#include + +int main(void) +{ + utimensat(AT_FDCWD, "foo", NULL, 0); + futimens(0, NULL); + return 0; +} +EOF +if compile_prog "" "" ; then + utimens=yes +fi + +# check if pipe2 is there +pipe2=no +cat > $TMPC << EOF +#include +#include + +int main(void) +{ + int pipefd[2]; + return pipe2(pipefd, O_CLOEXEC); +} +EOF +if compile_prog "" "" ; then + pipe2=yes +fi + +# check if accept4 is there +accept4=no +cat > $TMPC << EOF +#include +#include + +int main(void) +{ + accept4(0, NULL, NULL, SOCK_CLOEXEC); + return 0; +} +EOF +if compile_prog "" "" ; then + accept4=yes +fi + +# check if tee/splice is there. vmsplice was added same time. +splice=no +cat > $TMPC << EOF +#include +#include +#include + +int main(void) +{ + int len, fd = 0; + len = tee(STDIN_FILENO, STDOUT_FILENO, INT_MAX, SPLICE_F_NONBLOCK); + splice(STDIN_FILENO, NULL, fd, NULL, len, SPLICE_F_MOVE); + return 0; +} +EOF +if compile_prog "" "" ; then + splice=yes +fi + +########################################## +# libnuma probe + +if test "$numa" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { return numa_available(); } +EOF + + if compile_prog "" "-lnuma" ; then + numa=yes + libs_softmmu="-lnuma $libs_softmmu" + else + if test "$numa" = "yes" ; then + feature_not_found "numa" "install numactl devel" + fi + numa=no + fi +fi + +########################################## +# tcmalloc probe + +if test "$tcmalloc" = "yes" ; then + cat > $TMPC << EOF +#include +int main(void) { malloc(1); return 0; } +EOF + + if compile_prog "" "-ltcmalloc" ; then + LIBS="-ltcmalloc $LIBS" + else + feature_not_found "tcmalloc" "install gperftools devel" + fi +fi + +########################################## +# signalfd probe +signalfd="no" +cat > $TMPC << EOF +#include +#include +#include +int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); } +EOF + +if compile_prog "" "" ; then + signalfd=yes +fi + +# check if eventfd is supported +eventfd=no +cat > $TMPC << EOF +#include + +int main(void) +{ + return eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); +} +EOF +if compile_prog "" "" ; then + eventfd=yes +fi + +# check for fallocate +fallocate=no +cat > $TMPC << EOF +#include + +int main(void) +{ + fallocate(0, 0, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + fallocate=yes +fi + +# check for fallocate hole punching +fallocate_punch_hole=no +cat > $TMPC << EOF +#include +#include + +int main(void) +{ + fallocate(0, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + fallocate_punch_hole=yes +fi + +# check that fallocate supports range zeroing inside the file +fallocate_zero_range=no +cat > $TMPC << EOF +#include +#include + +int main(void) +{ + fallocate(0, FALLOC_FL_ZERO_RANGE, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + fallocate_zero_range=yes +fi + +# check for posix_fallocate +posix_fallocate=no +cat > $TMPC << EOF +#include + +int main(void) +{ + posix_fallocate(0, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + posix_fallocate=yes +fi + +# check for sync_file_range +sync_file_range=no +cat > $TMPC << EOF +#include + +int main(void) +{ + sync_file_range(0, 0, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + sync_file_range=yes +fi + +# check for linux/fiemap.h and FS_IOC_FIEMAP +fiemap=no +cat > $TMPC << EOF +#include +#include +#include + +int main(void) +{ + ioctl(0, FS_IOC_FIEMAP, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + fiemap=yes +fi + +# check for dup3 +dup3=no +cat > $TMPC << EOF +#include + +int main(void) +{ + dup3(0, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + dup3=yes +fi + +# check for ppoll support +ppoll=no +cat > $TMPC << EOF +#include + +int main(void) +{ + struct pollfd pfd = { .fd = 0, .events = 0, .revents = 0 }; + ppoll(&pfd, 1, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + ppoll=yes +fi + +# check for prctl(PR_SET_TIMERSLACK , ... ) support +prctl_pr_set_timerslack=no +cat > $TMPC << EOF +#include + +int main(void) +{ + prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + prctl_pr_set_timerslack=yes +fi + +# check for epoll support +epoll=no +cat > $TMPC << EOF +#include + +int main(void) +{ + epoll_create(0); + return 0; +} +EOF +if compile_prog "" "" ; then + epoll=yes +fi + +# epoll_create1 and epoll_pwait are later additions +# so we must check separately for their presence +epoll_create1=no +cat > $TMPC << EOF +#include + +int main(void) +{ + /* Note that we use epoll_create1 as a value, not as + * a function being called. This is necessary so that on + * old SPARC glibc versions where the function was present in + * the library but not declared in the header file we will + * fail the configure check. (Otherwise we will get a compiler + * warning but not an error, and will proceed to fail the + * qemu compile where we compile with -Werror.) + */ + return (int)(uintptr_t)&epoll_create1; +} +EOF +if compile_prog "" "" ; then + epoll_create1=yes +fi + +epoll_pwait=no +cat > $TMPC << EOF +#include + +int main(void) +{ + epoll_pwait(0, 0, 0, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + epoll_pwait=yes +fi + +# check for sendfile support +sendfile=no +cat > $TMPC << EOF +#include + +int main(void) +{ + return sendfile(0, 0, 0, 0); +} +EOF +if compile_prog "" "" ; then + sendfile=yes +fi + +# check for timerfd support (glibc 2.8 and newer) +timerfd=no +cat > $TMPC << EOF +#include + +int main(void) +{ + return(timerfd_create(CLOCK_REALTIME, 0)); +} +EOF +if compile_prog "" "" ; then + timerfd=yes +fi + +# check for setns and unshare support +setns=no +cat > $TMPC << EOF +#include + +int main(void) +{ + int ret; + ret = setns(0, 0); + ret = unshare(0); + return ret; +} +EOF +if compile_prog "" "" ; then + setns=yes +fi + +# Check if tools are available to build documentation. +if test "$docs" != "no" ; then + if has makeinfo && has pod2man; then + docs=yes + else + if test "$docs" = "yes" ; then + feature_not_found "docs" "Install texinfo and Perl/perl-podlators" + fi + docs=no + fi +fi + +# Search for bswap_32 function +byteswap_h=no +cat > $TMPC << EOF +#include +int main(void) { return bswap_32(0); } +EOF +if compile_prog "" "" ; then + byteswap_h=yes +fi + +# Search for bswap32 function +bswap_h=no +cat > $TMPC << EOF +#include +#include +#include +int main(void) { return bswap32(0); } +EOF +if compile_prog "" "" ; then + bswap_h=yes +fi + +########################################## +# Do we have libiscsi >= 1.9.0 +if test "$libiscsi" != "no" ; then + if $pkg_config --atleast-version=1.9.0 libiscsi; then + libiscsi="yes" + libiscsi_cflags=$($pkg_config --cflags libiscsi) + libiscsi_libs=$($pkg_config --libs libiscsi) + else + if test "$libiscsi" = "yes" ; then + feature_not_found "libiscsi" "Install libiscsi >= 1.9.0" + fi + libiscsi="no" + fi +fi + +########################################## +# Do we need libm +cat > $TMPC << EOF +#include +int main(int argc, char **argv) { return isnan(sin((double)argc)); } +EOF +if compile_prog "" "" ; then + : +elif compile_prog "" "-lm" ; then + LIBS="-lm $LIBS" + libs_qga="-lm $libs_qga" +else + error_exit "libm check failed" +fi + +########################################## +# Do we need librt +# uClibc provides 2 versions of clock_gettime(), one with realtime +# support and one without. This means that the clock_gettime() don't +# need -lrt. We still need it for timer_create() so we check for this +# function in addition. +cat > $TMPC < +#include +int main(void) { + timer_create(CLOCK_REALTIME, NULL, NULL); + return clock_gettime(CLOCK_REALTIME, NULL); +} +EOF + +if compile_prog "" "" ; then + : +# we need pthread for static linking. use previous pthread test result +elif compile_prog "" "$pthread_lib -lrt" ; then + LIBS="$LIBS -lrt" + libs_qga="$libs_qga -lrt" +fi + +if test "$darwin" != "yes" -a "$mingw32" != "yes" -a "$solaris" != yes -a \ + "$aix" != "yes" -a "$haiku" != "yes" ; then + libs_softmmu="-lutil $libs_softmmu" +fi + +########################################## +# spice probe +if test "$spice" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { spice_server_new(); return 0; } +EOF + spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null) + spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null) + if $pkg_config --atleast-version=0.12.0 spice-server && \ + $pkg_config --atleast-version=0.12.3 spice-protocol && \ + compile_prog "$spice_cflags" "$spice_libs" ; then + spice="yes" + libs_softmmu="$libs_softmmu $spice_libs" + QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags" + spice_protocol_version=$($pkg_config --modversion spice-protocol) + spice_server_version=$($pkg_config --modversion spice-server) + else + if test "$spice" = "yes" ; then + feature_not_found "spice" \ + "Install spice-server(>=0.12.0) and spice-protocol(>=0.12.3) devel" + fi + spice="no" + fi +fi + +# check for libcacard for smartcard support +smartcard_cflags="" +# TODO - what's the minimal nss version we support? +if test "$smartcard_nss" != "no"; then + cat > $TMPC << EOF +#include +int main(void) { PK11_FreeSlot(0); return 0; } +EOF + # FIXME: do not include $glib_* in here + nss_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs" + nss_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags" + test_cflags="$nss_cflags" + # The header files in nss < 3.13.3 have a bug which causes them to + # emit a warning. If we're going to compile QEMU with -Werror, then + # test that the headers don't have this bug. Otherwise we would pass + # the configure test but fail to compile QEMU later. + if test "$werror" = "yes"; then + test_cflags="-Werror $test_cflags" + fi + if test -n "$libtool" && + $pkg_config --atleast-version=3.12.8 nss && \ + compile_prog "$test_cflags" "$nss_libs"; then + smartcard_nss="yes" + else + if test "$smartcard_nss" = "yes"; then + feature_not_found "nss" "Install nss devel >= 3.12.8" + fi + smartcard_nss="no" + fi +fi + +# check for libusb +if test "$libusb" != "no" ; then + if $pkg_config --atleast-version=1.0.13 libusb-1.0; then + libusb="yes" + libusb_cflags=$($pkg_config --cflags libusb-1.0) + libusb_libs=$($pkg_config --libs libusb-1.0) + QEMU_CFLAGS="$QEMU_CFLAGS $libusb_cflags" + libs_softmmu="$libs_softmmu $libusb_libs" + else + if test "$libusb" = "yes"; then + feature_not_found "libusb" "Install libusb devel >= 1.0.13" + fi + libusb="no" + fi +fi + +# check for usbredirparser for usb network redirection support +if test "$usb_redir" != "no" ; then + if $pkg_config --atleast-version=0.6 libusbredirparser-0.5; then + usb_redir="yes" + usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5) + usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5) + QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags" + libs_softmmu="$libs_softmmu $usb_redir_libs" + else + if test "$usb_redir" = "yes"; then + feature_not_found "usb-redir" "Install usbredir devel" + fi + usb_redir="no" + fi +fi + +########################################## +# check if we have VSS SDK headers for win + +if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$vss_win32_sdk" != "no" ; then + case "$vss_win32_sdk" in + "") vss_win32_include="-I$source_path" ;; + *\ *) # The SDK is installed in "Program Files" by default, but we cannot + # handle path with spaces. So we symlink the headers into ".sdk/vss". + vss_win32_include="-I$source_path/.sdk/vss" + symlink "$vss_win32_sdk/inc" "$source_path/.sdk/vss/inc" + ;; + *) vss_win32_include="-I$vss_win32_sdk" + esac + cat > $TMPC << EOF +#define __MIDL_user_allocate_free_DEFINED__ +#include +int main(void) { return VSS_CTX_BACKUP; } +EOF + if compile_prog "$vss_win32_include" "" ; then + guest_agent_with_vss="yes" + QEMU_CFLAGS="$QEMU_CFLAGS $vss_win32_include" + libs_qga="-lole32 -loleaut32 -lshlwapi -luuid -lstdc++ -Wl,--enable-stdcall-fixup $libs_qga" + else + if test "$vss_win32_sdk" != "" ; then + echo "ERROR: Please download and install Microsoft VSS SDK:" + echo "ERROR: http://www.microsoft.com/en-us/download/details.aspx?id=23490" + echo "ERROR: On POSIX-systems, you can extract the SDK headers by:" + echo "ERROR: scripts/extract-vsssdk-headers setup.exe" + echo "ERROR: The headers are extracted in the directory \`inc'." + feature_not_found "VSS support" + fi + guest_agent_with_vss="no" + fi +fi + +########################################## +# lookup Windows platform SDK (if not specified) +# The SDK is needed only to build .tlb (type library) file of guest agent +# VSS provider from the source. It is usually unnecessary because the +# pre-compiled .tlb file is included. + +if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$guest_agent_with_vss" = "yes" ; then + if test -z "$win_sdk"; then + programfiles="$PROGRAMFILES" + test -n "$PROGRAMW6432" && programfiles="$PROGRAMW6432" + if test -n "$programfiles"; then + win_sdk=$(ls -d "$programfiles/Microsoft SDKs/Windows/v"* | tail -1) 2>/dev/null + else + feature_not_found "Windows SDK" + fi + elif test "$win_sdk" = "no"; then + win_sdk="" + fi +fi + +########################################## + +########################################## +# check if we have fdatasync + +fdatasync=no +cat > $TMPC << EOF +#include +int main(void) { +#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 +return fdatasync(0); +#else +#error Not supported +#endif +} +EOF +if compile_prog "" "" ; then + fdatasync=yes +fi + +########################################## +# check if we have madvise + +madvise=no +cat > $TMPC << EOF +#include +#include +#include +int main(void) { return madvise(NULL, 0, MADV_DONTNEED); } +EOF +if compile_prog "" "" ; then + madvise=yes +fi + +########################################## +# check if we have posix_madvise + +posix_madvise=no +cat > $TMPC << EOF +#include +#include +int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); } +EOF +if compile_prog "" "" ; then + posix_madvise=yes +fi + +########################################## +# check if we have usable SIGEV_THREAD_ID + +sigev_thread_id=no +cat > $TMPC << EOF +#include +int main(void) { + struct sigevent ev; + ev.sigev_notify = SIGEV_THREAD_ID; + ev._sigev_un._tid = 0; + asm volatile("" : : "g"(&ev)); + return 0; +} +EOF +if compile_prog "" "" ; then + sigev_thread_id=yes +fi + +########################################## +# check if trace backend exists + +$python "$source_path/scripts/tracetool.py" "--backends=$trace_backends" --check-backends > /dev/null 2> /dev/null +if test "$?" -ne 0 ; then + error_exit "invalid trace backends" \ + "Please choose supported trace backends." +fi + +########################################## +# For 'ust' backend, test if ust headers are present +if have_backend "ust"; then + cat > $TMPC << EOF +#include +int main(void) { return 0; } +EOF + if compile_prog "" "" ; then + if $pkg_config lttng-ust --exists; then + lttng_ust_libs=`$pkg_config --libs lttng-ust` + else + lttng_ust_libs="-llttng-ust" + fi + if $pkg_config liburcu-bp --exists; then + urcu_bp_libs=`$pkg_config --libs liburcu-bp` + else + urcu_bp_libs="-lurcu-bp" + fi + + LIBS="$lttng_ust_libs $urcu_bp_libs $LIBS" + libs_qga="$lttng_ust_libs $urcu_bp_libs $libs_qga" + else + error_exit "Trace backend 'ust' missing lttng-ust header files" + fi +fi + +########################################## +# For 'dtrace' backend, test if 'dtrace' command is present +if have_backend "dtrace"; then + if ! has 'dtrace' ; then + error_exit "dtrace command is not found in PATH $PATH" + fi + trace_backend_stap="no" + if has 'stap' ; then + trace_backend_stap="yes" + fi +fi + +########################################## +# check and set a backend for coroutine + +# We prefer ucontext, but it's not always possible. The fallback +# is sigcontext. gthread is not selectable except explicitly, because +# it is not functional enough to run QEMU proper. (It is occasionally +# useful for debugging purposes.) On Windows the only valid backend +# is the Windows-specific one. + +ucontext_works=no +if test "$darwin" != "yes"; then + cat > $TMPC << EOF +#include +#ifdef __stub_makecontext +#error Ignoring glibc stub makecontext which will always fail +#endif +int main(void) { makecontext(0, 0, 0); return 0; } +EOF + if compile_prog "" "" ; then + ucontext_works=yes + fi +fi + +if test "$coroutine" = ""; then + if test "$mingw32" = "yes"; then + coroutine=win32 + elif test "$ucontext_works" = "yes"; then + coroutine=ucontext + else + coroutine=sigaltstack + fi +else + case $coroutine in + windows) + if test "$mingw32" != "yes"; then + error_exit "'windows' coroutine backend only valid for Windows" + fi + # Unfortunately the user visible backend name doesn't match the + # coroutine-*.c filename for this case, so we have to adjust it here. + coroutine=win32 + ;; + ucontext) + if test "$ucontext_works" != "yes"; then + feature_not_found "ucontext" + fi + ;; + gthread|sigaltstack) + if test "$mingw32" = "yes"; then + error_exit "only the 'windows' coroutine backend is valid for Windows" + fi + ;; + *) + error_exit "unknown coroutine backend $coroutine" + ;; + esac +fi + +if test "$coroutine_pool" = ""; then + if test "$coroutine" = "gthread"; then + coroutine_pool=no + else + coroutine_pool=yes + fi +fi +if test "$coroutine" = "gthread" -a "$coroutine_pool" = "yes"; then + error_exit "'gthread' coroutine backend does not support pool (use --disable-coroutine-pool)" +fi + +########################################## +# check if we have open_by_handle_at + +open_by_handle_at=no +cat > $TMPC << EOF +#include +#if !defined(AT_EMPTY_PATH) +# error missing definition +#else +int main(void) { struct file_handle fh; return open_by_handle_at(0, &fh, 0); } +#endif +EOF +if compile_prog "" "" ; then + open_by_handle_at=yes +fi + +######################################## +# check if we have linux/magic.h + +linux_magic_h=no +cat > $TMPC << EOF +#include +int main(void) { + return 0; +} +EOF +if compile_prog "" "" ; then + linux_magic_h=yes +fi + +######################################## +# check whether we can disable warning option with a pragma (this is needed +# to silence warnings in the headers of some versions of external libraries). +# This test has to be compiled with -Werror as otherwise an unknown pragma is +# only a warning. +# +# If we can't selectively disable warning in the code, disable -Werror so that +# the build doesn't fail anyway. + +pragma_disable_unused_but_set=no +cat > $TMPC << EOF +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#pragma GCC diagnostic pop + +int main(void) { + return 0; +} +EOF +if compile_prog "-Werror" "" ; then + pragma_diagnostic_available=yes +else + werror=no +fi + +######################################## +# check if we have valgrind/valgrind.h + +valgrind_h=no +cat > $TMPC << EOF +#include +int main(void) { + return 0; +} +EOF +if compile_prog "" "" ; then + valgrind_h=yes +fi + +######################################## +# check if environ is declared + +has_environ=no +cat > $TMPC << EOF +#include +int main(void) { + environ = 0; + return 0; +} +EOF +if compile_prog "" "" ; then + has_environ=yes +fi + +######################################## +# check if cpuid.h is usable. + +cpuid_h=no +cat > $TMPC << EOF +#include +int main(void) { + unsigned a, b, c, d; + int max = __get_cpuid_max(0, 0); + + if (max >= 1) { + __cpuid(1, a, b, c, d); + } + + if (max >= 7) { + __cpuid_count(7, 0, a, b, c, d); + } + + return 0; +} +EOF +if compile_prog "" "" ; then + cpuid_h=yes +fi + +######################################## +# check if __[u]int128_t is usable. + +int128=no +cat > $TMPC << EOF +#if defined(__clang_major__) && defined(__clang_minor__) +# if ((__clang_major__ < 3) || (__clang_major__ == 3) && (__clang_minor__ < 2)) +# error __int128_t does not work in CLANG before 3.2 +# endif +#endif +__int128_t a; +__uint128_t b; +int main (void) { + a = a + b; + b = a * b; + a = a * a; + return 0; +} +EOF +if compile_prog "" "" ; then + int128=yes +fi + +######################################## +# check if getauxval is available. + +getauxval=no +cat > $TMPC << EOF +#include +int main(void) { + return getauxval(AT_HWCAP) == 0; +} +EOF +if compile_prog "" "" ; then + getauxval=yes +fi + +######################################## +# check if ccache is interfering with +# semantic analysis of macros + +ccache_cpp2=no +cat > $TMPC << EOF +static const int Z = 1; +#define fn() ({ Z; }) +#define TAUT(X) ((X) == Z) +#define PAREN(X, Y) (X == Y) +#define ID(X) (X) +int main(int argc, char *argv[]) +{ + int x = 0, y = 0; + x = ID(x); + x = fn(); + fn(); + if (PAREN(x, y)) return 0; + if (TAUT(Z)) return 0; + return 0; +} +EOF + +if ! compile_object "-Werror"; then + ccache_cpp2=yes +fi + +########################################## +# End of CC checks +# After here, no more $cc or $ld runs + +if test "$gcov" = "yes" ; then + CFLAGS="-fprofile-arcs -ftest-coverage -g $CFLAGS" + LDFLAGS="-fprofile-arcs -ftest-coverage $LDFLAGS" +elif test "$debug" = "no" ; then + CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS" +fi + +########################################## +# Do we have libnfs +if test "$libnfs" != "no" ; then + if $pkg_config --atleast-version=1.9.3 libnfs; then + libnfs="yes" + libnfs_libs=$($pkg_config --libs libnfs) + LIBS="$LIBS $libnfs_libs" + else + if test "$libnfs" = "yes" ; then + feature_not_found "libnfs" "Install libnfs devel >= 1.9.3" + fi + libnfs="no" + fi +fi + +# Disable zero malloc errors for official releases unless explicitly told to +# enable/disable +if test -z "$zero_malloc" ; then + if test "$z_version" = "50" ; then + zero_malloc="no" + else + zero_malloc="yes" + fi +fi + +# Now we've finished running tests it's OK to add -Werror to the compiler flags +if test "$werror" = "yes"; then + QEMU_CFLAGS="-Werror $QEMU_CFLAGS" +fi + +if test "$solaris" = "no" ; then + if $ld --version 2>/dev/null | grep "GNU ld" >/dev/null 2>/dev/null ; then + LDFLAGS="-Wl,--warn-common $LDFLAGS" + fi +fi + +# test if pod2man has --utf8 option +if pod2man --help | grep -q utf8; then + POD2MAN="pod2man --utf8" +else + POD2MAN="pod2man" +fi + +# Use ASLR, no-SEH and DEP if available +if test "$mingw32" = "yes" ; then + for flag in --dynamicbase --no-seh --nxcompat; do + if $ld --help 2>/dev/null | grep ".$flag" >/dev/null 2>/dev/null ; then + LDFLAGS="-Wl,$flag $LDFLAGS" + fi + done +fi + +qemu_confdir=$sysconfdir$confsuffix +qemu_moddir=$libdir$confsuffix +qemu_datadir=$datadir$confsuffix +qemu_localedir="$datadir/locale" + +tools="" +if test "$want_tools" = "yes" ; then + tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" + if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then + tools="qemu-nbd\$(EXESUF) $tools" + fi +fi +if test "$softmmu" = yes ; then + if test "$virtfs" != no ; then + if test "$cap" = yes && test "$linux" = yes && test "$attr" = yes ; then + virtfs=yes + tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)" + else + if test "$virtfs" = yes; then + error_exit "VirtFS is supported only on Linux and requires libcap-devel and libattr-devel" + fi + virtfs=no + fi + fi +fi +if [ "$guest_agent" != "no" ]; then + if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then + tools="qemu-ga\$(EXESUF) $tools" + if [ "$mingw32" = "yes" -a "$guest_agent_with_vss" = "yes" ]; then + tools="qga/vss-win32/qga-vss.dll qga/vss-win32/qga-vss.tlb $tools" + fi + guest_agent=yes + elif [ "$guest_agent" != yes ]; then + guest_agent=no + else + error_exit "Guest agent is not supported on this platform" + fi +fi + +# Mac OS X ships with a broken assembler +roms= +if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) -a \ + "$targetos" != "Darwin" -a "$targetos" != "SunOS" -a \ + "$softmmu" = yes ; then + roms="optionrom" +fi +if test "$cpu" = "ppc64" -a "$targetos" != "Darwin" ; then + roms="$roms spapr-rtas" +fi + +if test "$cpu" = "s390x" ; then + roms="$roms s390-ccw" +fi + +# Probe for the need for relocating the user-only binary. +if test "$pie" = "no" ; then + textseg_addr= + case "$cpu" in + arm | i386 | ppc* | s390* | sparc* | x86_64 | x32) + # ??? Rationale for choosing this address + textseg_addr=0x60000000 + ;; + mips) + # A 256M aligned address, high in the address space, with enough + # room for the code_gen_buffer above it before the stack. + textseg_addr=0x60000000 + ;; + esac + if [ -n "$textseg_addr" ]; then + cat > $TMPC < config-host.ld + textseg_ldflags="-Wl,-T../config-host.ld" + fi + fi +fi + +# prepend pixman and ftd flags after all config tests are done +QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS" +libs_softmmu="$pixman_libs $libs_softmmu" + +echo "Install prefix $prefix" +echo "BIOS directory `eval echo $qemu_datadir`" +echo "binary directory `eval echo $bindir`" +echo "library directory `eval echo $libdir`" +echo "module directory `eval echo $qemu_moddir`" +echo "libexec directory `eval echo $libexecdir`" +echo "include directory `eval echo $includedir`" +echo "config directory `eval echo $sysconfdir`" +if test "$mingw32" = "no" ; then +echo "local state directory `eval echo $local_statedir`" +echo "Manual directory `eval echo $mandir`" +echo "ELF interp prefix $interp_prefix" +else +echo "local state directory queried at runtime" +echo "Windows SDK $win_sdk" +fi +echo "Source path $source_path" +echo "C compiler $cc" +echo "Host C compiler $host_cc" +echo "C++ compiler $cxx" +echo "Objective-C compiler $objcc" +echo "ARFLAGS $ARFLAGS" +echo "CFLAGS $CFLAGS" +echo "QEMU_CFLAGS $QEMU_CFLAGS" +echo "LDFLAGS $LDFLAGS" +echo "make $make" +echo "install $install" +echo "python $python" +if test "$slirp" = "yes" ; then + echo "smbd $smbd" +fi +echo "module support $modules" +echo "host CPU $cpu" +echo "host big endian $bigendian" +echo "target list $target_list" +echo "tcg debug enabled $debug_tcg" +echo "gprof enabled $gprof" +echo "sparse enabled $sparse" +echo "strip binaries $strip_opt" +echo "profiler $profiler" +echo "static build $static" +if test "$darwin" = "yes" ; then + echo "Cocoa support $cocoa" +fi +echo "pixman $pixman" +echo "SDL support $sdl" +echo "GTK support $gtk" +echo "VTE support $vte" +echo "curses support $curses" +echo "curl support $curl" +echo "mingw32 support $mingw32" +echo "Audio drivers $audio_drv_list" +echo "Block whitelist (rw) $block_drv_rw_whitelist" +echo "Block whitelist (ro) $block_drv_ro_whitelist" +echo "VirtFS support $virtfs" +echo "VNC support $vnc" +if test "$vnc" = "yes" ; then + echo "VNC TLS support $vnc_tls" + echo "VNC SASL support $vnc_sasl" + echo "VNC JPEG support $vnc_jpeg" + echo "VNC PNG support $vnc_png" + echo "VNC WS support $vnc_ws" +fi +if test -n "$sparc_cpu"; then + echo "Target Sparc Arch $sparc_cpu" +fi +echo "xen support $xen" +if test "$xen" = "yes" ; then + echo "xen ctrl version $xen_ctrl_version" +fi +echo "brlapi support $brlapi" +echo "bluez support $bluez" +echo "Documentation $docs" +echo "GUEST_BASE $guest_base" +echo "PIE $pie" +echo "vde support $vde" +echo "netmap support $netmap" +echo "Linux AIO support $linux_aio" +echo "ATTR/XATTR support $attr" +echo "Install blobs $blobs" +echo "KVM support $kvm" +echo "RDMA support $rdma" +echo "TCG interpreter $tcg_interpreter" +echo "fdt support $fdt" +echo "preadv support $preadv" +echo "fdatasync $fdatasync" +echo "madvise $madvise" +echo "posix_madvise $posix_madvise" +echo "sigev_thread_id $sigev_thread_id" +echo "uuid support $uuid" +echo "libcap-ng support $cap_ng" +echo "vhost-net support $vhost_net" +echo "vhost-scsi support $vhost_scsi" +echo "Trace backends $trace_backends" +if test "$trace_backend" = "simple"; then +echo "Trace output file $trace_file-" +fi +if test "$spice" = "yes"; then +echo "spice support $spice ($spice_protocol_version/$spice_server_version)" +else +echo "spice support $spice" +fi +echo "rbd support $rbd" +echo "xfsctl support $xfs" +echo "nss used $smartcard_nss" +echo "libusb $libusb" +echo "usb net redir $usb_redir" +echo "OpenGL support $opengl" +echo "libiscsi support $libiscsi" +echo "libnfs support $libnfs" +echo "build guest agent $guest_agent" +echo "QGA VSS support $guest_agent_with_vss" +echo "seccomp support $seccomp" +echo "coroutine backend $coroutine" +echo "coroutine pool $coroutine_pool" +echo "GlusterFS support $glusterfs" +echo "Archipelago support $archipelago" +echo "gcov $gcov_tool" +echo "gcov enabled $gcov" +echo "TPM support $tpm" +echo "libssh2 support $libssh2" +echo "TPM passthrough $tpm_passthrough" +echo "QOM debugging $qom_cast_debug" +echo "vhdx $vhdx" +echo "Quorum $quorum" +echo "lzo support $lzo" +echo "snappy support $snappy" +echo "bzip2 support $bzip2" +echo "NUMA host support $numa" +echo "tcmalloc support $tcmalloc" + +if test "$sdl_too_old" = "yes"; then +echo "-> Your SDL version is too old - please upgrade to have SDL support" +fi + +config_host_mak="config-host.mak" + +echo "# Automatically generated by configure - do not modify" >config-all-disas.mak + +echo "# Automatically generated by configure - do not modify" > $config_host_mak +echo >> $config_host_mak + +echo all: >> $config_host_mak +echo "prefix=$prefix" >> $config_host_mak +echo "bindir=$bindir" >> $config_host_mak +echo "libdir=$libdir" >> $config_host_mak +echo "libexecdir=$libexecdir" >> $config_host_mak +echo "includedir=$includedir" >> $config_host_mak +echo "mandir=$mandir" >> $config_host_mak +echo "sysconfdir=$sysconfdir" >> $config_host_mak +echo "qemu_confdir=$qemu_confdir" >> $config_host_mak +echo "qemu_datadir=$qemu_datadir" >> $config_host_mak +echo "qemu_docdir=$qemu_docdir" >> $config_host_mak +echo "qemu_moddir=$qemu_moddir" >> $config_host_mak +if test "$mingw32" = "no" ; then + echo "qemu_localstatedir=$local_statedir" >> $config_host_mak +fi +echo "qemu_helperdir=$libexecdir" >> $config_host_mak +echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak +echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak +echo "qemu_localedir=$qemu_localedir" >> $config_host_mak +echo "libs_softmmu=$libs_softmmu" >> $config_host_mak + +echo "ARCH=$ARCH" >> $config_host_mak + +if test "$debug_tcg" = "yes" ; then + echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak +fi +if test "$strip_opt" = "yes" ; then + echo "STRIP=${strip}" >> $config_host_mak +fi +if test "$bigendian" = "yes" ; then + echo "HOST_WORDS_BIGENDIAN=y" >> $config_host_mak +fi +if test "$mingw32" = "yes" ; then + echo "CONFIG_WIN32=y" >> $config_host_mak + rc_version=`cat $source_path/VERSION` + version_major=${rc_version%%.*} + rc_version=${rc_version#*.} + version_minor=${rc_version%%.*} + rc_version=${rc_version#*.} + version_subminor=${rc_version%%.*} + version_micro=0 + echo "CONFIG_FILEVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak + echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak + if test "$guest_agent_with_vss" = "yes" ; then + echo "CONFIG_QGA_VSS=y" >> $config_host_mak + echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak + fi +else + echo "CONFIG_POSIX=y" >> $config_host_mak +fi + +if test "$linux" = "yes" ; then + echo "CONFIG_LINUX=y" >> $config_host_mak +fi + +if test "$darwin" = "yes" ; then + echo "CONFIG_DARWIN=y" >> $config_host_mak +fi + +if test "$aix" = "yes" ; then + echo "CONFIG_AIX=y" >> $config_host_mak +fi + +if test "$solaris" = "yes" ; then + echo "CONFIG_SOLARIS=y" >> $config_host_mak + echo "CONFIG_SOLARIS_VERSION=$solarisrev" >> $config_host_mak + if test "$needs_libsunmath" = "yes" ; then + echo "CONFIG_NEEDS_LIBSUNMATH=y" >> $config_host_mak + fi +fi +if test "$haiku" = "yes" ; then + echo "CONFIG_HAIKU=y" >> $config_host_mak +fi +if test "$static" = "yes" ; then + echo "CONFIG_STATIC=y" >> $config_host_mak +fi +if test "$profiler" = "yes" ; then + echo "CONFIG_PROFILER=y" >> $config_host_mak +fi +if test "$slirp" = "yes" ; then + echo "CONFIG_SLIRP=y" >> $config_host_mak + echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak +fi +if test "$vde" = "yes" ; then + echo "CONFIG_VDE=y" >> $config_host_mak +fi +if test "$netmap" = "yes" ; then + echo "CONFIG_NETMAP=y" >> $config_host_mak +fi +if test "$l2tpv3" = "yes" ; then + echo "CONFIG_L2TPV3=y" >> $config_host_mak +fi +if test "$cap_ng" = "yes" ; then + echo "CONFIG_LIBCAP=y" >> $config_host_mak +fi +echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak +for drv in $audio_drv_list; do + def=CONFIG_`echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]'` + echo "$def=y" >> $config_host_mak + if test "$drv" = "fmod"; then + echo "FMOD_CFLAGS=-I$fmod_inc" >> $config_host_mak + fi +done +if test "$audio_pt_int" = "yes" ; then + echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak +fi +if test "$audio_win_int" = "yes" ; then + echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak +fi +echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak +echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak +if test "$vnc" = "yes" ; then + echo "CONFIG_VNC=y" >> $config_host_mak +fi +if test "$vnc_tls" = "yes" ; then + echo "CONFIG_VNC_TLS=y" >> $config_host_mak +fi +if test "$vnc_sasl" = "yes" ; then + echo "CONFIG_VNC_SASL=y" >> $config_host_mak +fi +if test "$vnc_jpeg" = "yes" ; then + echo "CONFIG_VNC_JPEG=y" >> $config_host_mak +fi +if test "$vnc_png" = "yes" ; then + echo "CONFIG_VNC_PNG=y" >> $config_host_mak +fi +if test "$vnc_ws" = "yes" ; then + echo "CONFIG_VNC_WS=y" >> $config_host_mak + echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak +fi +if test "$fnmatch" = "yes" ; then + echo "CONFIG_FNMATCH=y" >> $config_host_mak +fi +if test "$uuid" = "yes" ; then + echo "CONFIG_UUID=y" >> $config_host_mak +fi +if test "$xfs" = "yes" ; then + echo "CONFIG_XFS=y" >> $config_host_mak +fi +qemu_version=`head $source_path/VERSION` +echo "VERSION=$qemu_version" >>$config_host_mak +echo "PKGVERSION=$pkgversion" >>$config_host_mak +echo "SRC_PATH=$source_path" >> $config_host_mak +echo "TARGET_DIRS=$target_list" >> $config_host_mak +if [ "$docs" = "yes" ] ; then + echo "BUILD_DOCS=yes" >> $config_host_mak +fi +if test "$modules" = "yes"; then + # $shacmd can generate a hash started with digit, which the compiler doesn't + # like as an symbol. So prefix it with an underscore + echo "CONFIG_STAMP=_`(echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ `" >> $config_host_mak + echo "CONFIG_MODULES=y" >> $config_host_mak +fi +if test "$sdl" = "yes" ; then + echo "CONFIG_SDL=y" >> $config_host_mak + echo "CONFIG_SDLABI=$sdlabi" >> $config_host_mak + echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak +fi +if test "$cocoa" = "yes" ; then + echo "CONFIG_COCOA=y" >> $config_host_mak +fi +if test "$curses" = "yes" ; then + echo "CONFIG_CURSES=y" >> $config_host_mak +fi +if test "$utimens" = "yes" ; then + echo "CONFIG_UTIMENSAT=y" >> $config_host_mak +fi +if test "$pipe2" = "yes" ; then + echo "CONFIG_PIPE2=y" >> $config_host_mak +fi +if test "$accept4" = "yes" ; then + echo "CONFIG_ACCEPT4=y" >> $config_host_mak +fi +if test "$splice" = "yes" ; then + echo "CONFIG_SPLICE=y" >> $config_host_mak +fi +if test "$eventfd" = "yes" ; then + echo "CONFIG_EVENTFD=y" >> $config_host_mak +fi +if test "$fallocate" = "yes" ; then + echo "CONFIG_FALLOCATE=y" >> $config_host_mak +fi +if test "$fallocate_punch_hole" = "yes" ; then + echo "CONFIG_FALLOCATE_PUNCH_HOLE=y" >> $config_host_mak +fi +if test "$fallocate_zero_range" = "yes" ; then + echo "CONFIG_FALLOCATE_ZERO_RANGE=y" >> $config_host_mak +fi +if test "$posix_fallocate" = "yes" ; then + echo "CONFIG_POSIX_FALLOCATE=y" >> $config_host_mak +fi +if test "$sync_file_range" = "yes" ; then + echo "CONFIG_SYNC_FILE_RANGE=y" >> $config_host_mak +fi +if test "$fiemap" = "yes" ; then + echo "CONFIG_FIEMAP=y" >> $config_host_mak +fi +if test "$dup3" = "yes" ; then + echo "CONFIG_DUP3=y" >> $config_host_mak +fi +if test "$ppoll" = "yes" ; then + echo "CONFIG_PPOLL=y" >> $config_host_mak +fi +if test "$prctl_pr_set_timerslack" = "yes" ; then + echo "CONFIG_PRCTL_PR_SET_TIMERSLACK=y" >> $config_host_mak +fi +if test "$epoll" = "yes" ; then + echo "CONFIG_EPOLL=y" >> $config_host_mak +fi +if test "$epoll_create1" = "yes" ; then + echo "CONFIG_EPOLL_CREATE1=y" >> $config_host_mak +fi +if test "$epoll_pwait" = "yes" ; then + echo "CONFIG_EPOLL_PWAIT=y" >> $config_host_mak +fi +if test "$sendfile" = "yes" ; then + echo "CONFIG_SENDFILE=y" >> $config_host_mak +fi +if test "$timerfd" = "yes" ; then + echo "CONFIG_TIMERFD=y" >> $config_host_mak +fi +if test "$setns" = "yes" ; then + echo "CONFIG_SETNS=y" >> $config_host_mak +fi +if test "$inotify" = "yes" ; then + echo "CONFIG_INOTIFY=y" >> $config_host_mak +fi +if test "$inotify1" = "yes" ; then + echo "CONFIG_INOTIFY1=y" >> $config_host_mak +fi +if test "$byteswap_h" = "yes" ; then + echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak +fi +if test "$bswap_h" = "yes" ; then + echo "CONFIG_MACHINE_BSWAP_H=y" >> $config_host_mak +fi +if test "$curl" = "yes" ; then + echo "CONFIG_CURL=m" >> $config_host_mak + echo "CURL_CFLAGS=$curl_cflags" >> $config_host_mak + echo "CURL_LIBS=$curl_libs" >> $config_host_mak +fi +if test "$brlapi" = "yes" ; then + echo "CONFIG_BRLAPI=y" >> $config_host_mak +fi +if test "$bluez" = "yes" ; then + echo "CONFIG_BLUEZ=y" >> $config_host_mak + echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak +fi +if test "glib_subprocess" = "yes" ; then + echo "CONFIG_HAS_GLIB_SUBPROCESS_TESTS=y" >> $config_host_mak +fi +echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak +if test "$gtk" = "yes" ; then + echo "CONFIG_GTK=y" >> $config_host_mak + echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak + echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak +fi +if test "$vte" = "yes" ; then + echo "CONFIG_VTE=y" >> $config_host_mak + echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak +fi +if test "$xen" = "yes" ; then + echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak + echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak +fi +if test "$linux_aio" = "yes" ; then + echo "CONFIG_LINUX_AIO=y" >> $config_host_mak +fi +if test "$attr" = "yes" ; then + echo "CONFIG_ATTR=y" >> $config_host_mak +fi +if test "$libattr" = "yes" ; then + echo "CONFIG_LIBATTR=y" >> $config_host_mak +fi +if test "$virtfs" = "yes" ; then + echo "CONFIG_VIRTFS=y" >> $config_host_mak +fi +if test "$vhost_scsi" = "yes" ; then + echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak +fi +if test "$vhost_net" = "yes" ; then + echo "CONFIG_VHOST_NET_USED=y" >> $config_host_mak +fi +if test "$blobs" = "yes" ; then + echo "INSTALL_BLOBS=yes" >> $config_host_mak +fi +if test "$iovec" = "yes" ; then + echo "CONFIG_IOVEC=y" >> $config_host_mak +fi +if test "$preadv" = "yes" ; then + echo "CONFIG_PREADV=y" >> $config_host_mak +fi +if test "$fdt" = "yes" ; then + echo "CONFIG_FDT=y" >> $config_host_mak +fi +if test "$signalfd" = "yes" ; then + echo "CONFIG_SIGNALFD=y" >> $config_host_mak +fi +if test "$tcg_interpreter" = "yes" ; then + echo "CONFIG_TCG_INTERPRETER=y" >> $config_host_mak +fi +if test "$fdatasync" = "yes" ; then + echo "CONFIG_FDATASYNC=y" >> $config_host_mak +fi +if test "$madvise" = "yes" ; then + echo "CONFIG_MADVISE=y" >> $config_host_mak +fi +if test "$posix_madvise" = "yes" ; then + echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak +fi +if test "$sigev_thread_id" = "yes" ; then + echo "CONFIG_SIGEV_THREAD_ID=y" >> $config_host_mak +fi + +if test "$spice" = "yes" ; then + echo "CONFIG_SPICE=y" >> $config_host_mak +fi + +if test "$smartcard_nss" = "yes" ; then + echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak + echo "NSS_LIBS=$nss_libs" >> $config_host_mak + echo "NSS_CFLAGS=$nss_cflags" >> $config_host_mak +fi + +if test "$libusb" = "yes" ; then + echo "CONFIG_USB_LIBUSB=y" >> $config_host_mak +fi + +if test "$usb_redir" = "yes" ; then + echo "CONFIG_USB_REDIR=y" >> $config_host_mak +fi + +if test "$opengl" = "yes" ; then + echo "CONFIG_OPENGL=y" >> $config_host_mak + echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak + echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak +fi + +if test "$lzo" = "yes" ; then + echo "CONFIG_LZO=y" >> $config_host_mak +fi + +if test "$snappy" = "yes" ; then + echo "CONFIG_SNAPPY=y" >> $config_host_mak +fi + +if test "$bzip2" = "yes" ; then + echo "CONFIG_BZIP2=y" >> $config_host_mak + echo "BZIP2_LIBS=-lbz2" >> $config_host_mak +fi + +if test "$libiscsi" = "yes" ; then + echo "CONFIG_LIBISCSI=m" >> $config_host_mak + echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak + echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak +fi + +if test "$libnfs" = "yes" ; then + echo "CONFIG_LIBNFS=y" >> $config_host_mak +fi + +if test "$seccomp" = "yes"; then + echo "CONFIG_SECCOMP=y" >> $config_host_mak +fi + +# XXX: suppress that +if [ "$bsd" = "yes" ] ; then + echo "CONFIG_BSD=y" >> $config_host_mak +fi + +if test "$zero_malloc" = "yes" ; then + echo "CONFIG_ZERO_MALLOC=y" >> $config_host_mak +fi +if test "$qom_cast_debug" = "yes" ; then + echo "CONFIG_QOM_CAST_DEBUG=y" >> $config_host_mak +fi +if test "$rbd" = "yes" ; then + echo "CONFIG_RBD=m" >> $config_host_mak + echo "RBD_CFLAGS=$rbd_cflags" >> $config_host_mak + echo "RBD_LIBS=$rbd_libs" >> $config_host_mak +fi + +echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak +if test "$coroutine_pool" = "yes" ; then + echo "CONFIG_COROUTINE_POOL=1" >> $config_host_mak +else + echo "CONFIG_COROUTINE_POOL=0" >> $config_host_mak +fi + +if test "$open_by_handle_at" = "yes" ; then + echo "CONFIG_OPEN_BY_HANDLE=y" >> $config_host_mak +fi + +if test "$linux_magic_h" = "yes" ; then + echo "CONFIG_LINUX_MAGIC_H=y" >> $config_host_mak +fi + +if test "$pragma_diagnostic_available" = "yes" ; then + echo "CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE=y" >> $config_host_mak +fi + +if test "$valgrind_h" = "yes" ; then + echo "CONFIG_VALGRIND_H=y" >> $config_host_mak +fi + +if test "$has_environ" = "yes" ; then + echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak +fi + +if test "$cpuid_h" = "yes" ; then + echo "CONFIG_CPUID_H=y" >> $config_host_mak +fi + +if test "$int128" = "yes" ; then + echo "CONFIG_INT128=y" >> $config_host_mak +fi + +if test "$getauxval" = "yes" ; then + echo "CONFIG_GETAUXVAL=y" >> $config_host_mak +fi + +if test "$glusterfs" = "yes" ; then + echo "CONFIG_GLUSTERFS=m" >> $config_host_mak + echo "GLUSTERFS_CFLAGS=$glusterfs_cflags" >> $config_host_mak + echo "GLUSTERFS_LIBS=$glusterfs_libs" >> $config_host_mak +fi + +if test "$glusterfs_discard" = "yes" ; then + echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak +fi + +if test "$glusterfs_zerofill" = "yes" ; then + echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak +fi + +if test "$archipelago" = "yes" ; then + echo "CONFIG_ARCHIPELAGO=m" >> $config_host_mak + echo "ARCHIPELAGO_LIBS=$archipelago_libs" >> $config_host_mak +fi + +if test "$libssh2" = "yes" ; then + echo "CONFIG_LIBSSH2=m" >> $config_host_mak + echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak + echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak +fi + +if test "$quorum" = "yes" ; then + echo "CONFIG_QUORUM=y" >> $config_host_mak +fi + +if test "$vhdx" = "yes" ; then + echo "CONFIG_VHDX=y" >> $config_host_mak +fi + +# USB host support +if test "$libusb" = "yes"; then + echo "HOST_USB=libusb legacy" >> $config_host_mak +else + echo "HOST_USB=stub" >> $config_host_mak +fi + +# TPM passthrough support? +if test "$tpm" = "yes"; then + echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak + if test "$tpm_passthrough" = "yes"; then + echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak + fi +fi + +echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak +if have_backend "nop"; then + echo "CONFIG_TRACE_NOP=y" >> $config_host_mak +fi +if have_backend "simple"; then + echo "CONFIG_TRACE_SIMPLE=y" >> $config_host_mak + # Set the appropriate trace file. + trace_file="\"$trace_file-\" FMT_pid" +fi +if have_backend "stderr"; then + echo "CONFIG_TRACE_STDERR=y" >> $config_host_mak +fi +if have_backend "ust"; then + echo "CONFIG_TRACE_UST=y" >> $config_host_mak +fi +if have_backend "dtrace"; then + echo "CONFIG_TRACE_DTRACE=y" >> $config_host_mak + if test "$trace_backend_stap" = "yes" ; then + echo "CONFIG_TRACE_SYSTEMTAP=y" >> $config_host_mak + fi +fi +if have_backend "ftrace"; then + if test "$linux" = "yes" ; then + echo "CONFIG_TRACE_FTRACE=y" >> $config_host_mak + else + feature_not_found "ftrace(trace backend)" "ftrace requires Linux" + fi +fi +echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak + +if test "$rdma" = "yes" ; then + echo "CONFIG_RDMA=y" >> $config_host_mak +fi + +# Hold two types of flag: +# CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on +# a thread we have a handle to +# CONFIG_PTHREAD_SETNAME_NP - A way of doing it on a particular +# platform +if test "$pthread_setname_np" = "yes" ; then + echo "CONFIG_THREAD_SETNAME_BYTHREAD=y" >> $config_host_mak + echo "CONFIG_PTHREAD_SETNAME_NP=y" >> $config_host_mak +fi + +if test "$tcg_interpreter" = "yes"; then + QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" +elif test "$ARCH" = "sparc64" ; then + QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/sparc $QEMU_INCLUDES" +elif test "$ARCH" = "s390x" ; then + QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/s390 $QEMU_INCLUDES" +elif test "$ARCH" = "x86_64" -o "$ARCH" = "x32" ; then + QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/i386 $QEMU_INCLUDES" +elif test "$ARCH" = "ppc64" ; then + QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/ppc $QEMU_INCLUDES" +else + QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES" +fi +QEMU_INCLUDES="-I\$(SRC_PATH)/tcg $QEMU_INCLUDES" + +echo "TOOLS=$tools" >> $config_host_mak +echo "ROMS=$roms" >> $config_host_mak +echo "MAKE=$make" >> $config_host_mak +echo "INSTALL=$install" >> $config_host_mak +echo "INSTALL_DIR=$install -d -m 0755" >> $config_host_mak +echo "INSTALL_DATA=$install -c -m 0644" >> $config_host_mak +if test -n "$libtool"; then + echo "INSTALL_PROG=\$(LIBTOOL) --mode=install $install -c -m 0755" >> $config_host_mak + echo "INSTALL_LIB=\$(LIBTOOL) --mode=install $install -c -m 0644" >> $config_host_mak +else + echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak + echo "INSTALL_LIB=$install -c -m 0644" >> $config_host_mak +fi +echo "PYTHON=$python" >> $config_host_mak +echo "CC=$cc" >> $config_host_mak +if $iasl -h > /dev/null 2>&1; then + echo "IASL=$iasl" >> $config_host_mak +fi +echo "CC_I386=$cc_i386" >> $config_host_mak +echo "HOST_CC=$host_cc" >> $config_host_mak +echo "CXX=$cxx" >> $config_host_mak +echo "OBJCC=$objcc" >> $config_host_mak +echo "AR=$ar" >> $config_host_mak +echo "ARFLAGS=$ARFLAGS" >> $config_host_mak +echo "AS=$as" >> $config_host_mak +echo "CPP=$cpp" >> $config_host_mak +echo "OBJCOPY=$objcopy" >> $config_host_mak +echo "LD=$ld" >> $config_host_mak +echo "NM=$nm" >> $config_host_mak +echo "WINDRES=$windres" >> $config_host_mak +echo "LIBTOOL=$libtool" >> $config_host_mak +echo "CFLAGS=$CFLAGS" >> $config_host_mak +echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak +echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak +echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak +if test "$sparse" = "yes" ; then + echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak + echo "CPP := REAL_CC=\"\$(CPP)\" cgcc" >> $config_host_mak + echo "CXX := REAL_CC=\"\$(CXX)\" cgcc" >> $config_host_mak + echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak + echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak +fi +if test "$cross_prefix" != ""; then + echo "AUTOCONF_HOST := --host=${cross_prefix%-}" >> $config_host_mak +else + echo "AUTOCONF_HOST := " >> $config_host_mak +fi +echo "LDFLAGS=$LDFLAGS" >> $config_host_mak +echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak +echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak +echo "LIBS+=$LIBS" >> $config_host_mak +echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak +echo "EXESUF=$EXESUF" >> $config_host_mak +echo "DSOSUF=$DSOSUF" >> $config_host_mak +echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak +echo "LIBS_QGA+=$libs_qga" >> $config_host_mak +echo "POD2MAN=$POD2MAN" >> $config_host_mak +echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak +if test "$gcov" = "yes" ; then + echo "CONFIG_GCOV=y" >> $config_host_mak + echo "GCOV=$gcov_tool" >> $config_host_mak +fi + +# use included Linux headers +if test "$linux" = "yes" ; then + mkdir -p linux-headers + case "$cpu" in + i386|x86_64|x32) + linux_arch=x86 + ;; + ppcemb|ppc|ppc64) + linux_arch=powerpc + ;; + s390x) + linux_arch=s390 + ;; + aarch64) + linux_arch=arm64 + ;; + mips64) + linux_arch=mips + ;; + *) + # For most CPUs the kernel architecture name and QEMU CPU name match. + linux_arch="$cpu" + ;; + esac + # For non-KVM architectures we will not have asm headers + if [ -e "$source_path/linux-headers/asm-$linux_arch" ]; then + symlink "$source_path/linux-headers/asm-$linux_arch" linux-headers/asm + fi +fi + +for target in $target_list; do +target_dir="$target" +config_target_mak=$target_dir/config-target.mak +target_name=`echo $target | cut -d '-' -f 1` +target_bigendian="no" + +case "$target_name" in + armeb|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or32|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb) + target_bigendian=yes + ;; +esac +target_softmmu="no" +target_user_only="no" +target_linux_user="no" +target_bsd_user="no" +case "$target" in + ${target_name}-softmmu) + target_softmmu="yes" + ;; + ${target_name}-linux-user) + if test "$linux" != "yes" ; then + error_exit "Target '$target' is only available on a Linux host" + fi + target_user_only="yes" + target_linux_user="yes" + ;; + ${target_name}-bsd-user) + if test "$bsd" != "yes" ; then + error_exit "Target '$target' is only available on a BSD host" + fi + target_user_only="yes" + target_bsd_user="yes" + ;; + *) + error_exit "Target '$target' not recognised" + exit 1 + ;; +esac + +mkdir -p $target_dir +echo "# Automatically generated by configure - do not modify" > $config_target_mak + +bflt="no" +interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_name/g"` +gdb_xml_files="" + +TARGET_ARCH="$target_name" +TARGET_BASE_ARCH="" +TARGET_ABI_DIR="" + +case "$target_name" in + i386) + ;; + x86_64) + TARGET_BASE_ARCH=i386 + ;; + alpha) + ;; + arm|armeb) + TARGET_ARCH=arm + bflt="yes" + gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" + ;; + aarch64) + TARGET_BASE_ARCH=arm + bflt="yes" + gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" + ;; + cris) + ;; + lm32) + ;; + m68k) + bflt="yes" + gdb_xml_files="cf-core.xml cf-fp.xml" + ;; + microblaze|microblazeel) + TARGET_ARCH=microblaze + bflt="yes" + ;; + mips|mipsel) + TARGET_ARCH=mips + echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak + ;; + mipsn32|mipsn32el) + TARGET_ARCH=mips64 + TARGET_BASE_ARCH=mips + echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak + echo "TARGET_ABI32=y" >> $config_target_mak + ;; + mips64|mips64el) + TARGET_ARCH=mips64 + TARGET_BASE_ARCH=mips + echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak + ;; + moxie) + ;; + or32) + TARGET_ARCH=openrisc + TARGET_BASE_ARCH=openrisc + ;; + ppc) + gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + ;; + ppcemb) + TARGET_BASE_ARCH=ppc + TARGET_ABI_DIR=ppc + gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + ;; + ppc64) + TARGET_BASE_ARCH=ppc + TARGET_ABI_DIR=ppc + gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + ;; + ppc64le) + TARGET_ARCH=ppc64 + TARGET_BASE_ARCH=ppc + TARGET_ABI_DIR=ppc + gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + ;; + ppc64abi32) + TARGET_ARCH=ppc64 + TARGET_BASE_ARCH=ppc + TARGET_ABI_DIR=ppc + echo "TARGET_ABI32=y" >> $config_target_mak + gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + ;; + sh4|sh4eb) + TARGET_ARCH=sh4 + bflt="yes" + ;; + sparc) + ;; + sparc64) + TARGET_BASE_ARCH=sparc + ;; + sparc32plus) + TARGET_ARCH=sparc64 + TARGET_BASE_ARCH=sparc + TARGET_ABI_DIR=sparc + echo "TARGET_ABI32=y" >> $config_target_mak + ;; + s390x) + gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml" + ;; + tricore) + ;; + unicore32) + ;; + xtensa|xtensaeb) + TARGET_ARCH=xtensa + ;; + *) + error_exit "Unsupported target CPU" + ;; +esac +# TARGET_BASE_ARCH needs to be defined after TARGET_ARCH +if [ "$TARGET_BASE_ARCH" = "" ]; then + TARGET_BASE_ARCH=$TARGET_ARCH +fi + +symlink "$source_path/Makefile.target" "$target_dir/Makefile" + +upper() { + echo "$@"| LC_ALL=C tr '[a-z]' '[A-Z]' +} + +target_arch_name="`upper $TARGET_ARCH`" +echo "TARGET_$target_arch_name=y" >> $config_target_mak +echo "TARGET_NAME=$target_name" >> $config_target_mak +echo "TARGET_BASE_ARCH=$TARGET_BASE_ARCH" >> $config_target_mak +if [ "$TARGET_ABI_DIR" = "" ]; then + TARGET_ABI_DIR=$TARGET_ARCH +fi +echo "TARGET_ABI_DIR=$TARGET_ABI_DIR" >> $config_target_mak +if [ "$HOST_VARIANT_DIR" != "" ]; then + echo "HOST_VARIANT_DIR=$HOST_VARIANT_DIR" >> $config_target_mak +fi +case "$target_name" in + i386|x86_64) + if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then + echo "CONFIG_XEN=y" >> $config_target_mak + if test "$xen_pci_passthrough" = yes; then + echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak" + fi + fi + ;; + *) +esac +case "$target_name" in + aarch64|arm|i386|x86_64|ppcemb|ppc|ppc64|s390x|mipsel|mips) + # Make sure the target and host cpus are compatible + if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \ + \( "$target_name" = "$cpu" -o \ + \( "$target_name" = "ppcemb" -a "$cpu" = "ppc" \) -o \ + \( "$target_name" = "ppc64" -a "$cpu" = "ppc" \) -o \ + \( "$target_name" = "ppc" -a "$cpu" = "ppc64" \) -o \ + \( "$target_name" = "ppcemb" -a "$cpu" = "ppc64" \) -o \ + \( "$target_name" = "mipsel" -a "$cpu" = "mips" \) -o \ + \( "$target_name" = "x86_64" -a "$cpu" = "i386" \) -o \ + \( "$target_name" = "i386" -a "$cpu" = "x86_64" \) -o \ + \( "$target_name" = "x86_64" -a "$cpu" = "x32" \) -o \ + \( "$target_name" = "i386" -a "$cpu" = "x32" \) \) ; then + echo "CONFIG_KVM=y" >> $config_target_mak + if test "$vhost_net" = "yes" ; then + echo "CONFIG_VHOST_NET=y" >> $config_target_mak + fi + fi +esac +if test "$target_bigendian" = "yes" ; then + echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak +fi +if test "$target_softmmu" = "yes" ; then + echo "CONFIG_SOFTMMU=y" >> $config_target_mak +fi +if test "$target_user_only" = "yes" ; then + echo "CONFIG_USER_ONLY=y" >> $config_target_mak + echo "CONFIG_QEMU_INTERP_PREFIX=\"$interp_prefix1\"" >> $config_target_mak +fi +if test "$target_linux_user" = "yes" ; then + echo "CONFIG_LINUX_USER=y" >> $config_target_mak +fi +list="" +if test ! -z "$gdb_xml_files" ; then + for x in $gdb_xml_files; do + list="$list $source_path/gdb-xml/$x" + done + echo "TARGET_XML_FILES=$list" >> $config_target_mak +fi + +if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then + echo "TARGET_HAS_BFLT=y" >> $config_target_mak +fi +if test "$target_user_only" = "yes" -a "$guest_base" = "yes"; then + echo "CONFIG_USE_GUEST_BASE=y" >> $config_target_mak +fi +if test "$target_bsd_user" = "yes" ; then + echo "CONFIG_BSD_USER=y" >> $config_target_mak +fi + +# generate QEMU_CFLAGS/LDFLAGS for targets + +cflags="" +ldflags="" + +for i in $ARCH $TARGET_BASE_ARCH ; do + case "$i" in + alpha) + echo "CONFIG_ALPHA_DIS=y" >> $config_target_mak + echo "CONFIG_ALPHA_DIS=y" >> config-all-disas.mak + ;; + aarch64) + if test -n "${cxx}"; then + echo "CONFIG_ARM_A64_DIS=y" >> $config_target_mak + echo "CONFIG_ARM_A64_DIS=y" >> config-all-disas.mak + fi + ;; + arm) + echo "CONFIG_ARM_DIS=y" >> $config_target_mak + echo "CONFIG_ARM_DIS=y" >> config-all-disas.mak + if test -n "${cxx}"; then + echo "CONFIG_ARM_A64_DIS=y" >> $config_target_mak + echo "CONFIG_ARM_A64_DIS=y" >> config-all-disas.mak + fi + ;; + cris) + echo "CONFIG_CRIS_DIS=y" >> $config_target_mak + echo "CONFIG_CRIS_DIS=y" >> config-all-disas.mak + ;; + hppa) + echo "CONFIG_HPPA_DIS=y" >> $config_target_mak + echo "CONFIG_HPPA_DIS=y" >> config-all-disas.mak + ;; + i386|x86_64|x32) + echo "CONFIG_I386_DIS=y" >> $config_target_mak + echo "CONFIG_I386_DIS=y" >> config-all-disas.mak + ;; + ia64*) + echo "CONFIG_IA64_DIS=y" >> $config_target_mak + echo "CONFIG_IA64_DIS=y" >> config-all-disas.mak + ;; + lm32) + echo "CONFIG_LM32_DIS=y" >> $config_target_mak + echo "CONFIG_LM32_DIS=y" >> config-all-disas.mak + ;; + m68k) + echo "CONFIG_M68K_DIS=y" >> $config_target_mak + echo "CONFIG_M68K_DIS=y" >> config-all-disas.mak + ;; + microblaze*) + echo "CONFIG_MICROBLAZE_DIS=y" >> $config_target_mak + echo "CONFIG_MICROBLAZE_DIS=y" >> config-all-disas.mak + ;; + mips*) + echo "CONFIG_MIPS_DIS=y" >> $config_target_mak + echo "CONFIG_MIPS_DIS=y" >> config-all-disas.mak + ;; + moxie*) + echo "CONFIG_MOXIE_DIS=y" >> $config_target_mak + echo "CONFIG_MOXIE_DIS=y" >> config-all-disas.mak + ;; + or32) + echo "CONFIG_OPENRISC_DIS=y" >> $config_target_mak + echo "CONFIG_OPENRISC_DIS=y" >> config-all-disas.mak + ;; + ppc*) + echo "CONFIG_PPC_DIS=y" >> $config_target_mak + echo "CONFIG_PPC_DIS=y" >> config-all-disas.mak + ;; + s390*) + echo "CONFIG_S390_DIS=y" >> $config_target_mak + echo "CONFIG_S390_DIS=y" >> config-all-disas.mak + ;; + sh4) + echo "CONFIG_SH4_DIS=y" >> $config_target_mak + echo "CONFIG_SH4_DIS=y" >> config-all-disas.mak + ;; + sparc*) + echo "CONFIG_SPARC_DIS=y" >> $config_target_mak + echo "CONFIG_SPARC_DIS=y" >> config-all-disas.mak + ;; + xtensa*) + echo "CONFIG_XTENSA_DIS=y" >> $config_target_mak + echo "CONFIG_XTENSA_DIS=y" >> config-all-disas.mak + ;; + esac +done +if test "$tcg_interpreter" = "yes" ; then + echo "CONFIG_TCI_DIS=y" >> $config_target_mak + echo "CONFIG_TCI_DIS=y" >> config-all-disas.mak +fi + +case "$ARCH" in +alpha) + # Ensure there's only a single GP + cflags="-msmall-data $cflags" +;; +esac + +if test "$gprof" = "yes" ; then + echo "TARGET_GPROF=yes" >> $config_target_mak + if test "$target_linux_user" = "yes" ; then + cflags="-p $cflags" + ldflags="-p $ldflags" + fi + if test "$target_softmmu" = "yes" ; then + ldflags="-p $ldflags" + echo "GPROF_CFLAGS=-p" >> $config_target_mak + fi +fi + +if test "$target_linux_user" = "yes" -o "$target_bsd_user" = "yes" ; then + ldflags="$ldflags $textseg_ldflags" +fi + +echo "LDFLAGS+=$ldflags" >> $config_target_mak +echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak + +done # for target in $targets + +if [ "$pixman" = "internal" ]; then + echo "config-host.h: subdir-pixman" >> $config_host_mak +fi + +if test "$rdma" = "yes" ; then +echo "CONFIG_RDMA=y" >> $config_host_mak +fi + +if [ "$dtc_internal" = "yes" ]; then + echo "config-host.h: subdir-dtc" >> $config_host_mak +fi + +if test "$numa" = "yes"; then + echo "CONFIG_NUMA=y" >> $config_host_mak +fi + +if test "$ccache_cpp2" = "yes"; then + echo "export CCACHE_CPP2=y" >> $config_host_mak +fi + +# build tree in object directory in case the source is not in the current directory +DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests" +DIRS="$DIRS fsdev" +DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" +DIRS="$DIRS roms/seabios roms/vgabios" +DIRS="$DIRS qapi-generated" +FILES="Makefile tests/tcg/Makefile qdict-test-data.txt" +FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit" +FILES="$FILES tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile" +FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" +FILES="$FILES pc-bios/spapr-rtas/Makefile" +FILES="$FILES pc-bios/s390-ccw/Makefile" +FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" +FILES="$FILES pc-bios/qemu-icon.bmp" +for bios_file in \ + $source_path/pc-bios/*.bin \ + $source_path/pc-bios/*.aml \ + $source_path/pc-bios/*.rom \ + $source_path/pc-bios/*.dtb \ + $source_path/pc-bios/*.img \ + $source_path/pc-bios/openbios-* \ + $source_path/pc-bios/u-boot.* \ + $source_path/pc-bios/palcode-* +do + FILES="$FILES pc-bios/`basename $bios_file`" +done +for test_file in `find $source_path/tests/acpi-test-data -type f` +do + FILES="$FILES tests/acpi-test-data`echo $test_file | sed -e 's/.*acpi-test-data//'`" +done +mkdir -p $DIRS +for f in $FILES ; do + if [ -e "$source_path/$f" ] && [ "$pwd_is_source_path" != "y" ]; then + symlink "$source_path/$f" "$f" + fi +done + +# temporary config to build submodules +for rom in seabios vgabios ; do + config_mak=roms/$rom/config.mak + echo "# Automatically generated by configure - do not modify" > $config_mak + echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak + echo "AS=$as" >> $config_mak + echo "CC=$cc" >> $config_mak + echo "BCC=bcc" >> $config_mak + echo "CPP=$cpp" >> $config_mak + echo "OBJCOPY=objcopy" >> $config_mak + echo "IASL=$iasl" >> $config_mak + echo "LD=$ld" >> $config_mak +done + +# set up qemu-iotests in this build directory +iotests_common_env="tests/qemu-iotests/common.env" +iotests_check="tests/qemu-iotests/check" + +echo "# Automatically generated by configure - do not modify" > "$iotests_common_env" +echo >> "$iotests_common_env" +echo "export PYTHON='$python'" >> "$iotests_common_env" + +if [ ! -e "$iotests_check" ]; then + symlink "$source_path/$iotests_check" "$iotests_check" +fi + +# Save the configure command line for later reuse. +cat <config.status +#!/bin/sh +# Generated by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. +EOD +printf "exec" >>config.status +printf " '%s'" "$0" "$@" >>config.status +echo >>config.status +chmod +x config.status + +rm -r "$TMPDIR1" diff --git a/coroutine-gthread.c b/coroutine-gthread.c new file mode 100644 index 0000000..6bd6d6b --- /dev/null +++ b/coroutine-gthread.c @@ -0,0 +1,198 @@ +/* + * GThread coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Aneesh Kumar K.V + * + * 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.0 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 . + */ + +#include +#include "qemu-common.h" +#include "block/coroutine_int.h" + +typedef struct { + Coroutine base; + GThread *thread; + bool runnable; + bool free_on_thread_exit; + CoroutineAction action; +} CoroutineGThread; + +static CompatGMutex coroutine_lock; +static CompatGCond coroutine_cond; + +/* GLib 2.31 and beyond deprecated various parts of the thread API, + * but the new interfaces are not available in older GLib versions + * so we have to cope with both. + */ +#if GLIB_CHECK_VERSION(2, 31, 0) +/* Awkwardly, the GPrivate API doesn't provide a way to update the + * GDestroyNotify handler for the coroutine key dynamically. So instead + * we track whether or not the CoroutineGThread should be freed on + * thread exit / coroutine key update using the free_on_thread_exit + * field. + */ +static void coroutine_destroy_notify(gpointer data) +{ + CoroutineGThread *co = data; + if (co && co->free_on_thread_exit) { + g_free(co); + } +} + +static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify); + +static inline CoroutineGThread *get_coroutine_key(void) +{ + return g_private_get(&coroutine_key); +} + +static inline void set_coroutine_key(CoroutineGThread *co, + bool free_on_thread_exit) +{ + /* Unlike g_static_private_set() this does not call the GDestroyNotify + * if the previous value of the key was NULL. Fortunately we only need + * the GDestroyNotify in the non-NULL key case. + */ + co->free_on_thread_exit = free_on_thread_exit; + g_private_replace(&coroutine_key, co); +} + +static inline GThread *create_thread(GThreadFunc func, gpointer data) +{ + return g_thread_new("coroutine", func, data); +} + +#else + +/* Handle older GLib versions */ + +static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT; + +static inline CoroutineGThread *get_coroutine_key(void) +{ + return g_static_private_get(&coroutine_key); +} + +static inline void set_coroutine_key(CoroutineGThread *co, + bool free_on_thread_exit) +{ + g_static_private_set(&coroutine_key, co, + free_on_thread_exit ? (GDestroyNotify)g_free : NULL); +} + +static inline GThread *create_thread(GThreadFunc func, gpointer data) +{ + return g_thread_create_full(func, data, 0, TRUE, TRUE, + G_THREAD_PRIORITY_NORMAL, NULL); +} + +#endif + + +static void __attribute__((constructor)) coroutine_init(void) +{ +#if !GLIB_CHECK_VERSION(2, 31, 0) + if (!g_thread_supported()) { + g_thread_init(NULL); + } +#endif +} + +static void coroutine_wait_runnable_locked(CoroutineGThread *co) +{ + while (!co->runnable) { + g_cond_wait(&coroutine_cond, &coroutine_lock); + } +} + +static void coroutine_wait_runnable(CoroutineGThread *co) +{ + g_mutex_lock(&coroutine_lock); + coroutine_wait_runnable_locked(co); + g_mutex_unlock(&coroutine_lock); +} + +static gpointer coroutine_thread(gpointer opaque) +{ + CoroutineGThread *co = opaque; + + set_coroutine_key(co, false); + coroutine_wait_runnable(co); + co->base.entry(co->base.entry_arg); + qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); + return NULL; +} + +Coroutine *qemu_coroutine_new(void) +{ + CoroutineGThread *co; + + co = g_malloc0(sizeof(*co)); + co->thread = create_thread(coroutine_thread, co); + if (!co->thread) { + g_free(co); + return NULL; + } + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); + + g_thread_join(co->thread); + g_free(co); +} + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, + Coroutine *to_, + CoroutineAction action) +{ + CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); + CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); + + g_mutex_lock(&coroutine_lock); + from->runnable = false; + from->action = action; + to->runnable = true; + to->action = action; + g_cond_broadcast(&coroutine_cond); + + if (action != COROUTINE_TERMINATE) { + coroutine_wait_runnable_locked(from); + } + g_mutex_unlock(&coroutine_lock); + return from->action; +} + +Coroutine *qemu_coroutine_self(void) +{ + CoroutineGThread *co = get_coroutine_key(); + if (!co) { + co = g_malloc0(sizeof(*co)); + co->runnable = true; + set_coroutine_key(co, true); + } + + return &co->base; +} + +bool qemu_in_coroutine(void) +{ + CoroutineGThread *co = get_coroutine_key(); + + return co && co->base.caller; +} diff --git a/coroutine-sigaltstack.c b/coroutine-sigaltstack.c new file mode 100644 index 0000000..63519ff --- /dev/null +++ b/coroutine-sigaltstack.c @@ -0,0 +1,293 @@ +/* + * sigaltstack coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Kevin Wolf + * Copyright (C) 2012 Alex Barcelo +** This file is partly based on pth_mctx.c, from the GNU Portable Threads +** Copyright (c) 1999-2006 Ralf S. Engelschall + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ +#ifdef _FORTIFY_SOURCE +#undef _FORTIFY_SOURCE +#endif +#include +#include +#include +#include +#include +#include "qemu-common.h" +#include "block/coroutine_int.h" + +typedef struct { + Coroutine base; + void *stack; + sigjmp_buf env; +} CoroutineUContext; + +/** + * Per-thread coroutine bookkeeping + */ +typedef struct { + /** Currently executing coroutine */ + Coroutine *current; + + /** The default coroutine */ + CoroutineUContext leader; + + /** Information for the signal handler (trampoline) */ + sigjmp_buf tr_reenter; + volatile sig_atomic_t tr_called; + void *tr_handler; +} CoroutineThreadState; + +static pthread_key_t thread_state_key; + +static CoroutineThreadState *coroutine_get_thread_state(void) +{ + CoroutineThreadState *s = pthread_getspecific(thread_state_key); + + if (!s) { + s = g_malloc0(sizeof(*s)); + s->current = &s->leader.base; + pthread_setspecific(thread_state_key, s); + } + return s; +} + +static void qemu_coroutine_thread_cleanup(void *opaque) +{ + CoroutineThreadState *s = opaque; + + g_free(s); +} + +static void __attribute__((constructor)) coroutine_init(void) +{ + int ret; + + ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup); + if (ret != 0) { + fprintf(stderr, "unable to create leader key: %s\n", strerror(errno)); + abort(); + } +} + +/* "boot" function + * This is what starts the coroutine, is called from the trampoline + * (from the signal handler when it is not signal handling, read ahead + * for more information). + */ +static void coroutine_bootstrap(CoroutineUContext *self, Coroutine *co) +{ + /* Initialize longjmp environment and switch back the caller */ + if (!sigsetjmp(self->env, 0)) { + siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); + } + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +/* + * This is used as the signal handler. This is called with the brand new stack + * (thanks to sigaltstack). We have to return, given that this is a signal + * handler and the sigmask and some other things are changed. + */ +static void coroutine_trampoline(int signal) +{ + CoroutineUContext *self; + Coroutine *co; + CoroutineThreadState *coTS; + + /* Get the thread specific information */ + coTS = coroutine_get_thread_state(); + self = coTS->tr_handler; + coTS->tr_called = 1; + co = &self->base; + + /* + * Here we have to do a bit of a ping pong between the caller, given that + * this is a signal handler and we have to do a return "soon". Then the + * caller can reestablish everything and do a siglongjmp here again. + */ + if (!sigsetjmp(coTS->tr_reenter, 0)) { + return; + } + + /* + * Ok, the caller has siglongjmp'ed back to us, so now prepare + * us for the real machine state switching. We have to jump + * into another function here to get a new stack context for + * the auto variables (which have to be auto-variables + * because the start of the thread happens later). Else with + * PIC (i.e. Position Independent Code which is used when PTH + * is built as a shared library) most platforms would + * horrible core dump as experience showed. + */ + coroutine_bootstrap(self, co); +} + +Coroutine *qemu_coroutine_new(void) +{ + const size_t stack_size = 1 << 20; + CoroutineUContext *co; + CoroutineThreadState *coTS; + struct sigaction sa; + struct sigaction osa; + stack_t ss; + stack_t oss; + sigset_t sigs; + sigset_t osigs; + sigjmp_buf old_env; + + /* The way to manipulate stack is with the sigaltstack function. We + * prepare a stack, with it delivering a signal to ourselves and then + * put sigsetjmp/siglongjmp where needed. + * This has been done keeping coroutine-ucontext as a model and with the + * pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics + * of the coroutines and see pth_mctx.c (from the pth project) for the + * sigaltstack way of manipulating stacks. + */ + + co = g_malloc0(sizeof(*co)); + co->stack = g_malloc(stack_size); + co->base.entry_arg = &old_env; /* stash away our jmp_buf */ + + coTS = coroutine_get_thread_state(); + coTS->tr_handler = co; + + /* + * Preserve the SIGUSR2 signal state, block SIGUSR2, + * and establish our signal handler. The signal will + * later transfer control onto the signal stack. + */ + sigemptyset(&sigs); + sigaddset(&sigs, SIGUSR2); + pthread_sigmask(SIG_BLOCK, &sigs, &osigs); + sa.sa_handler = coroutine_trampoline; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK; + if (sigaction(SIGUSR2, &sa, &osa) != 0) { + abort(); + } + + /* + * Set the new stack. + */ + ss.ss_sp = co->stack; + ss.ss_size = stack_size; + ss.ss_flags = 0; + if (sigaltstack(&ss, &oss) < 0) { + abort(); + } + + /* + * Now transfer control onto the signal stack and set it up. + * It will return immediately via "return" after the sigsetjmp() + * was performed. Be careful here with race conditions. The + * signal can be delivered the first time sigsuspend() is + * called. + */ + coTS->tr_called = 0; + pthread_kill(pthread_self(), SIGUSR2); + sigfillset(&sigs); + sigdelset(&sigs, SIGUSR2); + while (!coTS->tr_called) { + sigsuspend(&sigs); + } + + /* + * Inform the system that we are back off the signal stack by + * removing the alternative signal stack. Be careful here: It + * first has to be disabled, before it can be removed. + */ + sigaltstack(NULL, &ss); + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) < 0) { + abort(); + } + sigaltstack(NULL, &ss); + if (!(oss.ss_flags & SS_DISABLE)) { + sigaltstack(&oss, NULL); + } + + /* + * Restore the old SIGUSR2 signal handler and mask + */ + sigaction(SIGUSR2, &osa, NULL); + pthread_sigmask(SIG_SETMASK, &osigs, NULL); + + /* + * Now enter the trampoline again, but this time not as a signal + * handler. Instead we jump into it directly. The functionally + * redundant ping-pong pointer arithmetic is necessary to avoid + * type-conversion warnings related to the `volatile' qualifier and + * the fact that `jmp_buf' usually is an array type. + */ + if (!sigsetjmp(old_env, 0)) { + siglongjmp(coTS->tr_reenter, 1); + } + + /* + * Ok, we returned again, so now we're finished + */ + + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); + + g_free(co->stack); + g_free(co); +} + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); + CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); + CoroutineThreadState *s = coroutine_get_thread_state(); + int ret; + + s->current = to_; + + ret = sigsetjmp(from->env, 0); + if (ret == 0) { + siglongjmp(to->env, action); + } + return ret; +} + +Coroutine *qemu_coroutine_self(void) +{ + CoroutineThreadState *s = coroutine_get_thread_state(); + + return s->current; +} + +bool qemu_in_coroutine(void) +{ + CoroutineThreadState *s = pthread_getspecific(thread_state_key); + + return s && s->current->caller; +} + diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c new file mode 100644 index 0000000..259fcb4 --- /dev/null +++ b/coroutine-ucontext.c @@ -0,0 +1,194 @@ +/* + * ucontext coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Kevin Wolf + * + * 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.0 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 . + */ + +/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ +#ifdef _FORTIFY_SOURCE +#undef _FORTIFY_SOURCE +#endif +#include +#include +#include +#include +#include "qemu-common.h" +#include "block/coroutine_int.h" + +#ifdef CONFIG_VALGRIND_H +#include +#endif + +typedef struct { + Coroutine base; + void *stack; + sigjmp_buf env; + +#ifdef CONFIG_VALGRIND_H + unsigned int valgrind_stack_id; +#endif + +} CoroutineUContext; + +/** + * Per-thread coroutine bookkeeping + */ +static __thread CoroutineUContext leader; +static __thread Coroutine *current; + +/* + * va_args to makecontext() must be type 'int', so passing + * the pointer we need may require several int args. This + * union is a quick hack to let us do that + */ +union cc_arg { + void *p; + int i[2]; +}; + +static void coroutine_trampoline(int i0, int i1) +{ + union cc_arg arg; + CoroutineUContext *self; + Coroutine *co; + + arg.i[0] = i0; + arg.i[1] = i1; + self = arg.p; + co = &self->base; + + /* Initialize longjmp environment and switch back the caller */ + if (!sigsetjmp(self->env, 0)) { + siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); + } + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +Coroutine *qemu_coroutine_new(void) +{ + const size_t stack_size = 1 << 20; + CoroutineUContext *co; + ucontext_t old_uc, uc; + sigjmp_buf old_env; + union cc_arg arg = {0}; + + /* The ucontext functions preserve signal masks which incurs a + * system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not + * preserve signal masks but only works on the current stack. + * Since we need a way to create and switch to a new stack, use + * the ucontext functions for that but sigsetjmp()/siglongjmp() for + * everything else. + */ + + if (getcontext(&uc) == -1) { + abort(); + } + + co = g_malloc0(sizeof(*co)); + co->stack = g_malloc(stack_size); + co->base.entry_arg = &old_env; /* stash away our jmp_buf */ + + uc.uc_link = &old_uc; + uc.uc_stack.ss_sp = co->stack; + uc.uc_stack.ss_size = stack_size; + uc.uc_stack.ss_flags = 0; + +#ifdef CONFIG_VALGRIND_H + co->valgrind_stack_id = + VALGRIND_STACK_REGISTER(co->stack, co->stack + stack_size); +#endif + + arg.p = co; + + makecontext(&uc, (void (*)(void))coroutine_trampoline, + 2, arg.i[0], arg.i[1]); + + /* swapcontext() in, siglongjmp() back out */ + if (!sigsetjmp(old_env, 0)) { + swapcontext(&old_uc, &uc); + } + return &co->base; +} + +#ifdef CONFIG_VALGRIND_H +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +/* Work around an unused variable in the valgrind.h macro... */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif +static inline void valgrind_stack_deregister(CoroutineUContext *co) +{ + VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); +} +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +#pragma GCC diagnostic pop +#endif +#endif + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); + +#ifdef CONFIG_VALGRIND_H + valgrind_stack_deregister(co); +#endif + + g_free(co->stack); + g_free(co); +} + +/* This function is marked noinline to prevent GCC from inlining it + * into coroutine_trampoline(). If we allow it to do that then it + * hoists the code to get the address of the TLS variable "current" + * out of the while() loop. This is an invalid transformation because + * the sigsetjmp() call may be called when running thread A but + * return in thread B, and so we might be in a different thread + * context each time round the loop. + */ +CoroutineAction __attribute__((noinline)) +qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); + CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); + int ret; + + current = to_; + + ret = sigsetjmp(from->env, 0); + if (ret == 0) { + siglongjmp(to->env, action); + } + return ret; +} + +Coroutine *qemu_coroutine_self(void) +{ + if (!current) { + current = &leader.base; + } + return current; +} + +bool qemu_in_coroutine(void) +{ + return current && current->caller; +} diff --git a/coroutine-win32.c b/coroutine-win32.c new file mode 100644 index 0000000..17ace37 --- /dev/null +++ b/coroutine-win32.c @@ -0,0 +1,101 @@ +/* + * Win32 coroutine initialization code + * + * Copyright (c) 2011 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "block/coroutine_int.h" + +typedef struct +{ + Coroutine base; + + LPVOID fiber; + CoroutineAction action; +} CoroutineWin32; + +static __thread CoroutineWin32 leader; +static __thread Coroutine *current; + +/* This function is marked noinline to prevent GCC from inlining it + * into coroutine_trampoline(). If we allow it to do that then it + * hoists the code to get the address of the TLS variable "current" + * out of the while() loop. This is an invalid transformation because + * the SwitchToFiber() call may be called when running thread A but + * return in thread B, and so we might be in a different thread + * context each time round the loop. + */ +CoroutineAction __attribute__((noinline)) +qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_); + CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_); + + current = to_; + + to->action = action; + SwitchToFiber(to->fiber); + return from->action; +} + +static void CALLBACK coroutine_trampoline(void *co_) +{ + Coroutine *co = co_; + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +Coroutine *qemu_coroutine_new(void) +{ + const size_t stack_size = 1 << 20; + CoroutineWin32 *co; + + co = g_malloc0(sizeof(*co)); + co->fiber = CreateFiber(stack_size, coroutine_trampoline, &co->base); + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineWin32 *co = DO_UPCAST(CoroutineWin32, base, co_); + + DeleteFiber(co->fiber); + g_free(co); +} + +Coroutine *qemu_coroutine_self(void) +{ + if (!current) { + current = &leader.base; + leader.fiber = ConvertThreadToFiber(NULL); + } + return current; +} + +bool qemu_in_coroutine(void) +{ + return current && current->caller; +} diff --git a/cpu-exec.c b/cpu-exec.c new file mode 100644 index 0000000..2ffeb6e --- /dev/null +++ b/cpu-exec.c @@ -0,0 +1,588 @@ +/* + * emulator main execution loop + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + */ +#include "config.h" +#include "cpu.h" +#include "trace.h" +#include "disas/disas.h" +#include "tcg.h" +#include "qemu/atomic.h" +#include "sysemu/qtest.h" +#include "qemu/timer.h" +#include "exec/address-spaces.h" +#include "exec/memory-internal.h" +#include "qemu/rcu.h" + +/* -icount align implementation. */ + +typedef struct SyncClocks { + int64_t diff_clk; + int64_t last_cpu_icount; + int64_t realtime_clock; +} SyncClocks; + +#if !defined(CONFIG_USER_ONLY) +/* Allow the guest to have a max 3ms advance. + * The difference between the 2 clocks could therefore + * oscillate around 0. + */ +#define VM_CLOCK_ADVANCE 3000000 +#define THRESHOLD_REDUCE 1.5 +#define MAX_DELAY_PRINT_RATE 2000000000LL +#define MAX_NB_PRINTS 100 + +static void align_clocks(SyncClocks *sc, const CPUState *cpu) +{ + int64_t cpu_icount; + + if (!icount_align_option) { + return; + } + + cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low; + sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount); + sc->last_cpu_icount = cpu_icount; + + if (sc->diff_clk > VM_CLOCK_ADVANCE) { +#ifndef _WIN32 + struct timespec sleep_delay, rem_delay; + sleep_delay.tv_sec = sc->diff_clk / 1000000000LL; + sleep_delay.tv_nsec = sc->diff_clk % 1000000000LL; + if (nanosleep(&sleep_delay, &rem_delay) < 0) { + sc->diff_clk = rem_delay.tv_sec * 1000000000LL + rem_delay.tv_nsec; + } else { + sc->diff_clk = 0; + } +#else + Sleep(sc->diff_clk / SCALE_MS); + sc->diff_clk = 0; +#endif + } +} + +static void print_delay(const SyncClocks *sc) +{ + static float threshold_delay; + static int64_t last_realtime_clock; + static int nb_prints; + + if (icount_align_option && + sc->realtime_clock - last_realtime_clock >= MAX_DELAY_PRINT_RATE && + nb_prints < MAX_NB_PRINTS) { + if ((-sc->diff_clk / (float)1000000000LL > threshold_delay) || + (-sc->diff_clk / (float)1000000000LL < + (threshold_delay - THRESHOLD_REDUCE))) { + threshold_delay = (-sc->diff_clk / 1000000000LL) + 1; + printf("Warning: The guest is now late by %.1f to %.1f seconds\n", + threshold_delay - 1, + threshold_delay); + nb_prints++; + last_realtime_clock = sc->realtime_clock; + } + } +} + +static void init_delay_params(SyncClocks *sc, + const CPUState *cpu) +{ + if (!icount_align_option) { + return; + } + sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); + sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock; + sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low; + if (sc->diff_clk < max_delay) { + max_delay = sc->diff_clk; + } + if (sc->diff_clk > max_advance) { + max_advance = sc->diff_clk; + } + + /* Print every 2s max if the guest is late. We limit the number + of printed messages to NB_PRINT_MAX(currently 100) */ + print_delay(sc); +} +#else +static void align_clocks(SyncClocks *sc, const CPUState *cpu) +{ +} + +static void init_delay_params(SyncClocks *sc, const CPUState *cpu) +{ +} +#endif /* CONFIG USER ONLY */ + +void cpu_loop_exit(CPUState *cpu) +{ + cpu->current_tb = NULL; + siglongjmp(cpu->jmp_env, 1); +} + +/* exit the current TB from a signal handler. The host registers are + restored in a state compatible with the CPU emulator + */ +#if defined(CONFIG_SOFTMMU) +void cpu_resume_from_signal(CPUState *cpu, void *puc) +{ + /* XXX: restore cpu registers saved in host registers */ + + cpu->exception_index = -1; + siglongjmp(cpu->jmp_env, 1); +} + +void cpu_reload_memory_map(CPUState *cpu) +{ + AddressSpaceDispatch *d; + + if (qemu_in_vcpu_thread()) { + /* Do not let the guest prolong the critical section as much as it + * as it desires. + * + * Currently, this is prevented by the I/O thread's periodinc kicking + * of the VCPU thread (iothread_requesting_mutex, qemu_cpu_kick_thread) + * but this will go away once TCG's execution moves out of the global + * mutex. + * + * This pair matches cpu_exec's rcu_read_lock()/rcu_read_unlock(), which + * only protects cpu->as->dispatch. Since we reload it below, we can + * split the critical section. + */ + rcu_read_unlock(); + rcu_read_lock(); + } + + /* The CPU and TLB are protected by the iothread lock. */ + d = atomic_rcu_read(&cpu->as->dispatch); + cpu->memory_dispatch = d; + tlb_flush(cpu, 1); +} +#endif + +/* Execute a TB, and fix up the CPU state afterwards if necessary */ +static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr) +{ + CPUArchState *env = cpu->env_ptr; + uintptr_t next_tb; + +#if defined(DEBUG_DISAS) + if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { +#if defined(TARGET_I386) + log_cpu_state(cpu, CPU_DUMP_CCOP); +#elif defined(TARGET_M68K) + /* ??? Should not modify env state for dumping. */ + cpu_m68k_flush_flags(env, env->cc_op); + env->cc_op = CC_OP_FLAGS; + env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); + log_cpu_state(cpu, 0); +#else + log_cpu_state(cpu, 0); +#endif + } +#endif /* DEBUG_DISAS */ + + cpu->can_do_io = 0; + next_tb = tcg_qemu_tb_exec(env, tb_ptr); + cpu->can_do_io = 1; + trace_exec_tb_exit((void *) (next_tb & ~TB_EXIT_MASK), + next_tb & TB_EXIT_MASK); + + if ((next_tb & TB_EXIT_MASK) > TB_EXIT_IDX1) { + /* We didn't start executing this TB (eg because the instruction + * counter hit zero); we must restore the guest PC to the address + * of the start of the TB. + */ + CPUClass *cc = CPU_GET_CLASS(cpu); + TranslationBlock *tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); + if (cc->synchronize_from_tb) { + cc->synchronize_from_tb(cpu, tb); + } else { + assert(cc->set_pc); + cc->set_pc(cpu, tb->pc); + } + } + if ((next_tb & TB_EXIT_MASK) == TB_EXIT_REQUESTED) { + /* We were asked to stop executing TBs (probably a pending + * interrupt. We've now stopped, so clear the flag. + */ + cpu->tcg_exit_req = 0; + } + return next_tb; +} + +/* Execute the code without caching the generated code. An interpreter + could be used if available. */ +static void cpu_exec_nocache(CPUArchState *env, int max_cycles, + TranslationBlock *orig_tb) +{ + CPUState *cpu = ENV_GET_CPU(env); + TranslationBlock *tb; + target_ulong pc = orig_tb->pc; + target_ulong cs_base = orig_tb->cs_base; + uint64_t flags = orig_tb->flags; + + /* Should never happen. + We only end up here when an existing TB is too long. */ + if (max_cycles > CF_COUNT_MASK) + max_cycles = CF_COUNT_MASK; + + /* tb_gen_code can flush our orig_tb, invalidate it now */ + tb_phys_invalidate(orig_tb, -1); + tb = tb_gen_code(cpu, pc, cs_base, flags, + max_cycles | CF_NOCACHE); + cpu->current_tb = tb; + /* execute the generated code */ + trace_exec_tb_nocache(tb, tb->pc); + cpu_tb_exec(cpu, tb->tc_ptr); + cpu->current_tb = NULL; + tb_phys_invalidate(tb, -1); + tb_free(tb); +} + +static TranslationBlock *tb_find_slow(CPUArchState *env, + target_ulong pc, + target_ulong cs_base, + uint64_t flags) +{ + CPUState *cpu = ENV_GET_CPU(env); + TranslationBlock *tb, **ptb1; + unsigned int h; + tb_page_addr_t phys_pc, phys_page1; + target_ulong virt_page2; + + tcg_ctx.tb_ctx.tb_invalidated_flag = 0; + + /* find translated block using physical mappings */ + phys_pc = get_page_addr_code(env, pc); + phys_page1 = phys_pc & TARGET_PAGE_MASK; + h = tb_phys_hash_func(phys_pc); + ptb1 = &tcg_ctx.tb_ctx.tb_phys_hash[h]; + for(;;) { + tb = *ptb1; + if (!tb) + goto not_found; + if (tb->pc == pc && + tb->page_addr[0] == phys_page1 && + tb->cs_base == cs_base && + tb->flags == flags) { + /* check next page if needed */ + if (tb->page_addr[1] != -1) { + tb_page_addr_t phys_page2; + + virt_page2 = (pc & TARGET_PAGE_MASK) + + TARGET_PAGE_SIZE; + phys_page2 = get_page_addr_code(env, virt_page2); + if (tb->page_addr[1] == phys_page2) + goto found; + } else { + goto found; + } + } + ptb1 = &tb->phys_hash_next; + } + not_found: + /* if no translated code available, then translate it now */ + tb = tb_gen_code(cpu, pc, cs_base, flags, 0); + + found: + /* Move the last found TB to the head of the list */ + if (likely(*ptb1)) { + *ptb1 = tb->phys_hash_next; + tb->phys_hash_next = tcg_ctx.tb_ctx.tb_phys_hash[h]; + tcg_ctx.tb_ctx.tb_phys_hash[h] = tb; + } + /* we add the TB in the virtual pc hash table */ + cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; + return tb; +} + +static inline TranslationBlock *tb_find_fast(CPUArchState *env) +{ + CPUState *cpu = ENV_GET_CPU(env); + TranslationBlock *tb; + target_ulong cs_base, pc; + int flags; + + /* we record a subset of the CPU state. It will + always be the same before a given translated block + is executed. */ + cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + tb = cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]; + if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base || + tb->flags != flags)) { + tb = tb_find_slow(env, pc, cs_base, flags); + } + return tb; +} + +static void cpu_handle_debug_exception(CPUArchState *env) +{ + CPUState *cpu = ENV_GET_CPU(env); + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUWatchpoint *wp; + + if (!cpu->watchpoint_hit) { + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + wp->flags &= ~BP_WATCHPOINT_HIT; + } + } + + cc->debug_excp_handler(cpu); +} + +/* main execution loop */ + +volatile sig_atomic_t exit_request; + +int cpu_exec(CPUArchState *env) +{ + CPUState *cpu = ENV_GET_CPU(env); + CPUClass *cc = CPU_GET_CLASS(cpu); +#ifdef TARGET_I386 + X86CPU *x86_cpu = X86_CPU(cpu); +#endif + int ret, interrupt_request; + TranslationBlock *tb; + uint8_t *tc_ptr; + uintptr_t next_tb; + SyncClocks sc; + + /* This must be volatile so it is not trashed by longjmp() */ + volatile bool have_tb_lock = false; + + if (cpu->halted) { + if (!cpu_has_work(cpu)) { + return EXCP_HALTED; + } + + cpu->halted = 0; + } + + current_cpu = cpu; + + /* As long as current_cpu is null, up to the assignment just above, + * requests by other threads to exit the execution loop are expected to + * be issued using the exit_request global. We must make sure that our + * evaluation of the global value is performed past the current_cpu + * value transition point, which requires a memory barrier as well as + * an instruction scheduling constraint on modern architectures. */ + smp_mb(); + + rcu_read_lock(); + + if (unlikely(exit_request)) { + cpu->exit_request = 1; + } + + cc->cpu_exec_enter(cpu); + + /* Calculate difference between guest clock and host clock. + * This delay includes the delay of the last cycle, so + * what we have to do is sleep until it is 0. As for the + * advance/delay we gain here, we try to fix it next time. + */ + init_delay_params(&sc, cpu); + + /* prepare setjmp context for exception handling */ + for(;;) { + if (sigsetjmp(cpu->jmp_env, 0) == 0) { + /* if an exception is pending, we execute it here */ + if (cpu->exception_index >= 0) { + if (cpu->exception_index >= EXCP_INTERRUPT) { + /* exit request from the cpu execution loop */ + ret = cpu->exception_index; + if (ret == EXCP_DEBUG) { + cpu_handle_debug_exception(env); + } + cpu->exception_index = -1; + break; + } else { +#if defined(CONFIG_USER_ONLY) + /* if user mode only, we simulate a fake exception + which will be handled outside the cpu execution + loop */ +#if defined(TARGET_I386) + cc->do_interrupt(cpu); +#endif + ret = cpu->exception_index; + cpu->exception_index = -1; + break; +#else + cc->do_interrupt(cpu); + cpu->exception_index = -1; +#endif + } + } + + next_tb = 0; /* force lookup of first TB */ + for(;;) { + interrupt_request = cpu->interrupt_request; + if (unlikely(interrupt_request)) { + if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { + /* Mask out external interrupts for this step. */ + interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; + } + if (interrupt_request & CPU_INTERRUPT_DEBUG) { + cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; + cpu->exception_index = EXCP_DEBUG; + cpu_loop_exit(cpu); + } + if (interrupt_request & CPU_INTERRUPT_HALT) { + cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; + cpu->halted = 1; + cpu->exception_index = EXCP_HLT; + cpu_loop_exit(cpu); + } +#if defined(TARGET_I386) + if (interrupt_request & CPU_INTERRUPT_INIT) { + cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0); + do_cpu_init(x86_cpu); + cpu->exception_index = EXCP_HALTED; + cpu_loop_exit(cpu); + } +#else + if (interrupt_request & CPU_INTERRUPT_RESET) { + cpu_reset(cpu); + } +#endif + /* The target hook has 3 exit conditions: + False when the interrupt isn't processed, + True when it is, and we should restart on a new TB, + and via longjmp via cpu_loop_exit. */ + if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { + next_tb = 0; + } + /* Don't use the cached interrupt_request value, + do_interrupt may have updated the EXITTB flag. */ + if (cpu->interrupt_request & CPU_INTERRUPT_EXITTB) { + cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; + /* ensure that no TB jump will be modified as + the program flow was changed */ + next_tb = 0; + } + } + if (unlikely(cpu->exit_request)) { + cpu->exit_request = 0; + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit(cpu); + } + spin_lock(&tcg_ctx.tb_ctx.tb_lock); + have_tb_lock = true; + tb = tb_find_fast(env); + /* Note: we do it here to avoid a gcc bug on Mac OS X when + doing it in tb_find_slow */ + if (tcg_ctx.tb_ctx.tb_invalidated_flag) { + /* as some TB could have been invalidated because + of memory exceptions while generating the code, we + must recompute the hash index here */ + next_tb = 0; + tcg_ctx.tb_ctx.tb_invalidated_flag = 0; + } + if (qemu_loglevel_mask(CPU_LOG_EXEC)) { + qemu_log("Trace %p [" TARGET_FMT_lx "] %s\n", + tb->tc_ptr, tb->pc, lookup_symbol(tb->pc)); + } + /* see if we can patch the calling TB. When the TB + spans two pages, we cannot safely do a direct + jump. */ + if (next_tb != 0 && tb->page_addr[1] == -1) { + tb_add_jump((TranslationBlock *)(next_tb & ~TB_EXIT_MASK), + next_tb & TB_EXIT_MASK, tb); + } + have_tb_lock = false; + spin_unlock(&tcg_ctx.tb_ctx.tb_lock); + + /* cpu_interrupt might be called while translating the + TB, but before it is linked into a potentially + infinite loop and becomes env->current_tb. Avoid + starting execution if there is a pending interrupt. */ + cpu->current_tb = tb; + barrier(); + if (likely(!cpu->exit_request)) { + trace_exec_tb(tb, tb->pc); + tc_ptr = tb->tc_ptr; + /* execute the generated code */ + next_tb = cpu_tb_exec(cpu, tc_ptr); + switch (next_tb & TB_EXIT_MASK) { + case TB_EXIT_REQUESTED: + /* Something asked us to stop executing + * chained TBs; just continue round the main + * loop. Whatever requested the exit will also + * have set something else (eg exit_request or + * interrupt_request) which we will handle + * next time around the loop. + */ + next_tb = 0; + break; + case TB_EXIT_ICOUNT_EXPIRED: + { + /* Instruction counter expired. */ + int insns_left = cpu->icount_decr.u32; + if (cpu->icount_extra && insns_left >= 0) { + /* Refill decrementer and continue execution. */ + cpu->icount_extra += insns_left; + insns_left = MIN(0xffff, cpu->icount_extra); + cpu->icount_extra -= insns_left; + cpu->icount_decr.u16.low = insns_left; + } else { + if (insns_left > 0) { + /* Execute remaining instructions. */ + tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); + cpu_exec_nocache(env, insns_left, tb); + align_clocks(&sc, cpu); + } + cpu->exception_index = EXCP_INTERRUPT; + next_tb = 0; + cpu_loop_exit(cpu); + } + break; + } + default: + break; + } + } + cpu->current_tb = NULL; + /* Try to align the host and virtual clocks + if the guest is in advance */ + align_clocks(&sc, cpu); + /* reset soft MMU for next block (it can currently + only be set by a memory fault) */ + } /* for(;;) */ + } else { + /* Reload env after longjmp - the compiler may have smashed all + * local variables as longjmp is marked 'noreturn'. */ + cpu = current_cpu; + env = cpu->env_ptr; + cc = CPU_GET_CLASS(cpu); + cpu->can_do_io = 1; +#ifdef TARGET_I386 + x86_cpu = X86_CPU(cpu); +#endif + if (have_tb_lock) { + spin_unlock(&tcg_ctx.tb_ctx.tb_lock); + have_tb_lock = false; + } + } + } /* for(;;) */ + + cc->cpu_exec_exit(cpu); + rcu_read_unlock(); + + /* fail safe : never use current_cpu outside cpu_exec() */ + current_cpu = NULL; + return ret; +} diff --git a/cpus.c b/cpus.c new file mode 100644 index 0000000..de6469f --- /dev/null +++ b/cpus.c @@ -0,0 +1,1582 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Needed early for CONFIG_BSD etc. */ +#include "config-host.h" + +#include "monitor/monitor.h" +#include "qapi/qmp/qerror.h" +#include "sysemu/sysemu.h" +#include "exec/gdbstub.h" +#include "sysemu/dma.h" +#include "sysemu/kvm.h" +#include "qmp-commands.h" + +#include "qemu/thread.h" +#include "sysemu/cpus.h" +#include "sysemu/qtest.h" +#include "qemu/main-loop.h" +#include "qemu/bitmap.h" +#include "qemu/seqlock.h" +#include "qapi-event.h" +#include "hw/nmi.h" + +#ifndef _WIN32 +#include "qemu/compatfd.h" +#endif + +#ifdef CONFIG_LINUX + +#include + +#ifndef PR_MCE_KILL +#define PR_MCE_KILL 33 +#endif + +#ifndef PR_MCE_KILL_SET +#define PR_MCE_KILL_SET 1 +#endif + +#ifndef PR_MCE_KILL_EARLY +#define PR_MCE_KILL_EARLY 1 +#endif + +#endif /* CONFIG_LINUX */ + +static CPUState *next_cpu; +int64_t max_delay; +int64_t max_advance; + +bool cpu_is_stopped(CPUState *cpu) +{ + return cpu->stopped || !runstate_is_running(); +} + +static bool cpu_thread_is_idle(CPUState *cpu) +{ + if (cpu->stop || cpu->queued_work_first) { + return false; + } + if (cpu_is_stopped(cpu)) { + return true; + } + if (!cpu->halted || cpu_has_work(cpu) || + kvm_halt_in_kernel()) { + return false; + } + return true; +} + +static bool all_cpu_threads_idle(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (!cpu_thread_is_idle(cpu)) { + return false; + } + } + return true; +} + +/***********************************************************/ +/* guest cycle counter */ + +/* Protected by TimersState seqlock */ + +static int64_t vm_clock_warp_start = -1; +/* Conversion factor from emulated instructions to virtual clock ticks. */ +static int icount_time_shift; +/* Arbitrarily pick 1MIPS as the minimum allowable speed. */ +#define MAX_ICOUNT_SHIFT 10 + +static QEMUTimer *icount_rt_timer; +static QEMUTimer *icount_vm_timer; +static QEMUTimer *icount_warp_timer; + +typedef struct TimersState { + /* Protected by BQL. */ + int64_t cpu_ticks_prev; + int64_t cpu_ticks_offset; + + /* cpu_clock_offset can be read out of BQL, so protect it with + * this lock. + */ + QemuSeqLock vm_clock_seqlock; + int64_t cpu_clock_offset; + int32_t cpu_ticks_enabled; + int64_t dummy; + + /* Compensate for varying guest execution speed. */ + int64_t qemu_icount_bias; + /* Only written by TCG thread */ + int64_t qemu_icount; +} TimersState; + +static TimersState timers_state; + +int64_t cpu_get_icount_raw(void) +{ + int64_t icount; + CPUState *cpu = current_cpu; + + icount = timers_state.qemu_icount; + if (cpu) { + if (!cpu_can_do_io(cpu)) { + fprintf(stderr, "Bad icount read\n"); + exit(1); + } + icount -= (cpu->icount_decr.u16.low + cpu->icount_extra); + } + return icount; +} + +/* Return the virtual CPU time, based on the instruction counter. */ +static int64_t cpu_get_icount_locked(void) +{ + int64_t icount = cpu_get_icount_raw(); + return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount); +} + +int64_t cpu_get_icount(void) +{ + int64_t icount; + unsigned start; + + do { + start = seqlock_read_begin(&timers_state.vm_clock_seqlock); + icount = cpu_get_icount_locked(); + } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); + + return icount; +} + +int64_t cpu_icount_to_ns(int64_t icount) +{ + return icount << icount_time_shift; +} + +/* return the host CPU cycle counter and handle stop/restart */ +/* Caller must hold the BQL */ +int64_t cpu_get_ticks(void) +{ + int64_t ticks; + + if (use_icount) { + return cpu_get_icount(); + } + + ticks = timers_state.cpu_ticks_offset; + if (timers_state.cpu_ticks_enabled) { + ticks += cpu_get_real_ticks(); + } + + if (timers_state.cpu_ticks_prev > ticks) { + /* Note: non increasing ticks may happen if the host uses + software suspend */ + timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; + ticks = timers_state.cpu_ticks_prev; + } + + timers_state.cpu_ticks_prev = ticks; + return ticks; +} + +static int64_t cpu_get_clock_locked(void) +{ + int64_t ticks; + + ticks = timers_state.cpu_clock_offset; + if (timers_state.cpu_ticks_enabled) { + ticks += get_clock(); + } + + return ticks; +} + +/* return the host CPU monotonic timer and handle stop/restart */ +int64_t cpu_get_clock(void) +{ + int64_t ti; + unsigned start; + + do { + start = seqlock_read_begin(&timers_state.vm_clock_seqlock); + ti = cpu_get_clock_locked(); + } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); + + return ti; +} + +/* enable cpu_get_ticks() + * Caller must hold BQL which server as mutex for vm_clock_seqlock. + */ +void cpu_enable_ticks(void) +{ + /* Here, the really thing protected by seqlock is cpu_clock_offset. */ + seqlock_write_lock(&timers_state.vm_clock_seqlock); + if (!timers_state.cpu_ticks_enabled) { + timers_state.cpu_ticks_offset -= cpu_get_real_ticks(); + timers_state.cpu_clock_offset -= get_clock(); + timers_state.cpu_ticks_enabled = 1; + } + seqlock_write_unlock(&timers_state.vm_clock_seqlock); +} + +/* disable cpu_get_ticks() : the clock is stopped. You must not call + * cpu_get_ticks() after that. + * Caller must hold BQL which server as mutex for vm_clock_seqlock. + */ +void cpu_disable_ticks(void) +{ + /* Here, the really thing protected by seqlock is cpu_clock_offset. */ + seqlock_write_lock(&timers_state.vm_clock_seqlock); + if (timers_state.cpu_ticks_enabled) { + timers_state.cpu_ticks_offset += cpu_get_real_ticks(); + timers_state.cpu_clock_offset = cpu_get_clock_locked(); + timers_state.cpu_ticks_enabled = 0; + } + seqlock_write_unlock(&timers_state.vm_clock_seqlock); +} + +/* Correlation between real and virtual time is always going to be + fairly approximate, so ignore small variation. + When the guest is idle real and virtual time will be aligned in + the IO wait loop. */ +#define ICOUNT_WOBBLE (get_ticks_per_sec() / 10) + +static void icount_adjust(void) +{ + int64_t cur_time; + int64_t cur_icount; + int64_t delta; + + /* Protected by TimersState mutex. */ + static int64_t last_delta; + + /* If the VM is not running, then do nothing. */ + if (!runstate_is_running()) { + return; + } + + seqlock_write_lock(&timers_state.vm_clock_seqlock); + cur_time = cpu_get_clock_locked(); + cur_icount = cpu_get_icount_locked(); + + delta = cur_icount - cur_time; + /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ + if (delta > 0 + && last_delta + ICOUNT_WOBBLE < delta * 2 + && icount_time_shift > 0) { + /* The guest is getting too far ahead. Slow time down. */ + icount_time_shift--; + } + if (delta < 0 + && last_delta - ICOUNT_WOBBLE > delta * 2 + && icount_time_shift < MAX_ICOUNT_SHIFT) { + /* The guest is getting too far behind. Speed time up. */ + icount_time_shift++; + } + last_delta = delta; + timers_state.qemu_icount_bias = cur_icount + - (timers_state.qemu_icount << icount_time_shift); + seqlock_write_unlock(&timers_state.vm_clock_seqlock); +} + +static void icount_adjust_rt(void *opaque) +{ + timer_mod(icount_rt_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000); + icount_adjust(); +} + +static void icount_adjust_vm(void *opaque) +{ + timer_mod(icount_vm_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + get_ticks_per_sec() / 10); + icount_adjust(); +} + +static int64_t qemu_icount_round(int64_t count) +{ + return (count + (1 << icount_time_shift) - 1) >> icount_time_shift; +} + +static void icount_warp_rt(void *opaque) +{ + /* The icount_warp_timer is rescheduled soon after vm_clock_warp_start + * changes from -1 to another value, so the race here is okay. + */ + if (atomic_read(&vm_clock_warp_start) == -1) { + return; + } + + seqlock_write_lock(&timers_state.vm_clock_seqlock); + if (runstate_is_running()) { + int64_t clock = cpu_get_clock_locked(); + int64_t warp_delta; + + warp_delta = clock - vm_clock_warp_start; + if (use_icount == 2) { + /* + * In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too + * far ahead of real time. + */ + int64_t cur_icount = cpu_get_icount_locked(); + int64_t delta = clock - cur_icount; + warp_delta = MIN(warp_delta, delta); + } + timers_state.qemu_icount_bias += warp_delta; + } + vm_clock_warp_start = -1; + seqlock_write_unlock(&timers_state.vm_clock_seqlock); + + if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) { + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } +} + +void qtest_clock_warp(int64_t dest) +{ + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + AioContext *aio_context; + assert(qtest_enabled()); + aio_context = qemu_get_aio_context(); + while (clock < dest) { + int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); + int64_t warp = qemu_soonest_timeout(dest - clock, deadline); + + seqlock_write_lock(&timers_state.vm_clock_seqlock); + timers_state.qemu_icount_bias += warp; + seqlock_write_unlock(&timers_state.vm_clock_seqlock); + + qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); + timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); + clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); +} + +void qemu_clock_warp(QEMUClockType type) +{ + int64_t clock; + int64_t deadline; + + /* + * There are too many global variables to make the "warp" behavior + * applicable to other clocks. But a clock argument removes the + * need for if statements all over the place. + */ + if (type != QEMU_CLOCK_VIRTUAL || !use_icount) { + return; + } + + /* + * If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now. + * This ensures that the deadline for the timer is computed correctly below. + * This also makes sure that the insn counter is synchronized before the + * CPU starts running, in case the CPU is woken by an event other than + * the earliest QEMU_CLOCK_VIRTUAL timer. + */ + icount_warp_rt(NULL); + timer_del(icount_warp_timer); + if (!all_cpu_threads_idle()) { + return; + } + + if (qtest_enabled()) { + /* When testing, qtest commands advance icount. */ + return; + } + + /* We want to use the earliest deadline from ALL vm_clocks */ + clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); + if (deadline < 0) { + return; + } + + if (deadline > 0) { + /* + * Ensure QEMU_CLOCK_VIRTUAL proceeds even when the virtual CPU goes to + * sleep. Otherwise, the CPU might be waiting for a future timer + * interrupt to wake it up, but the interrupt never comes because + * the vCPU isn't running any insns and thus doesn't advance the + * QEMU_CLOCK_VIRTUAL. + * + * An extreme solution for this problem would be to never let VCPUs + * sleep in icount mode if there is a pending QEMU_CLOCK_VIRTUAL + * timer; rather time could just advance to the next QEMU_CLOCK_VIRTUAL + * event. Instead, we do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL + * after some "real" time, (related to the time left until the next + * event) has passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this. + * This avoids that the warps are visible externally; for example, + * you will not be sending network packets continuously instead of + * every 100ms. + */ + seqlock_write_lock(&timers_state.vm_clock_seqlock); + if (vm_clock_warp_start == -1 || vm_clock_warp_start > clock) { + vm_clock_warp_start = clock; + } + seqlock_write_unlock(&timers_state.vm_clock_seqlock); + timer_mod_anticipate(icount_warp_timer, clock + deadline); + } else if (deadline == 0) { + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } +} + +static bool icount_state_needed(void *opaque) +{ + return use_icount; +} + +/* + * This is a subsection for icount migration. + */ +static const VMStateDescription icount_vmstate_timers = { + .name = "timer/icount", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(qemu_icount_bias, TimersState), + VMSTATE_INT64(qemu_icount, TimersState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_timers = { + .name = "timer", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(cpu_ticks_offset, TimersState), + VMSTATE_INT64(dummy, TimersState), + VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), + VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &icount_vmstate_timers, + .needed = icount_state_needed, + }, { + /* empty */ + } + } +}; + +void cpu_ticks_init(void) +{ + seqlock_init(&timers_state.vm_clock_seqlock, NULL); + vmstate_register(NULL, 0, &vmstate_timers, &timers_state); +} + +void configure_icount(QemuOpts *opts, Error **errp) +{ + const char *option; + char *rem_str = NULL; + + option = qemu_opt_get(opts, "shift"); + if (!option) { + if (qemu_opt_get(opts, "align") != NULL) { + error_setg(errp, "Please specify shift option when using align"); + } + return; + } + icount_align_option = qemu_opt_get_bool(opts, "align", false); + icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + icount_warp_rt, NULL); + if (strcmp(option, "auto") != 0) { + errno = 0; + icount_time_shift = strtol(option, &rem_str, 0); + if (errno != 0 || *rem_str != '\0' || !strlen(option)) { + error_setg(errp, "icount: Invalid shift value"); + } + use_icount = 1; + return; + } else if (icount_align_option) { + error_setg(errp, "shift=auto and align=on are incompatible"); + } + + use_icount = 2; + + /* 125MIPS seems a reasonable initial guess at the guest speed. + It will be corrected fairly quickly anyway. */ + icount_time_shift = 3; + + /* Have both realtime and virtual time triggers for speed adjustment. + The realtime trigger catches emulated time passing too slowly, + the virtual time trigger catches emulated time passing too fast. + Realtime triggers occur even when idle, so use them less frequently + than VM triggers. */ + icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT, + icount_adjust_rt, NULL); + timer_mod(icount_rt_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000); + icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + icount_adjust_vm, NULL); + timer_mod(icount_vm_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + get_ticks_per_sec() / 10); +} + +/***********************************************************/ +void hw_error(const char *fmt, ...) +{ + va_list ap; + CPUState *cpu; + + va_start(ap, fmt); + fprintf(stderr, "qemu: hardware error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + CPU_FOREACH(cpu) { + fprintf(stderr, "CPU #%d:\n", cpu->cpu_index); + cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_FPU); + } + va_end(ap); + abort(); +} + +void cpu_synchronize_all_states(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + cpu_synchronize_state(cpu); + } +} + +void cpu_synchronize_all_post_reset(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + cpu_synchronize_post_reset(cpu); + } +} + +void cpu_synchronize_all_post_init(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + cpu_synchronize_post_init(cpu); + } +} + +void cpu_clean_all_dirty(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + cpu_clean_state(cpu); + } +} + +static int do_vm_stop(RunState state) +{ + int ret = 0; + + if (runstate_is_running()) { + cpu_disable_ticks(); + pause_all_vcpus(); + runstate_set(state); + vm_state_notify(0, state); + qapi_event_send_stop(&error_abort); + } + + bdrv_drain_all(); + ret = bdrv_flush_all(); + + return ret; +} + +static bool cpu_can_run(CPUState *cpu) +{ + if (cpu->stop) { + return false; + } + if (cpu_is_stopped(cpu)) { + return false; + } + return true; +} + +static void cpu_handle_guest_debug(CPUState *cpu) +{ + gdb_set_stop_cpu(cpu); + qemu_system_debug_request(); + cpu->stopped = true; +} + +static void cpu_signal(int sig) +{ + if (current_cpu) { + cpu_exit(current_cpu); + } + exit_request = 1; +} + +#ifdef CONFIG_LINUX +static void sigbus_reraise(void) +{ + sigset_t set; + struct sigaction action; + + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_DFL; + if (!sigaction(SIGBUS, &action, NULL)) { + raise(SIGBUS); + sigemptyset(&set); + sigaddset(&set, SIGBUS); + sigprocmask(SIG_UNBLOCK, &set, NULL); + } + perror("Failed to re-raise SIGBUS!\n"); + abort(); +} + +static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo, + void *ctx) +{ + if (kvm_on_sigbus(siginfo->ssi_code, + (void *)(intptr_t)siginfo->ssi_addr)) { + sigbus_reraise(); + } +} + +static void qemu_init_sigbus(void) +{ + struct sigaction action; + + memset(&action, 0, sizeof(action)); + action.sa_flags = SA_SIGINFO; + action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler; + sigaction(SIGBUS, &action, NULL); + + prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0); +} + +static void qemu_kvm_eat_signals(CPUState *cpu) +{ + struct timespec ts = { 0, 0 }; + siginfo_t siginfo; + sigset_t waitset; + sigset_t chkset; + int r; + + sigemptyset(&waitset); + sigaddset(&waitset, SIG_IPI); + sigaddset(&waitset, SIGBUS); + + do { + r = sigtimedwait(&waitset, &siginfo, &ts); + if (r == -1 && !(errno == EAGAIN || errno == EINTR)) { + perror("sigtimedwait"); + exit(1); + } + + switch (r) { + case SIGBUS: + if (kvm_on_sigbus_vcpu(cpu, siginfo.si_code, siginfo.si_addr)) { + sigbus_reraise(); + } + break; + default: + break; + } + + r = sigpending(&chkset); + if (r == -1) { + perror("sigpending"); + exit(1); + } + } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS)); +} + +#else /* !CONFIG_LINUX */ + +static void qemu_init_sigbus(void) +{ +} + +static void qemu_kvm_eat_signals(CPUState *cpu) +{ +} +#endif /* !CONFIG_LINUX */ + +#ifndef _WIN32 +static void dummy_signal(int sig) +{ +} + +static void qemu_kvm_init_cpu_signals(CPUState *cpu) +{ + int r; + sigset_t set; + struct sigaction sigact; + + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = dummy_signal; + sigaction(SIG_IPI, &sigact, NULL); + + pthread_sigmask(SIG_BLOCK, NULL, &set); + sigdelset(&set, SIG_IPI); + sigdelset(&set, SIGBUS); + r = kvm_set_signal_mask(cpu, &set); + if (r) { + fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(-r)); + exit(1); + } +} + +static void qemu_tcg_init_cpu_signals(void) +{ + sigset_t set; + struct sigaction sigact; + + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = cpu_signal; + sigaction(SIG_IPI, &sigact, NULL); + + sigemptyset(&set); + sigaddset(&set, SIG_IPI); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); +} + +#else /* _WIN32 */ +static void qemu_kvm_init_cpu_signals(CPUState *cpu) +{ + abort(); +} + +static void qemu_tcg_init_cpu_signals(void) +{ +} +#endif /* _WIN32 */ + +static QemuMutex qemu_global_mutex; +static QemuCond qemu_io_proceeded_cond; +static unsigned iothread_requesting_mutex; + +static QemuThread io_thread; + +static QemuThread *tcg_cpu_thread; +static QemuCond *tcg_halt_cond; + +/* cpu creation */ +static QemuCond qemu_cpu_cond; +/* system init */ +static QemuCond qemu_pause_cond; +static QemuCond qemu_work_cond; + +void qemu_init_cpu_loop(void) +{ + qemu_init_sigbus(); + qemu_cond_init(&qemu_cpu_cond); + qemu_cond_init(&qemu_pause_cond); + qemu_cond_init(&qemu_work_cond); + qemu_cond_init(&qemu_io_proceeded_cond); + qemu_mutex_init(&qemu_global_mutex); + + qemu_thread_get_self(&io_thread); +} + +void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data) +{ + struct qemu_work_item wi; + + if (qemu_cpu_is_self(cpu)) { + func(data); + return; + } + + wi.func = func; + wi.data = data; + wi.free = false; + if (cpu->queued_work_first == NULL) { + cpu->queued_work_first = &wi; + } else { + cpu->queued_work_last->next = &wi; + } + cpu->queued_work_last = &wi; + wi.next = NULL; + wi.done = false; + + qemu_cpu_kick(cpu); + while (!wi.done) { + CPUState *self_cpu = current_cpu; + + qemu_cond_wait(&qemu_work_cond, &qemu_global_mutex); + current_cpu = self_cpu; + } +} + +void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data) +{ + struct qemu_work_item *wi; + + if (qemu_cpu_is_self(cpu)) { + func(data); + return; + } + + wi = g_malloc0(sizeof(struct qemu_work_item)); + wi->func = func; + wi->data = data; + wi->free = true; + if (cpu->queued_work_first == NULL) { + cpu->queued_work_first = wi; + } else { + cpu->queued_work_last->next = wi; + } + cpu->queued_work_last = wi; + wi->next = NULL; + wi->done = false; + + qemu_cpu_kick(cpu); +} + +static void flush_queued_work(CPUState *cpu) +{ + struct qemu_work_item *wi; + + if (cpu->queued_work_first == NULL) { + return; + } + + while ((wi = cpu->queued_work_first)) { + cpu->queued_work_first = wi->next; + wi->func(wi->data); + wi->done = true; + if (wi->free) { + g_free(wi); + } + } + cpu->queued_work_last = NULL; + qemu_cond_broadcast(&qemu_work_cond); +} + +static void qemu_wait_io_event_common(CPUState *cpu) +{ + if (cpu->stop) { + cpu->stop = false; + cpu->stopped = true; + qemu_cond_signal(&qemu_pause_cond); + } + flush_queued_work(cpu); + cpu->thread_kicked = false; +} + +static void qemu_tcg_wait_io_event(void) +{ + CPUState *cpu; + + while (all_cpu_threads_idle()) { + /* Start accounting real time to the virtual clock if the CPUs + are idle. */ + qemu_clock_warp(QEMU_CLOCK_VIRTUAL); + qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex); + } + + while (iothread_requesting_mutex) { + qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex); + } + + CPU_FOREACH(cpu) { + qemu_wait_io_event_common(cpu); + } +} + +static void qemu_kvm_wait_io_event(CPUState *cpu) +{ + while (cpu_thread_is_idle(cpu)) { + qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); + } + + qemu_kvm_eat_signals(cpu); + qemu_wait_io_event_common(cpu); +} + +static void *qemu_kvm_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + int r; + + qemu_mutex_lock(&qemu_global_mutex); + qemu_thread_get_self(cpu->thread); + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + current_cpu = cpu; + + r = kvm_init_vcpu(cpu); + if (r < 0) { + fprintf(stderr, "kvm_init_vcpu failed: %s\n", strerror(-r)); + exit(1); + } + + qemu_kvm_init_cpu_signals(cpu); + + /* signal CPU creation */ + cpu->created = true; + qemu_cond_signal(&qemu_cpu_cond); + + while (1) { + if (cpu_can_run(cpu)) { + r = kvm_cpu_exec(cpu); + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + } + } + qemu_kvm_wait_io_event(cpu); + } + + return NULL; +} + +static void *qemu_dummy_cpu_thread_fn(void *arg) +{ +#ifdef _WIN32 + fprintf(stderr, "qtest is not supported under Windows\n"); + exit(1); +#else + CPUState *cpu = arg; + sigset_t waitset; + int r; + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + + sigemptyset(&waitset); + sigaddset(&waitset, SIG_IPI); + + /* signal CPU creation */ + cpu->created = true; + qemu_cond_signal(&qemu_cpu_cond); + + current_cpu = cpu; + while (1) { + current_cpu = NULL; + qemu_mutex_unlock_iothread(); + do { + int sig; + r = sigwait(&waitset, &sig); + } while (r == -1 && (errno == EAGAIN || errno == EINTR)); + if (r == -1) { + perror("sigwait"); + exit(1); + } + qemu_mutex_lock_iothread(); + current_cpu = cpu; + qemu_wait_io_event_common(cpu); + } + + return NULL; +#endif +} + +static void tcg_exec_all(void); + +static void *qemu_tcg_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + + qemu_tcg_init_cpu_signals(); + qemu_thread_get_self(cpu->thread); + + qemu_mutex_lock(&qemu_global_mutex); + CPU_FOREACH(cpu) { + cpu->thread_id = qemu_get_thread_id(); + cpu->created = true; + cpu->can_do_io = 1; + } + qemu_cond_signal(&qemu_cpu_cond); + + /* wait for initial kick-off after machine start */ + while (first_cpu->stopped) { + qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex); + + /* process any pending work */ + CPU_FOREACH(cpu) { + qemu_wait_io_event_common(cpu); + } + } + + /* process any pending work */ + exit_request = 1; + + while (1) { + tcg_exec_all(); + + if (use_icount) { + int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); + + if (deadline == 0) { + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } + } + qemu_tcg_wait_io_event(); + } + + return NULL; +} + +static void qemu_cpu_kick_thread(CPUState *cpu) +{ +#ifndef _WIN32 + int err; + + err = pthread_kill(cpu->thread->thread, SIG_IPI); + if (err) { + fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); + exit(1); + } +#else /* _WIN32 */ + if (!qemu_cpu_is_self(cpu)) { + CONTEXT tcgContext; + + if (SuspendThread(cpu->hThread) == (DWORD)-1) { + fprintf(stderr, "qemu:%s: GetLastError:%lu\n", __func__, + GetLastError()); + exit(1); + } + + /* On multi-core systems, we are not sure that the thread is actually + * suspended until we can get the context. + */ + tcgContext.ContextFlags = CONTEXT_CONTROL; + while (GetThreadContext(cpu->hThread, &tcgContext) != 0) { + continue; + } + + cpu_signal(0); + + if (ResumeThread(cpu->hThread) == (DWORD)-1) { + fprintf(stderr, "qemu:%s: GetLastError:%lu\n", __func__, + GetLastError()); + exit(1); + } + } +#endif +} + +void qemu_cpu_kick(CPUState *cpu) +{ + qemu_cond_broadcast(cpu->halt_cond); + if (!tcg_enabled() && !cpu->thread_kicked) { + qemu_cpu_kick_thread(cpu); + cpu->thread_kicked = true; + } +} + +void qemu_cpu_kick_self(void) +{ +#ifndef _WIN32 + assert(current_cpu); + + if (!current_cpu->thread_kicked) { + qemu_cpu_kick_thread(current_cpu); + current_cpu->thread_kicked = true; + } +#else + abort(); +#endif +} + +bool qemu_cpu_is_self(CPUState *cpu) +{ + return qemu_thread_is_self(cpu->thread); +} + +bool qemu_in_vcpu_thread(void) +{ + return current_cpu && qemu_cpu_is_self(current_cpu); +} + +void qemu_mutex_lock_iothread(void) +{ + atomic_inc(&iothread_requesting_mutex); + if (!tcg_enabled() || !first_cpu || !first_cpu->thread) { + qemu_mutex_lock(&qemu_global_mutex); + atomic_dec(&iothread_requesting_mutex); + } else { + if (qemu_mutex_trylock(&qemu_global_mutex)) { + qemu_cpu_kick_thread(first_cpu); + qemu_mutex_lock(&qemu_global_mutex); + } + atomic_dec(&iothread_requesting_mutex); + qemu_cond_broadcast(&qemu_io_proceeded_cond); + } +} + +void qemu_mutex_unlock_iothread(void) +{ + qemu_mutex_unlock(&qemu_global_mutex); +} + +static int all_vcpus_paused(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (!cpu->stopped) { + return 0; + } + } + + return 1; +} + +void pause_all_vcpus(void) +{ + CPUState *cpu; + + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false); + CPU_FOREACH(cpu) { + cpu->stop = true; + qemu_cpu_kick(cpu); + } + + if (qemu_in_vcpu_thread()) { + cpu_stop_current(); + if (!kvm_enabled()) { + CPU_FOREACH(cpu) { + cpu->stop = false; + cpu->stopped = true; + } + return; + } + } + + while (!all_vcpus_paused()) { + qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex); + CPU_FOREACH(cpu) { + qemu_cpu_kick(cpu); + } + } +} + +void cpu_resume(CPUState *cpu) +{ + cpu->stop = false; + cpu->stopped = false; + qemu_cpu_kick(cpu); +} + +void resume_all_vcpus(void) +{ + CPUState *cpu; + + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); + CPU_FOREACH(cpu) { + cpu_resume(cpu); + } +} + +/* For temporary buffers for forming a name */ +#define VCPU_THREAD_NAME_SIZE 16 + +static void qemu_tcg_init_vcpu(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + tcg_cpu_address_space_init(cpu, cpu->as); + + /* share a single thread for all cpus with TCG */ + if (!tcg_cpu_thread) { + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + tcg_halt_cond = cpu->halt_cond; + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); +#ifdef _WIN32 + cpu->hThread = qemu_thread_get_handle(cpu->thread); +#endif + while (!cpu->created) { + qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); + } + tcg_cpu_thread = cpu->thread; + } else { + cpu->thread = tcg_cpu_thread; + cpu->halt_cond = tcg_halt_cond; + } +} + +static void qemu_kvm_start_vcpu(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, qemu_kvm_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); + while (!cpu->created) { + qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); + } +} + +static void qemu_dummy_start_vcpu(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, qemu_dummy_cpu_thread_fn, cpu, + QEMU_THREAD_JOINABLE); + while (!cpu->created) { + qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); + } +} + +void qemu_init_vcpu(CPUState *cpu) +{ + cpu->nr_cores = smp_cores; + cpu->nr_threads = smp_threads; + cpu->stopped = true; + if (kvm_enabled()) { + qemu_kvm_start_vcpu(cpu); + } else if (tcg_enabled()) { + qemu_tcg_init_vcpu(cpu); + } else { + qemu_dummy_start_vcpu(cpu); + } +} + +void cpu_stop_current(void) +{ + if (current_cpu) { + current_cpu->stop = false; + current_cpu->stopped = true; + cpu_exit(current_cpu); + qemu_cond_signal(&qemu_pause_cond); + } +} + +int vm_stop(RunState state) +{ + if (qemu_in_vcpu_thread()) { + qemu_system_vmstop_request_prepare(); + qemu_system_vmstop_request(state); + /* + * FIXME: should not return to device code in case + * vm_stop() has been requested. + */ + cpu_stop_current(); + return 0; + } + + return do_vm_stop(state); +} + +/* does a state transition even if the VM is already stopped, + current state is forgotten forever */ +int vm_stop_force_state(RunState state) +{ + if (runstate_is_running()) { + return vm_stop(state); + } else { + runstate_set(state); + /* Make sure to return an error if the flush in a previous vm_stop() + * failed. */ + return bdrv_flush_all(); + } +} + +static int tcg_cpu_exec(CPUArchState *env) +{ + CPUState *cpu = ENV_GET_CPU(env); + int ret; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif + +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif + if (use_icount) { + int64_t count; + int64_t deadline; + int decr; + timers_state.qemu_icount -= (cpu->icount_decr.u16.low + + cpu->icount_extra); + cpu->icount_decr.u16.low = 0; + cpu->icount_extra = 0; + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); + + /* Maintain prior (possibly buggy) behaviour where if no deadline + * was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than + * INT32_MAX nanoseconds ahead, we still use INT32_MAX + * nanoseconds. + */ + if ((deadline < 0) || (deadline > INT32_MAX)) { + deadline = INT32_MAX; + } + + count = qemu_icount_round(deadline); + timers_state.qemu_icount += count; + decr = (count > 0xffff) ? 0xffff : count; + count -= decr; + cpu->icount_decr.u16.low = decr; + cpu->icount_extra = count; + } + ret = cpu_exec(env); +#ifdef CONFIG_PROFILER + tcg_time += profile_getclock() - ti; +#endif + if (use_icount) { + /* Fold pending instructions back into the + instruction counter, and clear the interrupt flag. */ + timers_state.qemu_icount -= (cpu->icount_decr.u16.low + + cpu->icount_extra); + cpu->icount_decr.u32 = 0; + cpu->icount_extra = 0; + } + return ret; +} + +static void tcg_exec_all(void) +{ + int r; + + /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ + qemu_clock_warp(QEMU_CLOCK_VIRTUAL); + + if (next_cpu == NULL) { + next_cpu = first_cpu; + } + for (; next_cpu != NULL && !exit_request; next_cpu = CPU_NEXT(next_cpu)) { + CPUState *cpu = next_cpu; + CPUArchState *env = cpu->env_ptr; + + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, + (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); + + if (cpu_can_run(cpu)) { + r = tcg_cpu_exec(env); + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + break; + } + } else if (cpu->stop || cpu->stopped) { + break; + } + } + exit_request = 0; +} + +void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg) +{ + /* XXX: implement xxx_cpu_list for targets that still miss it */ +#if defined(cpu_list) + cpu_list(f, cpu_fprintf); +#endif +} + +CpuInfoList *qmp_query_cpus(Error **errp) +{ + CpuInfoList *head = NULL, *cur_item = NULL; + CPUState *cpu; + + CPU_FOREACH(cpu) { + CpuInfoList *info; +#if defined(TARGET_I386) + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; +#elif defined(TARGET_PPC) + PowerPCCPU *ppc_cpu = POWERPC_CPU(cpu); + CPUPPCState *env = &ppc_cpu->env; +#elif defined(TARGET_SPARC) + SPARCCPU *sparc_cpu = SPARC_CPU(cpu); + CPUSPARCState *env = &sparc_cpu->env; +#elif defined(TARGET_MIPS) + MIPSCPU *mips_cpu = MIPS_CPU(cpu); + CPUMIPSState *env = &mips_cpu->env; +#elif defined(TARGET_TRICORE) + TriCoreCPU *tricore_cpu = TRICORE_CPU(cpu); + CPUTriCoreState *env = &tricore_cpu->env; +#endif + + cpu_synchronize_state(cpu); + + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->CPU = cpu->cpu_index; + info->value->current = (cpu == first_cpu); + info->value->halted = cpu->halted; + info->value->qom_path = object_get_canonical_path(OBJECT(cpu)); + info->value->thread_id = cpu->thread_id; +#if defined(TARGET_I386) + info->value->has_pc = true; + info->value->pc = env->eip + env->segs[R_CS].base; +#elif defined(TARGET_PPC) + info->value->has_nip = true; + info->value->nip = env->nip; +#elif defined(TARGET_SPARC) + info->value->has_pc = true; + info->value->pc = env->pc; + info->value->has_npc = true; + info->value->npc = env->npc; +#elif defined(TARGET_MIPS) + info->value->has_PC = true; + info->value->PC = env->active_tc.PC; +#elif defined(TARGET_TRICORE) + info->value->has_PC = true; + info->value->PC = env->PC; +#endif + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + } + + return head; +} + +void qmp_memsave(int64_t addr, int64_t size, const char *filename, + bool has_cpu, int64_t cpu_index, Error **errp) +{ + FILE *f; + uint32_t l; + CPUState *cpu; + uint8_t buf[1024]; + int64_t orig_addr = addr, orig_size = size; + + if (!has_cpu) { + cpu_index = 0; + } + + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return; + } + + f = fopen(filename, "wb"); + if (!f) { + error_setg_file_open(errp, errno, filename); + return; + } + + while (size != 0) { + l = sizeof(buf); + if (l > size) + l = size; + if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64 + " specified", orig_addr, orig_size); + goto exit; + } + if (fwrite(buf, 1, l, f) != l) { + error_set(errp, QERR_IO_ERROR); + goto exit; + } + addr += l; + size -= l; + } + +exit: + fclose(f); +} + +void qmp_pmemsave(int64_t addr, int64_t size, const char *filename, + Error **errp) +{ + FILE *f; + uint32_t l; + uint8_t buf[1024]; + + f = fopen(filename, "wb"); + if (!f) { + error_setg_file_open(errp, errno, filename); + return; + } + + while (size != 0) { + l = sizeof(buf); + if (l > size) + l = size; + cpu_physical_memory_read(addr, buf, l); + if (fwrite(buf, 1, l, f) != l) { + error_set(errp, QERR_IO_ERROR); + goto exit; + } + addr += l; + size -= l; + } + +exit: + fclose(f); +} + +void qmp_inject_nmi(Error **errp) +{ +#if defined(TARGET_I386) + CPUState *cs; + + CPU_FOREACH(cs) { + X86CPU *cpu = X86_CPU(cs); + + if (!cpu->apic_state) { + cpu_interrupt(cs, CPU_INTERRUPT_NMI); + } else { + apic_deliver_nmi(cpu->apic_state); + } + } +#else + nmi_monitor_handle(monitor_get_cpu_index(), errp); +#endif +} + +void dump_drift_info(FILE *f, fprintf_function cpu_fprintf) +{ + if (!use_icount) { + return; + } + + cpu_fprintf(f, "Host - Guest clock %"PRIi64" ms\n", + (cpu_get_clock() - cpu_get_icount())/SCALE_MS); + if (icount_align_option) { + cpu_fprintf(f, "Max guest delay %"PRIi64" ms\n", -max_delay/SCALE_MS); + cpu_fprintf(f, "Max guest advance %"PRIi64" ms\n", max_advance/SCALE_MS); + } else { + cpu_fprintf(f, "Max guest delay NA\n"); + cpu_fprintf(f, "Max guest advance NA\n"); + } +} diff --git a/cputlb.c b/cputlb.c new file mode 100644 index 0000000..7606548 --- /dev/null +++ b/cputlb.c @@ -0,0 +1,412 @@ +/* + * Common CPU TLB handling + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + */ + +#include "config.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" +#include "exec/cpu_ldst.h" + +#include "exec/cputlb.h" + +#include "exec/memory-internal.h" +#include "exec/ram_addr.h" +#include "tcg/tcg.h" + +//#define DEBUG_TLB +//#define DEBUG_TLB_CHECK + +/* statistics */ +int tlb_flush_count; + +/* NOTE: + * If flush_global is true (the usual case), flush all tlb entries. + * If flush_global is false, flush (at least) all tlb entries not + * marked global. + * + * Since QEMU doesn't currently implement a global/not-global flag + * for tlb entries, at the moment tlb_flush() will also flush all + * tlb entries in the flush_global == false case. This is OK because + * CPU architectures generally permit an implementation to drop + * entries from the TLB at any time, so flushing more entries than + * required is only an efficiency issue, not a correctness issue. + */ +void tlb_flush(CPUState *cpu, int flush_global) +{ + CPUArchState *env = cpu->env_ptr; + +#if defined(DEBUG_TLB) + printf("tlb_flush:\n"); +#endif + /* must reset current TB so that interrupts cannot modify the + links while we are modifying them */ + cpu->current_tb = NULL; + + memset(env->tlb_table, -1, sizeof(env->tlb_table)); + memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); + memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache)); + + env->vtlb_index = 0; + env->tlb_flush_addr = -1; + env->tlb_flush_mask = 0; + tlb_flush_count++; +} + +static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) +{ + if (addr == (tlb_entry->addr_read & + (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || + addr == (tlb_entry->addr_write & + (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || + addr == (tlb_entry->addr_code & + (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + memset(tlb_entry, -1, sizeof(*tlb_entry)); + } +} + +void tlb_flush_page(CPUState *cpu, target_ulong addr) +{ + CPUArchState *env = cpu->env_ptr; + int i; + int mmu_idx; + +#if defined(DEBUG_TLB) + printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr); +#endif + /* Check if we need to flush due to large pages. */ + if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) { +#if defined(DEBUG_TLB) + printf("tlb_flush_page: forced full flush (" + TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", + env->tlb_flush_addr, env->tlb_flush_mask); +#endif + tlb_flush(cpu, 1); + return; + } + /* must reset current TB so that interrupts cannot modify the + links while we are modifying them */ + cpu->current_tb = NULL; + + addr &= TARGET_PAGE_MASK; + i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); + } + + /* check whether there are entries that need to be flushed in the vtlb */ + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + int k; + for (k = 0; k < CPU_VTLB_SIZE; k++) { + tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr); + } + } + + tb_flush_jmp_cache(cpu, addr); +} + +/* update the TLBs so that writes to code in the virtual page 'addr' + can be detected */ +void tlb_protect_code(ram_addr_t ram_addr) +{ + cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE, + DIRTY_MEMORY_CODE); +} + +/* update the TLB so that writes in physical page 'phys_addr' are no longer + tested for self modifying code */ +void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr, + target_ulong vaddr) +{ + cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); +} + +static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe) +{ + return (tlbe->addr_write & (TLB_INVALID_MASK|TLB_MMIO|TLB_NOTDIRTY)) == 0; +} + +void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, + uintptr_t length) +{ + uintptr_t addr; + + if (tlb_is_dirty_ram(tlb_entry)) { + addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend; + if ((addr - start) < length) { + tlb_entry->addr_write |= TLB_NOTDIRTY; + } + } +} + +static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) +{ + ram_addr_t ram_addr; + + if (qemu_ram_addr_from_host(ptr, &ram_addr) == NULL) { + fprintf(stderr, "Bad ram pointer %p\n", ptr); + abort(); + } + return ram_addr; +} + +void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length) +{ + CPUState *cpu; + CPUArchState *env; + + CPU_FOREACH(cpu) { + int mmu_idx; + + env = cpu->env_ptr; + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + unsigned int i; + + for (i = 0; i < CPU_TLB_SIZE; i++) { + tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i], + start1, length); + } + + for (i = 0; i < CPU_VTLB_SIZE; i++) { + tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i], + start1, length); + } + } + } +} + +static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr) +{ + if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) { + tlb_entry->addr_write = vaddr; + } +} + +/* update the TLB corresponding to virtual page vaddr + so that it is no longer dirty */ +void tlb_set_dirty(CPUArchState *env, target_ulong vaddr) +{ + int i; + int mmu_idx; + + vaddr &= TARGET_PAGE_MASK; + i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr); + } + + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + int k; + for (k = 0; k < CPU_VTLB_SIZE; k++) { + tlb_set_dirty1(&env->tlb_v_table[mmu_idx][k], vaddr); + } + } +} + +/* Our TLB does not support large pages, so remember the area covered by + large pages and trigger a full TLB flush if these are invalidated. */ +static void tlb_add_large_page(CPUArchState *env, target_ulong vaddr, + target_ulong size) +{ + target_ulong mask = ~(size - 1); + + if (env->tlb_flush_addr == (target_ulong)-1) { + env->tlb_flush_addr = vaddr & mask; + env->tlb_flush_mask = mask; + return; + } + /* Extend the existing region to include the new page. + This is a compromise between unnecessary flushes and the cost + of maintaining a full variable size TLB. */ + mask &= env->tlb_flush_mask; + while (((env->tlb_flush_addr ^ vaddr) & mask) != 0) { + mask <<= 1; + } + env->tlb_flush_addr &= mask; + env->tlb_flush_mask = mask; +} + +/* Add a new TLB entry. At most one entry for a given virtual address + * is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the + * supplied size is only used by tlb_flush_page. + * + * Called from TCG-generated code, which is under an RCU read-side + * critical section. + */ +void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, + hwaddr paddr, MemTxAttrs attrs, int prot, + int mmu_idx, target_ulong size) +{ + CPUArchState *env = cpu->env_ptr; + MemoryRegionSection *section; + unsigned int index; + target_ulong address; + target_ulong code_address; + uintptr_t addend; + CPUTLBEntry *te; + hwaddr iotlb, xlat, sz; + unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; + + assert(size >= TARGET_PAGE_SIZE); + if (size != TARGET_PAGE_SIZE) { + tlb_add_large_page(env, vaddr, size); + } + + sz = size; + section = address_space_translate_for_iotlb(cpu, paddr, &xlat, &sz); + assert(sz >= TARGET_PAGE_SIZE); + +#if defined(DEBUG_TLB) + qemu_log_mask(CPU_LOG_MMU, + "tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx + " prot=%x idx=%d\n", + vaddr, paddr, prot, mmu_idx); +#endif + + address = vaddr; + if (!memory_region_is_ram(section->mr) && !memory_region_is_romd(section->mr)) { + /* IO memory case */ + address |= TLB_MMIO; + addend = 0; + } else { + /* TLB_MMIO for rom/romd handled below */ + addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat; + } + + code_address = address; + iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat, + prot, &address); + + index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + te = &env->tlb_table[mmu_idx][index]; + + /* do not discard the translation in te, evict it into a victim tlb */ + env->tlb_v_table[mmu_idx][vidx] = *te; + env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; + + /* refill the tlb */ + env->iotlb[mmu_idx][index].addr = iotlb - vaddr; + env->iotlb[mmu_idx][index].attrs = attrs; + te->addend = addend - vaddr; + if (prot & PAGE_READ) { + te->addr_read = address; + } else { + te->addr_read = -1; + } + + if (prot & PAGE_EXEC) { + te->addr_code = code_address; + } else { + te->addr_code = -1; + } + if (prot & PAGE_WRITE) { + if ((memory_region_is_ram(section->mr) && section->readonly) + || memory_region_is_romd(section->mr)) { + /* Write access calls the I/O callback. */ + te->addr_write = address | TLB_MMIO; + } else if (memory_region_is_ram(section->mr) + && cpu_physical_memory_is_clean(section->mr->ram_addr + + xlat)) { + te->addr_write = address | TLB_NOTDIRTY; + } else { + te->addr_write = address; + } + } else { + te->addr_write = -1; + } +} + +/* Add a new TLB entry, but without specifying the memory + * transaction attributes to be used. + */ +void tlb_set_page(CPUState *cpu, target_ulong vaddr, + hwaddr paddr, int prot, + int mmu_idx, target_ulong size) +{ + tlb_set_page_with_attrs(cpu, vaddr, paddr, MEMTXATTRS_UNSPECIFIED, + prot, mmu_idx, size); +} + +/* NOTE: this function can trigger an exception */ +/* NOTE2: the returned address is not exactly the physical address: it + * is actually a ram_addr_t (in system mode; the user mode emulation + * version of this function returns a guest virtual address). + */ +tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) +{ + int mmu_idx, page_index, pd; + void *p; + MemoryRegion *mr; + CPUState *cpu = ENV_GET_CPU(env1); + + page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + mmu_idx = cpu_mmu_index(env1); + if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code != + (addr & TARGET_PAGE_MASK))) { + cpu_ldub_code(env1, addr); + } + pd = env1->iotlb[mmu_idx][page_index].addr & ~TARGET_PAGE_MASK; + mr = iotlb_to_region(cpu, pd); + if (memory_region_is_unassigned(mr)) { + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->do_unassigned_access) { + cc->do_unassigned_access(cpu, addr, false, true, 0, 4); + } else { + cpu_abort(cpu, "Trying to execute code outside RAM or ROM at 0x" + TARGET_FMT_lx "\n", addr); + } + } + p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend); + return qemu_ram_addr_from_host_nofail(p); +} + +#define MMUSUFFIX _mmu + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" +#undef MMUSUFFIX + +#define MMUSUFFIX _cmmu +#undef GETPC_ADJ +#define GETPC_ADJ 0 +#undef GETRA +#define GETRA() ((uintptr_t)0) +#define SOFTMMU_CODE_ACCESS + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" diff --git a/default-configs/aarch64-linux-user.mak b/default-configs/aarch64-linux-user.mak new file mode 100644 index 0000000..3df7de5 --- /dev/null +++ b/default-configs/aarch64-linux-user.mak @@ -0,0 +1,3 @@ +# Default configuration for aarch64-linux-user + +CONFIG_GDBSTUB_XML=y diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak new file mode 100644 index 0000000..96dd994 --- /dev/null +++ b/default-configs/aarch64-softmmu.mak @@ -0,0 +1,6 @@ +# Default configuration for aarch64-softmmu + +# We support all the 32 bit boards so need all their config +include arm-softmmu.mak + +CONFIG_XLNX_ZYNQMP=y diff --git a/default-configs/alpha-linux-user.mak b/default-configs/alpha-linux-user.mak new file mode 100644 index 0000000..7956e29 --- /dev/null +++ b/default-configs/alpha-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for alpha-linux-user diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak new file mode 100644 index 0000000..7f6161e --- /dev/null +++ b/default-configs/alpha-softmmu.mak @@ -0,0 +1,15 @@ +# Default configuration for alpha-softmmu + +include pci.mak +include usb.mak +CONFIG_SERIAL=y +CONFIG_I8254=y +CONFIG_PCKBD=y +CONFIG_VGA_CIRRUS=y +CONFIG_IDE_CORE=y +CONFIG_IDE_QDEV=y +CONFIG_VMWARE_VGA=y +CONFIG_IDE_CMD646=y +CONFIG_I8259=y +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y diff --git a/default-configs/arm-linux-user.mak b/default-configs/arm-linux-user.mak new file mode 100644 index 0000000..413361a --- /dev/null +++ b/default-configs/arm-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for arm-linux-user diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak new file mode 100644 index 0000000..a767e4b --- /dev/null +++ b/default-configs/arm-softmmu.mak @@ -0,0 +1,103 @@ +# Default configuration for arm-softmmu + +include pci.mak +include usb.mak +CONFIG_VGA=y +CONFIG_ISA_MMIO=y +CONFIG_NAND=y +CONFIG_ECC=y +CONFIG_SERIAL=y +CONFIG_PTIMER=y +CONFIG_SD=y +CONFIG_MAX7310=y +CONFIG_WM8750=y +CONFIG_TWL92230=y +CONFIG_TSC2005=y +CONFIG_LM832X=y +CONFIG_TMP105=y +CONFIG_STELLARIS=y +CONFIG_STELLARIS_INPUT=y +CONFIG_STELLARIS_ENET=y +CONFIG_SSD0303=y +CONFIG_SSD0323=y +CONFIG_ADS7846=y +CONFIG_MAX111X=y +CONFIG_SSI=y +CONFIG_SSI_SD=y +CONFIG_SSI_M25P80=y +CONFIG_LAN9118=y +CONFIG_SMC91C111=y +CONFIG_ALLWINNER_EMAC=y +CONFIG_DS1338=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_MICRODRIVE=y +CONFIG_USB=y +CONFIG_USB_MUSB=y +CONFIG_USB_EHCI_SYSBUS=y +CONFIG_PLATFORM_BUS=y + +CONFIG_ARM11MPCORE=y +CONFIG_A9MPCORE=y +CONFIG_A15MPCORE=y + +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_KVM=$(CONFIG_KVM) +CONFIG_ARM_TIMER=y +CONFIG_ARM_MPTIMER=y +CONFIG_A9_GTIMER=y +CONFIG_PL011=y +CONFIG_PL022=y +CONFIG_PL031=y +CONFIG_PL041=y +CONFIG_PL050=y +CONFIG_PL061=y +CONFIG_PL080=y +CONFIG_PL110=y +CONFIG_PL181=y +CONFIG_PL190=y +CONFIG_PL310=y +CONFIG_PL330=y +CONFIG_CADENCE=y +CONFIG_XGMAC=y +CONFIG_EXYNOS4=y +CONFIG_PXA2XX=y +CONFIG_BITBANG_I2C=y +CONFIG_FRAMEBUFFER=y +CONFIG_XILINX_SPIPS=y + +CONFIG_ARM11SCU=y +CONFIG_A9SCU=y +CONFIG_DIGIC=y +CONFIG_MARVELL_88W8618=y +CONFIG_OMAP=y +CONFIG_TSC210X=y +CONFIG_BLIZZARD=y +CONFIG_ONENAND=y +CONFIG_TUSB6010=y +CONFIG_IMX=y +CONFIG_MAINSTONE=y +CONFIG_NSERIES=y +CONFIG_REALVIEW=y +CONFIG_ZAURUS=y +CONFIG_ZYNQ=y +CONFIG_STM32F2XX_TIMER=y +CONFIG_STM32F2XX_USART=y +CONFIG_STM32F2XX_SYSCFG=y +CONFIG_STM32F205_SOC=y + +CONFIG_VERSATILE_PCI=y +CONFIG_VERSATILE_I2C=y + +CONFIG_PCI_GENERIC=y + +CONFIG_SDHCI=y +CONFIG_INTEGRATOR_DEBUG=y + +CONFIG_ALLWINNER_A10_PIT=y +CONFIG_ALLWINNER_A10_PIC=y +CONFIG_ALLWINNER_A10=y + +CONFIG_XIO3130=y +CONFIG_IOH3420=y +CONFIG_I82801B11=y diff --git a/default-configs/armeb-linux-user.mak b/default-configs/armeb-linux-user.mak new file mode 100644 index 0000000..bf2ffe7 --- /dev/null +++ b/default-configs/armeb-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for armeb-linux-user diff --git a/default-configs/cris-linux-user.mak b/default-configs/cris-linux-user.mak new file mode 100644 index 0000000..e3aec7b --- /dev/null +++ b/default-configs/cris-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for cris-linux-user diff --git a/default-configs/cris-softmmu.mak b/default-configs/cris-softmmu.mak new file mode 100644 index 0000000..d970d50 --- /dev/null +++ b/default-configs/cris-softmmu.mak @@ -0,0 +1,6 @@ +# Default configuration for cris-softmmu + +CONFIG_ETRAXFS=y +CONFIG_NAND=y +CONFIG_PTIMER=y +CONFIG_PFLASH_CFI02=y diff --git a/default-configs/i386-bsd-user.mak b/default-configs/i386-bsd-user.mak new file mode 100644 index 0000000..af1b31a --- /dev/null +++ b/default-configs/i386-bsd-user.mak @@ -0,0 +1 @@ +# Default configuration for i386-bsd-user diff --git a/default-configs/i386-linux-user.mak b/default-configs/i386-linux-user.mak new file mode 100644 index 0000000..8657e68 --- /dev/null +++ b/default-configs/i386-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for i386-linux-user diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak new file mode 100644 index 0000000..6a74e00 --- /dev/null +++ b/default-configs/i386-softmmu.mak @@ -0,0 +1,47 @@ +# Default configuration for i386-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_QXL=$(CONFIG_SPICE) +CONFIG_VGA_ISA=y +CONFIG_VGA_CIRRUS=y +CONFIG_VMWARE_VGA=y +CONFIG_VMMOUSE=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_I8257=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_PIIX_PCI=y +CONFIG_HPET=y +CONFIG_APPLESMC=y +CONFIG_I8259=y +CONFIG_PFLASH_CFI01=y +CONFIG_TPM_TIS=$(CONFIG_TPM) +CONFIG_MC146818RTC=y +CONFIG_PAM=y +CONFIG_PCI_PIIX=y +CONFIG_WDT_IB700=y +CONFIG_XEN_I386=$(CONFIG_XEN) +CONFIG_ISA_DEBUG=y +CONFIG_ISA_TESTDEV=y +CONFIG_VMPORT=y +CONFIG_SGA=y +CONFIG_LPC_ICH9=y +CONFIG_PCI_Q35=y +CONFIG_APIC=y +CONFIG_IOAPIC=y +CONFIG_ICC_BUS=y +CONFIG_PVPANIC=y +CONFIG_MEM_HOTPLUG=y +CONFIG_XIO3130=y +CONFIG_IOH3420=y +CONFIG_I82801B11=y diff --git a/default-configs/lm32-softmmu.mak b/default-configs/lm32-softmmu.mak new file mode 100644 index 0000000..4889348 --- /dev/null +++ b/default-configs/lm32-softmmu.mak @@ -0,0 +1,10 @@ +# Default configuration for lm32-softmmu + +CONFIG_LM32=y +CONFIG_MILKYMIST=y +CONFIG_MILKYMIST_TMU2=$(CONFIG_OPENGL) +CONFIG_FRAMEBUFFER=y +CONFIG_PTIMER=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_SD=y diff --git a/default-configs/m68k-linux-user.mak b/default-configs/m68k-linux-user.mak new file mode 100644 index 0000000..06cd5ed --- /dev/null +++ b/default-configs/m68k-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for m68k-linux-user diff --git a/default-configs/m68k-softmmu.mak b/default-configs/m68k-softmmu.mak new file mode 100644 index 0000000..d9552df --- /dev/null +++ b/default-configs/m68k-softmmu.mak @@ -0,0 +1,6 @@ +# Default configuration for m68k-softmmu + +include pci.mak +include usb.mak +CONFIG_COLDFIRE=y +CONFIG_PTIMER=y diff --git a/default-configs/microblaze-linux-user.mak b/default-configs/microblaze-linux-user.mak new file mode 100644 index 0000000..566fdc0 --- /dev/null +++ b/default-configs/microblaze-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for microblaze-linux-user diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak new file mode 100644 index 0000000..ce26308 --- /dev/null +++ b/default-configs/microblaze-softmmu.mak @@ -0,0 +1,11 @@ +# Default configuration for microblaze-softmmu + +CONFIG_PTIMER=y +CONFIG_PFLASH_CFI01=y +CONFIG_SERIAL=y +CONFIG_XILINX=y +CONFIG_XILINX_AXI=y +CONFIG_XILINX_SPI=y +CONFIG_XILINX_ETHLITE=y +CONFIG_SSI=y +CONFIG_SSI_M25P80=y diff --git a/default-configs/microblazeel-linux-user.mak b/default-configs/microblazeel-linux-user.mak new file mode 100644 index 0000000..378c6dd --- /dev/null +++ b/default-configs/microblazeel-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for microblazeel-linux-user diff --git a/default-configs/microblazeel-softmmu.mak b/default-configs/microblazeel-softmmu.mak new file mode 100644 index 0000000..2fcf442 --- /dev/null +++ b/default-configs/microblazeel-softmmu.mak @@ -0,0 +1,3 @@ +# Default configuration for microblazeel-softmmu + +include microblaze-softmmu.mak diff --git a/default-configs/mips-linux-user.mak b/default-configs/mips-linux-user.mak new file mode 100644 index 0000000..31df570 --- /dev/null +++ b/default-configs/mips-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for mips-linux-user diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak new file mode 100644 index 0000000..cce2c81 --- /dev/null +++ b/default-configs/mips-softmmu.mak @@ -0,0 +1,34 @@ +# Default configuration for mips-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_ESP=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VGA_CIRRUS=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_I8257=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_RC4030=y +CONFIG_DP8393X=y +CONFIG_DS1225Y=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y +CONFIG_G364FB=y +CONFIG_I8259=y +CONFIG_JAZZ_LED=y +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y +CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mips64-linux-user.mak b/default-configs/mips64-linux-user.mak new file mode 100644 index 0000000..1598bfc --- /dev/null +++ b/default-configs/mips64-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for mips64-linux-user diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak new file mode 100644 index 0000000..7a88a08 --- /dev/null +++ b/default-configs/mips64-softmmu.mak @@ -0,0 +1,34 @@ +# Default configuration for mips64-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_ESP=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VGA_CIRRUS=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_I8257=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_RC4030=y +CONFIG_DP8393X=y +CONFIG_DS1225Y=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y +CONFIG_G364FB=y +CONFIG_I8259=y +CONFIG_JAZZ_LED=y +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y +CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mips64el-linux-user.mak b/default-configs/mips64el-linux-user.mak new file mode 100644 index 0000000..629f084 --- /dev/null +++ b/default-configs/mips64el-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for mips64el-linux-user diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak new file mode 100644 index 0000000..095de43 --- /dev/null +++ b/default-configs/mips64el-softmmu.mak @@ -0,0 +1,37 @@ +# Default configuration for mips64el-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_ESP=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VGA_CIRRUS=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_I8257=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_IDE_VIA=y +CONFIG_NE2000_ISA=y +CONFIG_RC4030=y +CONFIG_DP8393X=y +CONFIG_DS1225Y=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y +CONFIG_FULONG=y +CONFIG_G364FB=y +CONFIG_I8259=y +CONFIG_JAZZ_LED=y +CONFIG_MC146818RTC=y +CONFIG_VT82C686=y +CONFIG_ISA_TESTDEV=y +CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mipsel-linux-user.mak b/default-configs/mipsel-linux-user.mak new file mode 100644 index 0000000..4d0e4af --- /dev/null +++ b/default-configs/mipsel-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for mipsel-linux-user diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak new file mode 100644 index 0000000..0e25108 --- /dev/null +++ b/default-configs/mipsel-softmmu.mak @@ -0,0 +1,34 @@ +# Default configuration for mipsel-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_ESP=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VGA_CIRRUS=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_I8257=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_RC4030=y +CONFIG_DP8393X=y +CONFIG_DS1225Y=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y +CONFIG_G364FB=y +CONFIG_I8259=y +CONFIG_JAZZ_LED=y +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y +CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mipsn32-linux-user.mak b/default-configs/mipsn32-linux-user.mak new file mode 100644 index 0000000..5b97919 --- /dev/null +++ b/default-configs/mipsn32-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for mipsn32-linux-user diff --git a/default-configs/mipsn32el-linux-user.mak b/default-configs/mipsn32el-linux-user.mak new file mode 100644 index 0000000..d6367ff --- /dev/null +++ b/default-configs/mipsn32el-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for mipsn32el-linux-user diff --git a/default-configs/moxie-softmmu.mak b/default-configs/moxie-softmmu.mak new file mode 100644 index 0000000..1a95476 --- /dev/null +++ b/default-configs/moxie-softmmu.mak @@ -0,0 +1,5 @@ +# Default configuration for moxie-softmmu + +CONFIG_MC146818RTC=y +CONFIG_SERIAL=y +CONFIG_VGA=y diff --git a/default-configs/or32-linux-user.mak b/default-configs/or32-linux-user.mak new file mode 100644 index 0000000..808c1f9 --- /dev/null +++ b/default-configs/or32-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for or32-linux-user diff --git a/default-configs/or32-softmmu.mak b/default-configs/or32-softmmu.mak new file mode 100644 index 0000000..cce4746 --- /dev/null +++ b/default-configs/or32-softmmu.mak @@ -0,0 +1,4 @@ +# Default configuration for or32-softmmu + +CONFIG_SERIAL=y +CONFIG_OPENCORES_ETH=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak new file mode 100644 index 0000000..7e10903 --- /dev/null +++ b/default-configs/pci.mak @@ -0,0 +1,39 @@ +CONFIG_PCI=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO=y +CONFIG_USB_UHCI=y +CONFIG_USB_OHCI=y +CONFIG_USB_EHCI=y +CONFIG_USB_XHCI=y +CONFIG_NE2000_PCI=y +CONFIG_EEPRO100_PCI=y +CONFIG_PCNET_PCI=y +CONFIG_PCNET_COMMON=y +CONFIG_AC97=y +CONFIG_HDA=y +CONFIG_ES1370=y +CONFIG_LSI_SCSI_PCI=y +CONFIG_VMW_PVSCSI_SCSI_PCI=y +CONFIG_MEGASAS_SCSI_PCI=y +CONFIG_RTL8139_PCI=y +CONFIG_E1000_PCI=y +CONFIG_VMXNET3_PCI=y +CONFIG_IDE_CORE=y +CONFIG_IDE_QDEV=y +CONFIG_IDE_PCI=y +CONFIG_AHCI=y +CONFIG_ESP=y +CONFIG_ESP_PCI=y +CONFIG_SERIAL=y +CONFIG_SERIAL_PCI=y +CONFIG_IPACK=y +CONFIG_WDT_IB6300ESB=y +CONFIG_PCI_TESTDEV=y +CONFIG_NVME_PCI=y +CONFIG_SD=y +CONFIG_SDHCI=y +CONFIG_EDU=y +CONFIG_VGA=y +CONFIG_VGA_PCI=y +CONFIG_IVSHMEM=$(CONFIG_KVM) +CONFIG_ROCKER=y diff --git a/default-configs/ppc-linux-user.mak b/default-configs/ppc-linux-user.mak new file mode 100644 index 0000000..260ba41 --- /dev/null +++ b/default-configs/ppc-linux-user.mak @@ -0,0 +1,2 @@ +# Default configuration for ppc-linux-user +CONFIG_LIBDECNUMBER=y diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak new file mode 100644 index 0000000..4befde3 --- /dev/null +++ b/default-configs/ppc-softmmu.mak @@ -0,0 +1,50 @@ +# Default configuration for ppc-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_ISA_MMIO=y +CONFIG_ESCC=y +CONFIG_M48T59=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_I8257=y +CONFIG_I82374=y +CONFIG_OPENPIC=y +CONFIG_PREP_PCI=y +CONFIG_I82378=y +CONFIG_PC87312=y +CONFIG_MACIO=y +CONFIG_PCSPK=y +CONFIG_CUDA=y +CONFIG_ADB=y +CONFIG_MAC_NVRAM=y +CONFIG_MAC_DBDMA=y +CONFIG_HEATHROW_PIC=y +CONFIG_GRACKLE_PCI=y +CONFIG_UNIN_PCI=y +CONFIG_DEC_PCI=y +CONFIG_PPCE500_PCI=y +CONFIG_IDE_ISA=y +CONFIG_IDE_CMD646=y +CONFIG_IDE_MACIO=y +CONFIG_NE2000_ISA=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_PTIMER=y +CONFIG_I8259=y +CONFIG_XILINX=y +CONFIG_XILINX_ETHLITE=y +CONFIG_PREP=y +CONFIG_MAC=y +CONFIG_E500=y +CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) +CONFIG_PLATFORM_BUS=y +CONFIG_ETSEC=y +CONFIG_LIBDECNUMBER=y +# For PReP +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y diff --git a/default-configs/ppc64-linux-user.mak b/default-configs/ppc64-linux-user.mak new file mode 100644 index 0000000..e731ce0 --- /dev/null +++ b/default-configs/ppc64-linux-user.mak @@ -0,0 +1,2 @@ +# Default configuration for ppc64-linux-user +CONFIG_LIBDECNUMBER=y diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak new file mode 100644 index 0000000..ab62cc7 --- /dev/null +++ b/default-configs/ppc64-softmmu.mak @@ -0,0 +1,54 @@ +# Default configuration for ppc64-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_ISA_MMIO=y +CONFIG_ESCC=y +CONFIG_M48T59=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_I8257=y +CONFIG_I82374=y +CONFIG_OPENPIC=y +CONFIG_PREP_PCI=y +CONFIG_I82378=y +CONFIG_PC87312=y +CONFIG_MACIO=y +CONFIG_PCSPK=y +CONFIG_CUDA=y +CONFIG_ADB=y +CONFIG_MAC_NVRAM=y +CONFIG_MAC_DBDMA=y +CONFIG_HEATHROW_PIC=y +CONFIG_GRACKLE_PCI=y +CONFIG_UNIN_PCI=y +CONFIG_DEC_PCI=y +CONFIG_PPCE500_PCI=y +CONFIG_IDE_ISA=y +CONFIG_IDE_CMD646=y +CONFIG_IDE_MACIO=y +CONFIG_NE2000_ISA=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_PTIMER=y +CONFIG_I8259=y +CONFIG_XILINX=y +CONFIG_XILINX_ETHLITE=y +CONFIG_PSERIES=y +CONFIG_PREP=y +CONFIG_MAC=y +CONFIG_E500=y +CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) +CONFIG_PLATFORM_BUS=y +CONFIG_ETSEC=y +CONFIG_LIBDECNUMBER=y +# For pSeries +CONFIG_XICS=$(CONFIG_PSERIES) +CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) +# For PReP +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y diff --git a/default-configs/ppc64abi32-linux-user.mak b/default-configs/ppc64abi32-linux-user.mak new file mode 100644 index 0000000..c244d07 --- /dev/null +++ b/default-configs/ppc64abi32-linux-user.mak @@ -0,0 +1,2 @@ +# Default configuration for ppc64abi32-linux-user +CONFIG_LIBDECNUMBER=y diff --git a/default-configs/ppc64le-linux-user.mak b/default-configs/ppc64le-linux-user.mak new file mode 100644 index 0000000..4ba4eae --- /dev/null +++ b/default-configs/ppc64le-linux-user.mak @@ -0,0 +1,2 @@ +# Default configuration for ppc64le-linux-user +CONFIG_LIBDECNUMBER=y diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak new file mode 100644 index 0000000..54acc4d --- /dev/null +++ b/default-configs/ppcemb-softmmu.mak @@ -0,0 +1,16 @@ +# Default configuration for ppcemb-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_M48T59=y +CONFIG_SERIAL=y +CONFIG_I8257=y +CONFIG_OPENPIC=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_PTIMER=y +CONFIG_I8259=y +CONFIG_XILINX=y +CONFIG_XILINX_ETHLITE=y +CONFIG_LIBDECNUMBER=y diff --git a/default-configs/s390x-linux-user.mak b/default-configs/s390x-linux-user.mak new file mode 100644 index 0000000..a243c99 --- /dev/null +++ b/default-configs/s390x-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for s390x-linux-user diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak new file mode 100644 index 0000000..f9e13f1 --- /dev/null +++ b/default-configs/s390x-softmmu.mak @@ -0,0 +1,6 @@ +CONFIG_PCI=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO=y +CONFIG_SCLPCONSOLE=y +CONFIG_S390_FLIC=y +CONFIG_S390_FLIC_KVM=$(CONFIG_KVM) diff --git a/default-configs/sh4-linux-user.mak b/default-configs/sh4-linux-user.mak new file mode 100644 index 0000000..a469e19 --- /dev/null +++ b/default-configs/sh4-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for sh4-linux-user diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak new file mode 100644 index 0000000..8e00390 --- /dev/null +++ b/default-configs/sh4-softmmu.mak @@ -0,0 +1,18 @@ +# Default configuration for sh4-softmmu + +include pci.mak +include usb.mak +CONFIG_SERIAL=y +CONFIG_PTIMER=y +CONFIG_PFLASH_CFI02=y +CONFIG_SH4=y +CONFIG_IDE_MMIO=y +CONFIG_SM501=y +CONFIG_ISA_TESTDEV=y +CONFIG_I82378=y +CONFIG_I8259=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_I82374=y +CONFIG_I8257=y +CONFIG_MC146818RTC=y diff --git a/default-configs/sh4eb-linux-user.mak b/default-configs/sh4eb-linux-user.mak new file mode 100644 index 0000000..be08ca1 --- /dev/null +++ b/default-configs/sh4eb-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for sh4eb-linux-user diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak new file mode 100644 index 0000000..efdd058 --- /dev/null +++ b/default-configs/sh4eb-softmmu.mak @@ -0,0 +1,18 @@ +# Default configuration for sh4eb-softmmu + +include pci.mak +include usb.mak +CONFIG_SERIAL=y +CONFIG_PTIMER=y +CONFIG_PFLASH_CFI02=y +CONFIG_SH4=y +CONFIG_IDE_MMIO=y +CONFIG_SM501=y +CONFIG_ISA_TESTDEV=y +CONFIG_I82378=y +CONFIG_I8259=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_I82374=y +CONFIG_I8257=y +CONFIG_MC146818RTC=y diff --git a/default-configs/sound.mak b/default-configs/sound.mak new file mode 100644 index 0000000..4f22c34 --- /dev/null +++ b/default-configs/sound.mak @@ -0,0 +1,4 @@ +CONFIG_SB16=y +CONFIG_ADLIB=y +CONFIG_GUS=y +CONFIG_CS4231A=y diff --git a/default-configs/sparc-bsd-user.mak b/default-configs/sparc-bsd-user.mak new file mode 100644 index 0000000..21e0950 --- /dev/null +++ b/default-configs/sparc-bsd-user.mak @@ -0,0 +1 @@ +# Default configuration for sparc-bsd-user diff --git a/default-configs/sparc-linux-user.mak b/default-configs/sparc-linux-user.mak new file mode 100644 index 0000000..9c716d1 --- /dev/null +++ b/default-configs/sparc-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for sparc-linux-user diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak new file mode 100644 index 0000000..ab796b3 --- /dev/null +++ b/default-configs/sparc-softmmu.mak @@ -0,0 +1,19 @@ +# Default configuration for sparc-softmmu + +CONFIG_ECC=y +CONFIG_ESP=y +CONFIG_ESCC=y +CONFIG_M48T59=y +CONFIG_PTIMER=y +CONFIG_FDC=y +CONFIG_EMPTY_SLOT=y +CONFIG_PCNET_COMMON=y +CONFIG_LANCE=y +CONFIG_TCX=y +CONFIG_CG3=y +CONFIG_SLAVIO=y +CONFIG_CS4231=y +CONFIG_GRLIB=y +CONFIG_STP2000=y +CONFIG_ECCMEMCTL=y +CONFIG_SUN4M=y diff --git a/default-configs/sparc32plus-linux-user.mak b/default-configs/sparc32plus-linux-user.mak new file mode 100644 index 0000000..432e880 --- /dev/null +++ b/default-configs/sparc32plus-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for sparc32plus-linux-user diff --git a/default-configs/sparc64-bsd-user.mak b/default-configs/sparc64-bsd-user.mak new file mode 100644 index 0000000..b8b9eea --- /dev/null +++ b/default-configs/sparc64-bsd-user.mak @@ -0,0 +1 @@ +# Default configuration for sparc64-bsd-user diff --git a/default-configs/sparc64-linux-user.mak b/default-configs/sparc64-linux-user.mak new file mode 100644 index 0000000..bf1bdd6 --- /dev/null +++ b/default-configs/sparc64-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for sparc64-linux-user diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak new file mode 100644 index 0000000..123bb99 --- /dev/null +++ b/default-configs/sparc64-softmmu.mak @@ -0,0 +1,16 @@ +# Default configuration for sparc64-softmmu + +include pci.mak +include usb.mak +CONFIG_ISA_MMIO=y +CONFIG_M48T59=y +CONFIG_PTIMER=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_IDE_ISA=y +CONFIG_IDE_CMD646=y +CONFIG_PCI_APB=y +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y diff --git a/default-configs/tricore-softmmu.mak b/default-configs/tricore-softmmu.mak new file mode 100644 index 0000000..e69de29 diff --git a/default-configs/unicore32-linux-user.mak b/default-configs/unicore32-linux-user.mak new file mode 100644 index 0000000..6aafd21 --- /dev/null +++ b/default-configs/unicore32-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for unicore32-linux-user diff --git a/default-configs/unicore32-softmmu.mak b/default-configs/unicore32-softmmu.mak new file mode 100644 index 0000000..de38577 --- /dev/null +++ b/default-configs/unicore32-softmmu.mak @@ -0,0 +1,4 @@ +# Default configuration for unicore32-softmmu +CONFIG_PUV3=y +CONFIG_PTIMER=y +CONFIG_PCKBD=y diff --git a/default-configs/usb.mak b/default-configs/usb.mak new file mode 100644 index 0000000..f4b8568 --- /dev/null +++ b/default-configs/usb.mak @@ -0,0 +1,10 @@ +CONFIG_USB=y +CONFIG_USB_TABLET_WACOM=y +CONFIG_USB_STORAGE_BOT=y +CONFIG_USB_STORAGE_UAS=y +CONFIG_USB_STORAGE_MTP=y +CONFIG_USB_SMARTCARD=y +CONFIG_USB_AUDIO=y +CONFIG_USB_SERIAL=y +CONFIG_USB_NETWORK=y +CONFIG_USB_BLUETOOTH=y diff --git a/default-configs/x86_64-bsd-user.mak b/default-configs/x86_64-bsd-user.mak new file mode 100644 index 0000000..73e5d34 --- /dev/null +++ b/default-configs/x86_64-bsd-user.mak @@ -0,0 +1 @@ +# Default configuration for x86_64-bsd-user diff --git a/default-configs/x86_64-linux-user.mak b/default-configs/x86_64-linux-user.mak new file mode 100644 index 0000000..bec1d9e --- /dev/null +++ b/default-configs/x86_64-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for x86_64-linux-user diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak new file mode 100644 index 0000000..46b87dd --- /dev/null +++ b/default-configs/x86_64-softmmu.mak @@ -0,0 +1,47 @@ +# Default configuration for x86_64-softmmu + +include pci.mak +include sound.mak +include usb.mak +CONFIG_QXL=$(CONFIG_SPICE) +CONFIG_VGA_ISA=y +CONFIG_VGA_CIRRUS=y +CONFIG_VMWARE_VGA=y +CONFIG_VMMOUSE=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_I8257=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_PIIX_PCI=y +CONFIG_HPET=y +CONFIG_APPLESMC=y +CONFIG_I8259=y +CONFIG_PFLASH_CFI01=y +CONFIG_TPM_TIS=$(CONFIG_TPM) +CONFIG_MC146818RTC=y +CONFIG_PAM=y +CONFIG_PCI_PIIX=y +CONFIG_WDT_IB700=y +CONFIG_XEN_I386=$(CONFIG_XEN) +CONFIG_ISA_DEBUG=y +CONFIG_ISA_TESTDEV=y +CONFIG_VMPORT=y +CONFIG_SGA=y +CONFIG_LPC_ICH9=y +CONFIG_PCI_Q35=y +CONFIG_APIC=y +CONFIG_IOAPIC=y +CONFIG_ICC_BUS=y +CONFIG_PVPANIC=y +CONFIG_MEM_HOTPLUG=y +CONFIG_XIO3130=y +CONFIG_IOH3420=y +CONFIG_I82801B11=y diff --git a/default-configs/xtensa-softmmu.mak b/default-configs/xtensa-softmmu.mak new file mode 100644 index 0000000..9d8899c --- /dev/null +++ b/default-configs/xtensa-softmmu.mak @@ -0,0 +1,5 @@ +# Default configuration for Xtensa + +CONFIG_SERIAL=y +CONFIG_OPENCORES_ETH=y +CONFIG_PFLASH_CFI01=y diff --git a/default-configs/xtensaeb-softmmu.mak b/default-configs/xtensaeb-softmmu.mak new file mode 100644 index 0000000..9d8899c --- /dev/null +++ b/default-configs/xtensaeb-softmmu.mak @@ -0,0 +1,5 @@ +# Default configuration for Xtensa + +CONFIG_SERIAL=y +CONFIG_OPENCORES_ETH=y +CONFIG_PFLASH_CFI01=y diff --git a/device-hotplug.c b/device-hotplug.c new file mode 100644 index 0000000..68b9496 --- /dev/null +++ b/device-hotplug.c @@ -0,0 +1,81 @@ +/* + * QEMU device hotplug helpers + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/hw.h" +#include "hw/boards.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "qemu/config-file.h" +#include "sysemu/sysemu.h" +#include "monitor/monitor.h" + +static DriveInfo *add_init_drive(const char *optstr) +{ + DriveInfo *dinfo; + QemuOpts *opts; + MachineClass *mc; + + opts = drive_def(optstr); + if (!opts) + return NULL; + + mc = MACHINE_GET_CLASS(current_machine); + dinfo = drive_new(opts, mc->block_default_type); + if (!dinfo) { + qemu_opts_del(opts); + return NULL; + } + + return dinfo; +} + +void hmp_drive_add(Monitor *mon, const QDict *qdict) +{ + DriveInfo *dinfo = NULL; + const char *opts = qdict_get_str(qdict, "opts"); + + dinfo = add_init_drive(opts); + if (!dinfo) { + goto err; + } + if (dinfo->devaddr) { + monitor_printf(mon, "Parameter addr not supported\n"); + goto err; + } + + switch (dinfo->type) { + case IF_NONE: + monitor_printf(mon, "OK\n"); + break; + default: + monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); + goto err; + } + return; + +err: + if (dinfo) { + blk_unref(blk_by_legacy_dinfo(dinfo)); + } +} diff --git a/device_tree.c b/device_tree.c new file mode 100644 index 0000000..3d119ef --- /dev/null +++ b/device_tree.c @@ -0,0 +1,353 @@ +/* + * Functions to help device tree manipulation using libfdt. + * It also provides functions to read entries from device tree proc + * interface. + * + * Copyright 2008 IBM Corporation. + * Authors: Jerone Young + * Hollis Blanchard + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "sysemu/device_tree.h" +#include "sysemu/sysemu.h" +#include "hw/loader.h" +#include "hw/boards.h" +#include "qemu/config-file.h" + +#include + +#define FDT_MAX_SIZE 0x10000 + +void *create_device_tree(int *sizep) +{ + void *fdt; + int ret; + + *sizep = FDT_MAX_SIZE; + fdt = g_malloc0(FDT_MAX_SIZE); + ret = fdt_create(fdt, FDT_MAX_SIZE); + if (ret < 0) { + goto fail; + } + ret = fdt_finish_reservemap(fdt); + if (ret < 0) { + goto fail; + } + ret = fdt_begin_node(fdt, ""); + if (ret < 0) { + goto fail; + } + ret = fdt_end_node(fdt); + if (ret < 0) { + goto fail; + } + ret = fdt_finish(fdt); + if (ret < 0) { + goto fail; + } + ret = fdt_open_into(fdt, fdt, *sizep); + if (ret) { + error_report("Unable to copy device tree in memory"); + exit(1); + } + + return fdt; +fail: + error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret)); + exit(1); +} + +void *load_device_tree(const char *filename_path, int *sizep) +{ + int dt_size; + int dt_file_load_size; + int ret; + void *fdt = NULL; + + *sizep = 0; + dt_size = get_image_size(filename_path); + if (dt_size < 0) { + error_report("Unable to get size of device tree file '%s'", + filename_path); + goto fail; + } + + /* Expand to 2x size to give enough room for manipulation. */ + dt_size += 10000; + dt_size *= 2; + /* First allocate space in qemu for device tree */ + fdt = g_malloc0(dt_size); + + dt_file_load_size = load_image(filename_path, fdt); + if (dt_file_load_size < 0) { + error_report("Unable to open device tree file '%s'", + filename_path); + goto fail; + } + + ret = fdt_open_into(fdt, fdt, dt_size); + if (ret) { + error_report("Unable to copy device tree in memory"); + goto fail; + } + + /* Check sanity of device tree */ + if (fdt_check_header(fdt)) { + error_report("Device tree file loaded into memory is invalid: %s", + filename_path); + goto fail; + } + *sizep = dt_size; + return fdt; + +fail: + g_free(fdt); + return NULL; +} + +static int findnode_nofail(void *fdt, const char *node_path) +{ + int offset; + + offset = fdt_path_offset(fdt, node_path); + if (offset < 0) { + error_report("%s Couldn't find node %s: %s", __func__, node_path, + fdt_strerror(offset)); + exit(1); + } + + return offset; +} + +int qemu_fdt_setprop(void *fdt, const char *node_path, + const char *property, const void *val, int size) +{ + int r; + + r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size); + if (r < 0) { + error_report("%s: Couldn't set %s/%s: %s", __func__, node_path, + property, fdt_strerror(r)); + exit(1); + } + + return r; +} + +int qemu_fdt_setprop_cell(void *fdt, const char *node_path, + const char *property, uint32_t val) +{ + int r; + + r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val); + if (r < 0) { + error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__, + node_path, property, val, fdt_strerror(r)); + exit(1); + } + + return r; +} + +int qemu_fdt_setprop_u64(void *fdt, const char *node_path, + const char *property, uint64_t val) +{ + val = cpu_to_be64(val); + return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val)); +} + +int qemu_fdt_setprop_string(void *fdt, const char *node_path, + const char *property, const char *string) +{ + int r; + + r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string); + if (r < 0) { + error_report("%s: Couldn't set %s/%s = %s: %s", __func__, + node_path, property, string, fdt_strerror(r)); + exit(1); + } + + return r; +} + +const void *qemu_fdt_getprop(void *fdt, const char *node_path, + const char *property, int *lenp) +{ + int len; + const void *r; + if (!lenp) { + lenp = &len; + } + r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp); + if (!r) { + error_report("%s: Couldn't get %s/%s: %s", __func__, + node_path, property, fdt_strerror(*lenp)); + exit(1); + } + return r; +} + +uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path, + const char *property) +{ + int len; + const uint32_t *p = qemu_fdt_getprop(fdt, node_path, property, &len); + if (len != 4) { + error_report("%s: %s/%s not 4 bytes long (not a cell?)", + __func__, node_path, property); + exit(1); + } + return be32_to_cpu(*p); +} + +uint32_t qemu_fdt_get_phandle(void *fdt, const char *path) +{ + uint32_t r; + + r = fdt_get_phandle(fdt, findnode_nofail(fdt, path)); + if (r == 0) { + error_report("%s: Couldn't get phandle for %s: %s", __func__, + path, fdt_strerror(r)); + exit(1); + } + + return r; +} + +int qemu_fdt_setprop_phandle(void *fdt, const char *node_path, + const char *property, + const char *target_node_path) +{ + uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path); + return qemu_fdt_setprop_cell(fdt, node_path, property, phandle); +} + +uint32_t qemu_fdt_alloc_phandle(void *fdt) +{ + static int phandle = 0x0; + + /* + * We need to find out if the user gave us special instruction at + * which phandle id to start allocting phandles. + */ + if (!phandle) { + phandle = machine_phandle_start(current_machine); + } + + if (!phandle) { + /* + * None or invalid phandle given on the command line, so fall back to + * default starting point. + */ + phandle = 0x8000; + } + + return phandle++; +} + +int qemu_fdt_nop_node(void *fdt, const char *node_path) +{ + int r; + + r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path)); + if (r < 0) { + error_report("%s: Couldn't nop node %s: %s", __func__, node_path, + fdt_strerror(r)); + exit(1); + } + + return r; +} + +int qemu_fdt_add_subnode(void *fdt, const char *name) +{ + char *dupname = g_strdup(name); + char *basename = strrchr(dupname, '/'); + int retval; + int parent = 0; + + if (!basename) { + g_free(dupname); + return -1; + } + + basename[0] = '\0'; + basename++; + + if (dupname[0]) { + parent = findnode_nofail(fdt, dupname); + } + + retval = fdt_add_subnode(fdt, parent, basename); + if (retval < 0) { + error_report("FDT: Failed to create subnode %s: %s", name, + fdt_strerror(retval)); + exit(1); + } + + g_free(dupname); + return retval; +} + +void qemu_fdt_dumpdtb(void *fdt, int size) +{ + const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb"); + + if (dumpdtb) { + /* Dump the dtb to a file and quit */ + exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1); + } +} + +int qemu_fdt_setprop_sized_cells_from_array(void *fdt, + const char *node_path, + const char *property, + int numvalues, + uint64_t *values) +{ + uint32_t *propcells; + uint64_t value; + int cellnum, vnum, ncells; + uint32_t hival; + int ret; + + propcells = g_new0(uint32_t, numvalues * 2); + + cellnum = 0; + for (vnum = 0; vnum < numvalues; vnum++) { + ncells = values[vnum * 2]; + if (ncells != 1 && ncells != 2) { + ret = -1; + goto out; + } + value = values[vnum * 2 + 1]; + hival = cpu_to_be32(value >> 32); + if (ncells > 1) { + propcells[cellnum++] = hival; + } else if (hival != 0) { + ret = -1; + goto out; + } + propcells[cellnum++] = cpu_to_be32(value); + } + + ret = qemu_fdt_setprop(fdt, node_path, property, propcells, + cellnum * sizeof(uint32_t)); +out: + g_free(propcells); + return ret; +} diff --git a/disas.c b/disas.c new file mode 100644 index 0000000..44a019a --- /dev/null +++ b/disas.c @@ -0,0 +1,539 @@ +/* General "disassemble this chunk" code. Used for debugging. */ +#include "config.h" +#include "disas/bfd.h" +#include "elf.h" +#include + +#include "cpu.h" +#include "disas/disas.h" + +typedef struct CPUDebug { + struct disassemble_info info; + CPUArchState *env; +} CPUDebug; + +/* Filled in by elfload.c. Simplistic, but will do for now. */ +struct syminfo *syminfos = NULL; + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +int +buffer_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + if (memaddr < info->buffer_vma + || memaddr + length > info->buffer_vma + info->buffer_length) + /* Out of bounds. Use EIO because GDB uses it. */ + return EIO; + memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length); + return 0; +} + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +static int +target_read_memory (bfd_vma memaddr, + bfd_byte *myaddr, + int length, + struct disassemble_info *info) +{ + CPUDebug *s = container_of(info, CPUDebug, info); + + cpu_memory_rw_debug(ENV_GET_CPU(s->env), memaddr, myaddr, length, 0); + return 0; +} + +/* Print an error message. We can assume that this is in response to + an error return from buffer_read_memory. */ +void +perror_memory (int status, bfd_vma memaddr, struct disassemble_info *info) +{ + if (status != EIO) + /* Can't happen. */ + (*info->fprintf_func) (info->stream, "Unknown error %d\n", status); + else + /* Actually, address between memaddr and memaddr + len was + out of bounds. */ + (*info->fprintf_func) (info->stream, + "Address 0x%" PRIx64 " is out of bounds.\n", memaddr); +} + +/* This could be in a separate file, to save minuscule amounts of space + in statically linked executables. */ + +/* Just print the address is hex. This is included for completeness even + though both GDB and objdump provide their own (to print symbolic + addresses). */ + +void +generic_print_address (bfd_vma addr, struct disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr); +} + +/* Print address in hex, truncated to the width of a target virtual address. */ +static void +generic_print_target_address(bfd_vma addr, struct disassemble_info *info) +{ + uint64_t mask = ~0ULL >> (64 - TARGET_VIRT_ADDR_SPACE_BITS); + generic_print_address(addr & mask, info); +} + +/* Print address in hex, truncated to the width of a host virtual address. */ +static void +generic_print_host_address(bfd_vma addr, struct disassemble_info *info) +{ + uint64_t mask = ~0ULL >> (64 - (sizeof(void *) * 8)); + generic_print_address(addr & mask, info); +} + +/* Just return the given address. */ + +int +generic_symbol_at_address (bfd_vma addr, struct disassemble_info *info) +{ + return 1; +} + +bfd_vma bfd_getl64 (const bfd_byte *addr) +{ + unsigned long long v; + + v = (unsigned long long) addr[0]; + v |= (unsigned long long) addr[1] << 8; + v |= (unsigned long long) addr[2] << 16; + v |= (unsigned long long) addr[3] << 24; + v |= (unsigned long long) addr[4] << 32; + v |= (unsigned long long) addr[5] << 40; + v |= (unsigned long long) addr[6] << 48; + v |= (unsigned long long) addr[7] << 56; + return (bfd_vma) v; +} + +bfd_vma bfd_getl32 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + v |= (unsigned long) addr[2] << 16; + v |= (unsigned long) addr[3] << 24; + return (bfd_vma) v; +} + +bfd_vma bfd_getb32 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + v |= (unsigned long) addr[2] << 8; + v |= (unsigned long) addr[3]; + return (bfd_vma) v; +} + +bfd_vma bfd_getl16 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + return (bfd_vma) v; +} + +bfd_vma bfd_getb16 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + return (bfd_vma) v; +} + +#ifdef TARGET_ARM +static int +print_insn_thumb1(bfd_vma pc, disassemble_info *info) +{ + return print_insn_arm(pc | 1, info); +} +#endif + +static int print_insn_objdump(bfd_vma pc, disassemble_info *info, + const char *prefix) +{ + int i, n = info->buffer_length; + uint8_t *buf = g_malloc(n); + + info->read_memory_func(pc, buf, n, info); + + for (i = 0; i < n; ++i) { + if (i % 32 == 0) { + info->fprintf_func(info->stream, "\n%s: ", prefix); + } + info->fprintf_func(info->stream, "%02x", buf[i]); + } + + g_free(buf); + return n; +} + +static int print_insn_od_host(bfd_vma pc, disassemble_info *info) +{ + return print_insn_objdump(pc, info, "OBJD-H"); +} + +static int print_insn_od_target(bfd_vma pc, disassemble_info *info) +{ + return print_insn_objdump(pc, info, "OBJD-T"); +} + +/* Disassemble this for me please... (debugging). 'flags' has the following + values: + i386 - 1 means 16 bit code, 2 means 64 bit code + arm - bit 0 = thumb, bit 1 = reverse endian, bit 2 = A64 + ppc - bits 0:15 specify (optionally) the machine instruction set; + bit 16 indicates little endian. + other targets - unused + */ +void target_disas(FILE *out, CPUArchState *env, target_ulong code, + target_ulong size, int flags) +{ + target_ulong pc; + int count; + CPUDebug s; + int (*print_insn)(bfd_vma pc, disassemble_info *info) = NULL; + + INIT_DISASSEMBLE_INFO(s.info, out, fprintf); + + s.env = env; + s.info.read_memory_func = target_read_memory; + s.info.buffer_vma = code; + s.info.buffer_length = size; + s.info.print_address_func = generic_print_target_address; + +#ifdef TARGET_WORDS_BIGENDIAN + s.info.endian = BFD_ENDIAN_BIG; +#else + s.info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(TARGET_I386) + if (flags == 2) { + s.info.mach = bfd_mach_x86_64; + } else if (flags == 1) { + s.info.mach = bfd_mach_i386_i8086; + } else { + s.info.mach = bfd_mach_i386_i386; + } + print_insn = print_insn_i386; +#elif defined(TARGET_ARM) + if (flags & 4) { + /* We might not be compiled with the A64 disassembler + * because it needs a C++ compiler; in that case we will + * fall through to the default print_insn_od case. + */ +#if defined(CONFIG_ARM_A64_DIS) + print_insn = print_insn_arm_a64; +#endif + } else if (flags & 1) { + print_insn = print_insn_thumb1; + } else { + print_insn = print_insn_arm; + } + if (flags & 2) { +#ifdef TARGET_WORDS_BIGENDIAN + s.info.endian = BFD_ENDIAN_LITTLE; +#else + s.info.endian = BFD_ENDIAN_BIG; +#endif + } +#elif defined(TARGET_SPARC) + print_insn = print_insn_sparc; +#ifdef TARGET_SPARC64 + s.info.mach = bfd_mach_sparc_v9b; +#endif +#elif defined(TARGET_PPC) + if ((flags >> 16) & 1) { + s.info.endian = BFD_ENDIAN_LITTLE; + } + if (flags & 0xFFFF) { + /* If we have a precise definition of the instruction set, use it. */ + s.info.mach = flags & 0xFFFF; + } else { +#ifdef TARGET_PPC64 + s.info.mach = bfd_mach_ppc64; +#else + s.info.mach = bfd_mach_ppc; +#endif + } + s.info.disassembler_options = (char *)"any"; + print_insn = print_insn_ppc; +#elif defined(TARGET_M68K) + print_insn = print_insn_m68k; +#elif defined(TARGET_MIPS) +#ifdef TARGET_WORDS_BIGENDIAN + print_insn = print_insn_big_mips; +#else + print_insn = print_insn_little_mips; +#endif +#elif defined(TARGET_SH4) + s.info.mach = bfd_mach_sh4; + print_insn = print_insn_sh; +#elif defined(TARGET_ALPHA) + s.info.mach = bfd_mach_alpha_ev6; + print_insn = print_insn_alpha; +#elif defined(TARGET_CRIS) + if (flags != 32) { + s.info.mach = bfd_mach_cris_v0_v10; + print_insn = print_insn_crisv10; + } else { + s.info.mach = bfd_mach_cris_v32; + print_insn = print_insn_crisv32; + } +#elif defined(TARGET_S390X) + s.info.mach = bfd_mach_s390_64; + print_insn = print_insn_s390; +#elif defined(TARGET_MICROBLAZE) + s.info.mach = bfd_arch_microblaze; + print_insn = print_insn_microblaze; +#elif defined(TARGET_MOXIE) + s.info.mach = bfd_arch_moxie; + print_insn = print_insn_moxie; +#elif defined(TARGET_LM32) + s.info.mach = bfd_mach_lm32; + print_insn = print_insn_lm32; +#endif + if (print_insn == NULL) { + print_insn = print_insn_od_target; + } + + for (pc = code; size > 0; pc += count, size -= count) { + fprintf(out, "0x" TARGET_FMT_lx ": ", pc); + count = print_insn(pc, &s.info); +#if 0 + { + int i; + uint8_t b; + fprintf(out, " {"); + for(i = 0; i < count; i++) { + target_read_memory(pc + i, &b, 1, &s.info); + fprintf(out, " %02x", b); + } + fprintf(out, " }"); + } +#endif + fprintf(out, "\n"); + if (count < 0) + break; + if (size < count) { + fprintf(out, + "Disassembler disagrees with translator over instruction " + "decoding\n" + "Please report this to qemu-devel@nongnu.org\n"); + break; + } + } +} + +/* Disassemble this for me please... (debugging). */ +void disas(FILE *out, void *code, unsigned long size) +{ + uintptr_t pc; + int count; + CPUDebug s; + int (*print_insn)(bfd_vma pc, disassemble_info *info) = NULL; + + INIT_DISASSEMBLE_INFO(s.info, out, fprintf); + s.info.print_address_func = generic_print_host_address; + + s.info.buffer = code; + s.info.buffer_vma = (uintptr_t)code; + s.info.buffer_length = size; + +#ifdef HOST_WORDS_BIGENDIAN + s.info.endian = BFD_ENDIAN_BIG; +#else + s.info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(CONFIG_TCG_INTERPRETER) + print_insn = print_insn_tci; +#elif defined(__i386__) + s.info.mach = bfd_mach_i386_i386; + print_insn = print_insn_i386; +#elif defined(__x86_64__) + s.info.mach = bfd_mach_x86_64; + print_insn = print_insn_i386; +#elif defined(_ARCH_PPC) + s.info.disassembler_options = (char *)"any"; + print_insn = print_insn_ppc; +#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS) + print_insn = print_insn_arm_a64; +#elif defined(__alpha__) + print_insn = print_insn_alpha; +#elif defined(__sparc__) + print_insn = print_insn_sparc; + s.info.mach = bfd_mach_sparc_v9b; +#elif defined(__arm__) + print_insn = print_insn_arm; +#elif defined(__MIPSEB__) + print_insn = print_insn_big_mips; +#elif defined(__MIPSEL__) + print_insn = print_insn_little_mips; +#elif defined(__m68k__) + print_insn = print_insn_m68k; +#elif defined(__s390__) + print_insn = print_insn_s390; +#elif defined(__hppa__) + print_insn = print_insn_hppa; +#elif defined(__ia64__) + print_insn = print_insn_ia64; +#endif + if (print_insn == NULL) { + print_insn = print_insn_od_host; + } + for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) { + fprintf(out, "0x%08" PRIxPTR ": ", pc); + count = print_insn(pc, &s.info); + fprintf(out, "\n"); + if (count < 0) + break; + } +} + +/* Look up symbol for debugging purpose. Returns "" if unknown. */ +const char *lookup_symbol(target_ulong orig_addr) +{ + const char *symbol = ""; + struct syminfo *s; + + for (s = syminfos; s; s = s->next) { + symbol = s->lookup_symbol(s, orig_addr); + if (symbol[0] != '\0') { + break; + } + } + + return symbol; +} + +#if !defined(CONFIG_USER_ONLY) + +#include "monitor/monitor.h" + +static int monitor_disas_is_physical; + +static int +monitor_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + CPUDebug *s = container_of(info, CPUDebug, info); + + if (monitor_disas_is_physical) { + cpu_physical_memory_read(memaddr, myaddr, length); + } else { + cpu_memory_rw_debug(ENV_GET_CPU(s->env), memaddr, myaddr, length, 0); + } + return 0; +} + +static int GCC_FMT_ATTR(2, 3) +monitor_fprintf(FILE *stream, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + monitor_vprintf((Monitor *)stream, fmt, ap); + va_end(ap); + return 0; +} + +/* Disassembler for the monitor. + See target_disas for a description of flags. */ +void monitor_disas(Monitor *mon, CPUArchState *env, + target_ulong pc, int nb_insn, int is_physical, int flags) +{ + int count, i; + CPUDebug s; + int (*print_insn)(bfd_vma pc, disassemble_info *info); + + INIT_DISASSEMBLE_INFO(s.info, (FILE *)mon, monitor_fprintf); + + s.env = env; + monitor_disas_is_physical = is_physical; + s.info.read_memory_func = monitor_read_memory; + s.info.print_address_func = generic_print_target_address; + + s.info.buffer_vma = pc; + +#ifdef TARGET_WORDS_BIGENDIAN + s.info.endian = BFD_ENDIAN_BIG; +#else + s.info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(TARGET_I386) + if (flags == 2) { + s.info.mach = bfd_mach_x86_64; + } else if (flags == 1) { + s.info.mach = bfd_mach_i386_i8086; + } else { + s.info.mach = bfd_mach_i386_i386; + } + print_insn = print_insn_i386; +#elif defined(TARGET_ARM) + print_insn = print_insn_arm; +#elif defined(TARGET_ALPHA) + print_insn = print_insn_alpha; +#elif defined(TARGET_SPARC) + print_insn = print_insn_sparc; +#ifdef TARGET_SPARC64 + s.info.mach = bfd_mach_sparc_v9b; +#endif +#elif defined(TARGET_PPC) + if (flags & 0xFFFF) { + /* If we have a precise definition of the instruction set, use it. */ + s.info.mach = flags & 0xFFFF; + } else { +#ifdef TARGET_PPC64 + s.info.mach = bfd_mach_ppc64; +#else + s.info.mach = bfd_mach_ppc; +#endif + } + if ((flags >> 16) & 1) { + s.info.endian = BFD_ENDIAN_LITTLE; + } + print_insn = print_insn_ppc; +#elif defined(TARGET_M68K) + print_insn = print_insn_m68k; +#elif defined(TARGET_MIPS) +#ifdef TARGET_WORDS_BIGENDIAN + print_insn = print_insn_big_mips; +#else + print_insn = print_insn_little_mips; +#endif +#elif defined(TARGET_SH4) + s.info.mach = bfd_mach_sh4; + print_insn = print_insn_sh; +#elif defined(TARGET_S390X) + s.info.mach = bfd_mach_s390_64; + print_insn = print_insn_s390; +#elif defined(TARGET_MOXIE) + s.info.mach = bfd_arch_moxie; + print_insn = print_insn_moxie; +#elif defined(TARGET_LM32) + s.info.mach = bfd_mach_lm32; + print_insn = print_insn_lm32; +#else + monitor_printf(mon, "0x" TARGET_FMT_lx + ": Asm output not supported on this arch\n", pc); + return; +#endif + + for(i = 0; i < nb_insn; i++) { + monitor_printf(mon, "0x" TARGET_FMT_lx ": ", pc); + count = print_insn(pc, &s.info); + monitor_printf(mon, "\n"); + if (count < 0) + break; + pc += count; + } +} +#endif diff --git a/disas/Makefile.objs b/disas/Makefile.objs new file mode 100644 index 0000000..8dae4da --- /dev/null +++ b/disas/Makefile.objs @@ -0,0 +1,24 @@ + +common-obj-$(CONFIG_ALPHA_DIS) += alpha.o +common-obj-$(CONFIG_ARM_DIS) += arm.o +common-obj-$(CONFIG_ARM_A64_DIS) += arm-a64.o +common-obj-$(CONFIG_ARM_A64_DIS) += libvixl/ +libvixldir = $(SRC_PATH)/disas/libvixl +arm-a64.o-cflags := -I$(libvixldir) +common-obj-$(CONFIG_CRIS_DIS) += cris.o +common-obj-$(CONFIG_HPPA_DIS) += hppa.o +common-obj-$(CONFIG_I386_DIS) += i386.o +common-obj-$(CONFIG_IA64_DIS) += ia64.o +common-obj-$(CONFIG_M68K_DIS) += m68k.o +common-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o +common-obj-$(CONFIG_MIPS_DIS) += mips.o +common-obj-$(CONFIG_MOXIE_DIS) += moxie.o +common-obj-$(CONFIG_PPC_DIS) += ppc.o +common-obj-$(CONFIG_S390_DIS) += s390.o +common-obj-$(CONFIG_SH4_DIS) += sh4.o +common-obj-$(CONFIG_SPARC_DIS) += sparc.o +common-obj-$(CONFIG_LM32_DIS) += lm32.o + +# TODO: As long as the TCG interpreter and its generated code depend +# on the QEMU target, we cannot compile the disassembler here. +#common-obj-$(CONFIG_TCI_DIS) += tci.o diff --git a/disas/alpha.c b/disas/alpha.c new file mode 100644 index 0000000..a950b9c --- /dev/null +++ b/disas/alpha.c @@ -0,0 +1,1916 @@ +/* alpha-dis.c -- Disassemble Alpha AXP instructions + Copyright 1996, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Richard Henderson , + patterned after the PPC opcode handling written by Ian Lance Taylor. + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, see +. */ + +#include +#include "disas/bfd.h" + +/* MAX is redefined below, so remove any previous definition. */ +#undef MAX + +/* The opcode table is an array of struct alpha_opcode. */ + +struct alpha_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned mask; + + /* One bit flags for the opcode. These are primarily used to + indicate specific processors and environments support the + instructions. The defined values are listed below. */ + unsigned flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[4]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct alpha_opcode alpha_opcodes[]; +extern const unsigned alpha_num_opcodes; + +/* Values defined for the flags field of a struct alpha_opcode. */ + +/* CPU Availability */ +#define AXP_OPCODE_BASE 0x0001 /* Base architecture -- all cpus. */ +#define AXP_OPCODE_EV4 0x0002 /* EV4 specific PALcode insns. */ +#define AXP_OPCODE_EV5 0x0004 /* EV5 specific PALcode insns. */ +#define AXP_OPCODE_EV6 0x0008 /* EV6 specific PALcode insns. */ +#define AXP_OPCODE_BWX 0x0100 /* Byte/word extension (amask bit 0). */ +#define AXP_OPCODE_CIX 0x0200 /* "Count" extension (amask bit 1). */ +#define AXP_OPCODE_MAX 0x0400 /* Multimedia extension (amask bit 8). */ + +#define AXP_OPCODE_NOPAL (~(AXP_OPCODE_EV4|AXP_OPCODE_EV5|AXP_OPCODE_EV6)) + +/* A macro to extract the major opcode from an instruction. */ +#define AXP_OP(i) (((i) >> 26) & 0x3F) + +/* The total number of major opcodes. */ +#define AXP_NOPS 0x40 + + +/* The operands table is an array of struct alpha_operand. */ + +struct alpha_operand +{ + /* The number of bits in the operand. */ + unsigned int bits : 5; + + /* How far the operand is left shifted in the instruction. */ + unsigned int shift : 5; + + /* The default relocation type for this operand. */ + signed int default_reloc : 16; + + /* One bit syntax flags. */ + unsigned int flags : 16; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned (*insert) (unsigned instruction, int op, + const char **errmsg); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & AXP_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + int (*extract) (unsigned instruction, int *invalid); +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the alpha_opcodes table. */ + +extern const struct alpha_operand alpha_operands[]; +extern const unsigned alpha_num_operands; + +/* Values defined for the flags field of a struct alpha_operand. */ + +/* Mask for selecting the type for typecheck purposes */ +#define AXP_OPERAND_TYPECHECK_MASK \ + (AXP_OPERAND_PARENS | AXP_OPERAND_COMMA | AXP_OPERAND_IR | \ + AXP_OPERAND_FPR | AXP_OPERAND_RELATIVE | AXP_OPERAND_SIGNED | \ + AXP_OPERAND_UNSIGNED) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics, for which two operands fields + are identical. The assembler should call the insert function with + any op value. The disassembler should call the extract function, + ignore the return value, and check the value placed in the invalid + argument. */ +#define AXP_OPERAND_FAKE 01 + +/* The operand should be wrapped in parentheses rather than separated + from the previous by a comma. This is used for the load and store + instructions which want their operands to look like "Ra,disp(Rb)". */ +#define AXP_OPERAND_PARENS 02 + +/* Used in combination with PARENS, this suppresses the suppression of + the comma. This is used for "jmp Ra,(Rb),hint". */ +#define AXP_OPERAND_COMMA 04 + +/* This operand names an integer register. */ +#define AXP_OPERAND_IR 010 + +/* This operand names a floating point register. */ +#define AXP_OPERAND_FPR 020 + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define AXP_OPERAND_RELATIVE 040 + +/* This operand takes signed values. */ +#define AXP_OPERAND_SIGNED 0100 + +/* This operand takes unsigned values. This exists primarily so that + a flags value of 0 can be treated as end-of-arguments. */ +#define AXP_OPERAND_UNSIGNED 0200 + +/* Suppress overflow detection on this field. This is used for hints. */ +#define AXP_OPERAND_NOOVERFLOW 0400 + +/* Mask for optional argument default value. */ +#define AXP_OPERAND_OPTIONAL_MASK 07000 + +/* This operand defaults to zero. This is used for jump hints. */ +#define AXP_OPERAND_DEFAULT_ZERO 01000 + +/* This operand should default to the first (real) operand and is used + in conjunction with AXP_OPERAND_OPTIONAL. This allows + "and $0,3,$0" to be written as "and $0,3", etc. I don't like + it, but it's what DEC does. */ +#define AXP_OPERAND_DEFAULT_FIRST 02000 + +/* Similarly, this operand should default to the second (real) operand. + This allows "negl $0" instead of "negl $0,$0". */ +#define AXP_OPERAND_DEFAULT_SECOND 04000 + + +/* Register common names */ + +#define AXP_REG_V0 0 +#define AXP_REG_T0 1 +#define AXP_REG_T1 2 +#define AXP_REG_T2 3 +#define AXP_REG_T3 4 +#define AXP_REG_T4 5 +#define AXP_REG_T5 6 +#define AXP_REG_T6 7 +#define AXP_REG_T7 8 +#define AXP_REG_S0 9 +#define AXP_REG_S1 10 +#define AXP_REG_S2 11 +#define AXP_REG_S3 12 +#define AXP_REG_S4 13 +#define AXP_REG_S5 14 +#define AXP_REG_FP 15 +#define AXP_REG_A0 16 +#define AXP_REG_A1 17 +#define AXP_REG_A2 18 +#define AXP_REG_A3 19 +#define AXP_REG_A4 20 +#define AXP_REG_A5 21 +#define AXP_REG_T8 22 +#define AXP_REG_T9 23 +#define AXP_REG_T10 24 +#define AXP_REG_T11 25 +#define AXP_REG_RA 26 +#define AXP_REG_PV 27 +#define AXP_REG_T12 27 +#define AXP_REG_AT 28 +#define AXP_REG_GP 29 +#define AXP_REG_SP 30 +#define AXP_REG_ZERO 31 + +enum bfd_reloc_code_real { + BFD_RELOC_23_PCREL_S2, + BFD_RELOC_ALPHA_HINT +}; + +/* This file holds the Alpha AXP opcode table. The opcode table includes + almost all of the extended instruction mnemonics. This permits the + disassembler to use them, and simplifies the assembler logic, at the + cost of increasing the table size. The table is strictly constant + data, so the compiler should be able to put it in the text segment. + + This file also holds the operand table. All knowledge about inserting + and extracting operands from instructions is kept in this file. + + The information for the base instruction set was compiled from the + _Alpha Architecture Handbook_, Digital Order Number EC-QD2KB-TE, + version 2. + + The information for the post-ev5 architecture extensions BWX, CIX and + MAX came from version 3 of this same document, which is also available + on-line at http://ftp.digital.com/pub/Digital/info/semiconductor + /literature/alphahb2.pdf + + The information for the EV4 PALcode instructions was compiled from + _DECchip 21064 and DECchip 21064A Alpha AXP Microprocessors Hardware + Reference Manual_, Digital Order Number EC-Q9ZUA-TE, preliminary + revision dated June 1994. + + The information for the EV5 PALcode instructions was compiled from + _Alpha 21164 Microprocessor Hardware Reference Manual_, Digital + Order Number EC-QAEQB-TE, preliminary revision dated April 1995. */ + +/* Local insertion and extraction functions */ + +static unsigned insert_rba (unsigned, int, const char **); +static unsigned insert_rca (unsigned, int, const char **); +static unsigned insert_za (unsigned, int, const char **); +static unsigned insert_zb (unsigned, int, const char **); +static unsigned insert_zc (unsigned, int, const char **); +static unsigned insert_bdisp (unsigned, int, const char **); +static unsigned insert_jhint (unsigned, int, const char **); +static unsigned insert_ev6hwjhint (unsigned, int, const char **); + +static int extract_rba (unsigned, int *); +static int extract_rca (unsigned, int *); +static int extract_za (unsigned, int *); +static int extract_zb (unsigned, int *); +static int extract_zc (unsigned, int *); +static int extract_bdisp (unsigned, int *); +static int extract_jhint (unsigned, int *); +static int extract_ev6hwjhint (unsigned, int *); + + +/* The operands table */ + +const struct alpha_operand alpha_operands[] = +{ + /* The fields are bits, shift, insert, extract, flags */ + /* The zero index is used to indicate end-of-list */ +#define UNUSED 0 + { 0, 0, 0, 0, 0, 0 }, + + /* The plain integer register fields */ +#define RA (UNUSED + 1) + { 5, 21, 0, AXP_OPERAND_IR, 0, 0 }, +#define RB (RA + 1) + { 5, 16, 0, AXP_OPERAND_IR, 0, 0 }, +#define RC (RB + 1) + { 5, 0, 0, AXP_OPERAND_IR, 0, 0 }, + + /* The plain fp register fields */ +#define FA (RC + 1) + { 5, 21, 0, AXP_OPERAND_FPR, 0, 0 }, +#define FB (FA + 1) + { 5, 16, 0, AXP_OPERAND_FPR, 0, 0 }, +#define FC (FB + 1) + { 5, 0, 0, AXP_OPERAND_FPR, 0, 0 }, + + /* The integer registers when they are ZERO */ +#define ZA (FC + 1) + { 5, 21, 0, AXP_OPERAND_FAKE, insert_za, extract_za }, +#define ZB (ZA + 1) + { 5, 16, 0, AXP_OPERAND_FAKE, insert_zb, extract_zb }, +#define ZC (ZB + 1) + { 5, 0, 0, AXP_OPERAND_FAKE, insert_zc, extract_zc }, + + /* The RB field when it needs parentheses */ +#define PRB (ZC + 1) + { 5, 16, 0, AXP_OPERAND_IR|AXP_OPERAND_PARENS, 0, 0 }, + + /* The RB field when it needs parentheses _and_ a preceding comma */ +#define CPRB (PRB + 1) + { 5, 16, 0, + AXP_OPERAND_IR|AXP_OPERAND_PARENS|AXP_OPERAND_COMMA, 0, 0 }, + + /* The RB field when it must be the same as the RA field */ +#define RBA (CPRB + 1) + { 5, 16, 0, AXP_OPERAND_FAKE, insert_rba, extract_rba }, + + /* The RC field when it must be the same as the RB field */ +#define RCA (RBA + 1) + { 5, 0, 0, AXP_OPERAND_FAKE, insert_rca, extract_rca }, + + /* The RC field when it can *default* to RA */ +#define DRC1 (RCA + 1) + { 5, 0, 0, + AXP_OPERAND_IR|AXP_OPERAND_DEFAULT_FIRST, 0, 0 }, + + /* The RC field when it can *default* to RB */ +#define DRC2 (DRC1 + 1) + { 5, 0, 0, + AXP_OPERAND_IR|AXP_OPERAND_DEFAULT_SECOND, 0, 0 }, + + /* The FC field when it can *default* to RA */ +#define DFC1 (DRC2 + 1) + { 5, 0, 0, + AXP_OPERAND_FPR|AXP_OPERAND_DEFAULT_FIRST, 0, 0 }, + + /* The FC field when it can *default* to RB */ +#define DFC2 (DFC1 + 1) + { 5, 0, 0, + AXP_OPERAND_FPR|AXP_OPERAND_DEFAULT_SECOND, 0, 0 }, + + /* The unsigned 8-bit literal of Operate format insns */ +#define LIT (DFC2 + 1) + { 8, 13, -LIT, AXP_OPERAND_UNSIGNED, 0, 0 }, + + /* The signed 16-bit displacement of Memory format insns. From here + we can't tell what relocation should be used, so don't use a default. */ +#define MDISP (LIT + 1) + { 16, 0, -MDISP, AXP_OPERAND_SIGNED, 0, 0 }, + + /* The signed "23-bit" aligned displacement of Branch format insns */ +#define BDISP (MDISP + 1) + { 21, 0, BFD_RELOC_23_PCREL_S2, + AXP_OPERAND_RELATIVE, insert_bdisp, extract_bdisp }, + + /* The 26-bit PALcode function */ +#define PALFN (BDISP + 1) + { 26, 0, -PALFN, AXP_OPERAND_UNSIGNED, 0, 0 }, + + /* The optional signed "16-bit" aligned displacement of the JMP/JSR hint */ +#define JMPHINT (PALFN + 1) + { 14, 0, BFD_RELOC_ALPHA_HINT, + AXP_OPERAND_RELATIVE|AXP_OPERAND_DEFAULT_ZERO|AXP_OPERAND_NOOVERFLOW, + insert_jhint, extract_jhint }, + + /* The optional hint to RET/JSR_COROUTINE */ +#define RETHINT (JMPHINT + 1) + { 14, 0, -RETHINT, + AXP_OPERAND_UNSIGNED|AXP_OPERAND_DEFAULT_ZERO, 0, 0 }, + + /* The 12-bit displacement for the ev[46] hw_{ld,st} (pal1b/pal1f) insns */ +#define EV4HWDISP (RETHINT + 1) +#define EV6HWDISP (EV4HWDISP) + { 12, 0, -EV4HWDISP, AXP_OPERAND_SIGNED, 0, 0 }, + + /* The 5-bit index for the ev4 hw_m[ft]pr (pal19/pal1d) insns */ +#define EV4HWINDEX (EV4HWDISP + 1) + { 5, 0, -EV4HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 }, + + /* The 8-bit index for the oddly unqualified hw_m[tf]pr insns + that occur in DEC PALcode. */ +#define EV4EXTHWINDEX (EV4HWINDEX + 1) + { 8, 0, -EV4EXTHWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 }, + + /* The 10-bit displacement for the ev5 hw_{ld,st} (pal1b/pal1f) insns */ +#define EV5HWDISP (EV4EXTHWINDEX + 1) + { 10, 0, -EV5HWDISP, AXP_OPERAND_SIGNED, 0, 0 }, + + /* The 16-bit index for the ev5 hw_m[ft]pr (pal19/pal1d) insns */ +#define EV5HWINDEX (EV5HWDISP + 1) + { 16, 0, -EV5HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 }, + + /* The 16-bit combined index/scoreboard mask for the ev6 + hw_m[ft]pr (pal19/pal1d) insns */ +#define EV6HWINDEX (EV5HWINDEX + 1) + { 16, 0, -EV6HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 }, + + /* The 13-bit branch hint for the ev6 hw_jmp/jsr (pal1e) insn */ +#define EV6HWJMPHINT (EV6HWINDEX+ 1) + { 8, 0, -EV6HWJMPHINT, + AXP_OPERAND_RELATIVE|AXP_OPERAND_DEFAULT_ZERO|AXP_OPERAND_NOOVERFLOW, + insert_ev6hwjhint, extract_ev6hwjhint } +}; + +const unsigned alpha_num_operands = sizeof(alpha_operands)/sizeof(*alpha_operands); + +/* The RB field when it is the same as the RA field in the same insn. + This operand is marked fake. The insertion function just copies + the RA field into the RB field, and the extraction function just + checks that the fields are the same. */ + +/*ARGSUSED*/ +static unsigned +insert_rba(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static int +extract_rba(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + + +/* The same for the RC field */ + +/*ARGSUSED*/ +static unsigned +insert_rca(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | ((insn >> 21) & 0x1f); +} + +static int +extract_rca(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != (insn & 0x1f)) + *invalid = 1; + return 0; +} + + +/* Fake arguments in which the registers must be set to ZERO */ + +/*ARGSUSED*/ +static unsigned +insert_za(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (31 << 21); +} + +static int +extract_za(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL && ((insn >> 21) & 0x1f) != 31) + *invalid = 1; + return 0; +} + +/*ARGSUSED*/ +static unsigned +insert_zb(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (31 << 16); +} + +static int +extract_zb(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL && ((insn >> 16) & 0x1f) != 31) + *invalid = 1; + return 0; +} + +/*ARGSUSED*/ +static unsigned +insert_zc(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | 31; +} + +static int +extract_zc(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL && (insn & 0x1f) != 31) + *invalid = 1; + return 0; +} + + +/* The displacement field of a Branch format insn. */ + +static unsigned +insert_bdisp(unsigned insn, int value, const char **errmsg) +{ + if (errmsg != (const char **)NULL && (value & 3)) + *errmsg = _("branch operand unaligned"); + return insn | ((value / 4) & 0x1FFFFF); +} + +/*ARGSUSED*/ +static int +extract_bdisp(unsigned insn, int *invalid ATTRIBUTE_UNUSED) +{ + return 4 * (((insn & 0x1FFFFF) ^ 0x100000) - 0x100000); +} + + +/* The hint field of a JMP/JSR insn. */ + +static unsigned +insert_jhint(unsigned insn, int value, const char **errmsg) +{ + if (errmsg != (const char **)NULL && (value & 3)) + *errmsg = _("jump hint unaligned"); + return insn | ((value / 4) & 0x3FFF); +} + +/*ARGSUSED*/ +static int +extract_jhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED) +{ + return 4 * (((insn & 0x3FFF) ^ 0x2000) - 0x2000); +} + +/* The hint field of an EV6 HW_JMP/JSR insn. */ + +static unsigned +insert_ev6hwjhint(unsigned insn, int value, const char **errmsg) +{ + if (errmsg != (const char **)NULL && (value & 3)) + *errmsg = _("jump hint unaligned"); + return insn | ((value / 4) & 0x1FFF); +} + +/*ARGSUSED*/ +static int +extract_ev6hwjhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED) +{ + return 4 * (((insn & 0x1FFF) ^ 0x1000) - 0x1000); +} + + +/* Macros used to form opcodes */ + +/* The main opcode */ +#define OP(x) (((x) & 0x3F) << 26) +#define OP_MASK 0xFC000000 + +/* Branch format instructions */ +#define BRA_(oo) OP(oo) +#define BRA_MASK OP_MASK +#define BRA(oo) BRA_(oo), BRA_MASK + +/* Floating point format instructions */ +#define FP_(oo,fff) (OP(oo) | (((fff) & 0x7FF) << 5)) +#define FP_MASK (OP_MASK | 0xFFE0) +#define FP(oo,fff) FP_(oo,fff), FP_MASK + +/* Memory format instructions */ +#define MEM_(oo) OP(oo) +#define MEM_MASK OP_MASK +#define MEM(oo) MEM_(oo), MEM_MASK + +/* Memory/Func Code format instructions */ +#define MFC_(oo,ffff) (OP(oo) | ((ffff) & 0xFFFF)) +#define MFC_MASK (OP_MASK | 0xFFFF) +#define MFC(oo,ffff) MFC_(oo,ffff), MFC_MASK + +/* Memory/Branch format instructions */ +#define MBR_(oo,h) (OP(oo) | (((h) & 3) << 14)) +#define MBR_MASK (OP_MASK | 0xC000) +#define MBR(oo,h) MBR_(oo,h), MBR_MASK + +/* Operate format instructions. The OPRL variant specifies a + literal second argument. */ +#define OPR_(oo,ff) (OP(oo) | (((ff) & 0x7F) << 5)) +#define OPRL_(oo,ff) (OPR_((oo),(ff)) | 0x1000) +#define OPR_MASK (OP_MASK | 0x1FE0) +#define OPR(oo,ff) OPR_(oo,ff), OPR_MASK +#define OPRL(oo,ff) OPRL_(oo,ff), OPR_MASK + +/* Generic PALcode format instructions */ +#define PCD_(oo) OP(oo) +#define PCD_MASK OP_MASK +#define PCD(oo) PCD_(oo), PCD_MASK + +/* Specific PALcode instructions */ +#define SPCD_(oo,ffff) (OP(oo) | ((ffff) & 0x3FFFFFF)) +#define SPCD_MASK 0xFFFFFFFF +#define SPCD(oo,ffff) SPCD_(oo,ffff), SPCD_MASK + +/* Hardware memory (hw_{ld,st}) instructions */ +#define EV4HWMEM_(oo,f) (OP(oo) | (((f) & 0xF) << 12)) +#define EV4HWMEM_MASK (OP_MASK | 0xF000) +#define EV4HWMEM(oo,f) EV4HWMEM_(oo,f), EV4HWMEM_MASK + +#define EV5HWMEM_(oo,f) (OP(oo) | (((f) & 0x3F) << 10)) +#define EV5HWMEM_MASK (OP_MASK | 0xF800) +#define EV5HWMEM(oo,f) EV5HWMEM_(oo,f), EV5HWMEM_MASK + +#define EV6HWMEM_(oo,f) (OP(oo) | (((f) & 0xF) << 12)) +#define EV6HWMEM_MASK (OP_MASK | 0xF000) +#define EV6HWMEM(oo,f) EV6HWMEM_(oo,f), EV6HWMEM_MASK + +#define EV6HWMBR_(oo,h) (OP(oo) | (((h) & 7) << 13)) +#define EV6HWMBR_MASK (OP_MASK | 0xE000) +#define EV6HWMBR(oo,h) EV6HWMBR_(oo,h), EV6HWMBR_MASK + +/* Abbreviations for instruction subsets. */ +#define BASE AXP_OPCODE_BASE +#define EV4 AXP_OPCODE_EV4 +#define EV5 AXP_OPCODE_EV5 +#define EV6 AXP_OPCODE_EV6 +#define BWX AXP_OPCODE_BWX +#define CIX AXP_OPCODE_CIX +#define MAX AXP_OPCODE_MAX + +/* Common combinations of arguments */ +#define ARG_NONE { 0 } +#define ARG_BRA { RA, BDISP } +#define ARG_FBRA { FA, BDISP } +#define ARG_FP { FA, FB, DFC1 } +#define ARG_FPZ1 { ZA, FB, DFC1 } +#define ARG_MEM { RA, MDISP, PRB } +#define ARG_FMEM { FA, MDISP, PRB } +#define ARG_OPR { RA, RB, DRC1 } +#define ARG_OPRL { RA, LIT, DRC1 } +#define ARG_OPRZ1 { ZA, RB, DRC1 } +#define ARG_OPRLZ1 { ZA, LIT, RC } +#define ARG_PCD { PALFN } +#define ARG_EV4HWMEM { RA, EV4HWDISP, PRB } +#define ARG_EV4HWMPR { RA, RBA, EV4HWINDEX } +#define ARG_EV5HWMEM { RA, EV5HWDISP, PRB } +#define ARG_EV6HWMEM { RA, EV6HWDISP, PRB } + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK { OPERANDS } + + NAME is the name of the instruction. + + OPCODE is the instruction opcode. + + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + + OPERANDS is the list of operands. + + The preceding macros merge the text of the OPCODE and MASK fields. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. + + Otherwise, it is sorted by major opcode and minor function code. + + There are three classes of not-really-instructions in this table: + + ALIAS is another name for another instruction. Some of + these come from the Architecture Handbook, some + come from the original gas opcode tables. In all + cases, the functionality of the opcode is unchanged. + + PSEUDO a stylized code form endorsed by Chapter A.4 of the + Architecture Handbook. + + EXTRA a stylized code form found in the original gas tables. + + And two annotations: + + EV56 BUT opcodes that are officially introduced as of the ev56, + but with defined results on previous implementations. + + EV56 UNA opcodes that were introduced as of the ev56 with + presumably undefined results on previous implementations + that were not assigned to a particular extension. +*/ + +const struct alpha_opcode alpha_opcodes[] = { + { "halt", SPCD(0x00,0x0000), BASE, ARG_NONE }, + { "draina", SPCD(0x00,0x0002), BASE, ARG_NONE }, + { "bpt", SPCD(0x00,0x0080), BASE, ARG_NONE }, + { "bugchk", SPCD(0x00,0x0081), BASE, ARG_NONE }, + { "callsys", SPCD(0x00,0x0083), BASE, ARG_NONE }, + { "chmk", SPCD(0x00,0x0083), BASE, ARG_NONE }, + { "imb", SPCD(0x00,0x0086), BASE, ARG_NONE }, + { "rduniq", SPCD(0x00,0x009e), BASE, ARG_NONE }, + { "wruniq", SPCD(0x00,0x009f), BASE, ARG_NONE }, + { "gentrap", SPCD(0x00,0x00aa), BASE, ARG_NONE }, + { "call_pal", PCD(0x00), BASE, ARG_PCD }, + { "pal", PCD(0x00), BASE, ARG_PCD }, /* alias */ + + { "lda", MEM(0x08), BASE, { RA, MDISP, ZB } }, /* pseudo */ + { "lda", MEM(0x08), BASE, ARG_MEM }, + { "ldah", MEM(0x09), BASE, { RA, MDISP, ZB } }, /* pseudo */ + { "ldah", MEM(0x09), BASE, ARG_MEM }, + { "ldbu", MEM(0x0A), BWX, ARG_MEM }, + { "unop", MEM_(0x0B) | (30 << 16), + MEM_MASK, BASE, { ZA } }, /* pseudo */ + { "ldq_u", MEM(0x0B), BASE, ARG_MEM }, + { "ldwu", MEM(0x0C), BWX, ARG_MEM }, + { "stw", MEM(0x0D), BWX, ARG_MEM }, + { "stb", MEM(0x0E), BWX, ARG_MEM }, + { "stq_u", MEM(0x0F), BASE, ARG_MEM }, + + { "sextl", OPR(0x10,0x00), BASE, ARG_OPRZ1 }, /* pseudo */ + { "sextl", OPRL(0x10,0x00), BASE, ARG_OPRLZ1 }, /* pseudo */ + { "addl", OPR(0x10,0x00), BASE, ARG_OPR }, + { "addl", OPRL(0x10,0x00), BASE, ARG_OPRL }, + { "s4addl", OPR(0x10,0x02), BASE, ARG_OPR }, + { "s4addl", OPRL(0x10,0x02), BASE, ARG_OPRL }, + { "negl", OPR(0x10,0x09), BASE, ARG_OPRZ1 }, /* pseudo */ + { "negl", OPRL(0x10,0x09), BASE, ARG_OPRLZ1 }, /* pseudo */ + { "subl", OPR(0x10,0x09), BASE, ARG_OPR }, + { "subl", OPRL(0x10,0x09), BASE, ARG_OPRL }, + { "s4subl", OPR(0x10,0x0B), BASE, ARG_OPR }, + { "s4subl", OPRL(0x10,0x0B), BASE, ARG_OPRL }, + { "cmpbge", OPR(0x10,0x0F), BASE, ARG_OPR }, + { "cmpbge", OPRL(0x10,0x0F), BASE, ARG_OPRL }, + { "s8addl", OPR(0x10,0x12), BASE, ARG_OPR }, + { "s8addl", OPRL(0x10,0x12), BASE, ARG_OPRL }, + { "s8subl", OPR(0x10,0x1B), BASE, ARG_OPR }, + { "s8subl", OPRL(0x10,0x1B), BASE, ARG_OPRL }, + { "cmpult", OPR(0x10,0x1D), BASE, ARG_OPR }, + { "cmpult", OPRL(0x10,0x1D), BASE, ARG_OPRL }, + { "addq", OPR(0x10,0x20), BASE, ARG_OPR }, + { "addq", OPRL(0x10,0x20), BASE, ARG_OPRL }, + { "s4addq", OPR(0x10,0x22), BASE, ARG_OPR }, + { "s4addq", OPRL(0x10,0x22), BASE, ARG_OPRL }, + { "negq", OPR(0x10,0x29), BASE, ARG_OPRZ1 }, /* pseudo */ + { "negq", OPRL(0x10,0x29), BASE, ARG_OPRLZ1 }, /* pseudo */ + { "subq", OPR(0x10,0x29), BASE, ARG_OPR }, + { "subq", OPRL(0x10,0x29), BASE, ARG_OPRL }, + { "s4subq", OPR(0x10,0x2B), BASE, ARG_OPR }, + { "s4subq", OPRL(0x10,0x2B), BASE, ARG_OPRL }, + { "cmpeq", OPR(0x10,0x2D), BASE, ARG_OPR }, + { "cmpeq", OPRL(0x10,0x2D), BASE, ARG_OPRL }, + { "s8addq", OPR(0x10,0x32), BASE, ARG_OPR }, + { "s8addq", OPRL(0x10,0x32), BASE, ARG_OPRL }, + { "s8subq", OPR(0x10,0x3B), BASE, ARG_OPR }, + { "s8subq", OPRL(0x10,0x3B), BASE, ARG_OPRL }, + { "cmpule", OPR(0x10,0x3D), BASE, ARG_OPR }, + { "cmpule", OPRL(0x10,0x3D), BASE, ARG_OPRL }, + { "addl/v", OPR(0x10,0x40), BASE, ARG_OPR }, + { "addl/v", OPRL(0x10,0x40), BASE, ARG_OPRL }, + { "negl/v", OPR(0x10,0x49), BASE, ARG_OPRZ1 }, /* pseudo */ + { "negl/v", OPRL(0x10,0x49), BASE, ARG_OPRLZ1 }, /* pseudo */ + { "subl/v", OPR(0x10,0x49), BASE, ARG_OPR }, + { "subl/v", OPRL(0x10,0x49), BASE, ARG_OPRL }, + { "cmplt", OPR(0x10,0x4D), BASE, ARG_OPR }, + { "cmplt", OPRL(0x10,0x4D), BASE, ARG_OPRL }, + { "addq/v", OPR(0x10,0x60), BASE, ARG_OPR }, + { "addq/v", OPRL(0x10,0x60), BASE, ARG_OPRL }, + { "negq/v", OPR(0x10,0x69), BASE, ARG_OPRZ1 }, /* pseudo */ + { "negq/v", OPRL(0x10,0x69), BASE, ARG_OPRLZ1 }, /* pseudo */ + { "subq/v", OPR(0x10,0x69), BASE, ARG_OPR }, + { "subq/v", OPRL(0x10,0x69), BASE, ARG_OPRL }, + { "cmple", OPR(0x10,0x6D), BASE, ARG_OPR }, + { "cmple", OPRL(0x10,0x6D), BASE, ARG_OPRL }, + + { "and", OPR(0x11,0x00), BASE, ARG_OPR }, + { "and", OPRL(0x11,0x00), BASE, ARG_OPRL }, + { "andnot", OPR(0x11,0x08), BASE, ARG_OPR }, /* alias */ + { "andnot", OPRL(0x11,0x08), BASE, ARG_OPRL }, /* alias */ + { "bic", OPR(0x11,0x08), BASE, ARG_OPR }, + { "bic", OPRL(0x11,0x08), BASE, ARG_OPRL }, + { "cmovlbs", OPR(0x11,0x14), BASE, ARG_OPR }, + { "cmovlbs", OPRL(0x11,0x14), BASE, ARG_OPRL }, + { "cmovlbc", OPR(0x11,0x16), BASE, ARG_OPR }, + { "cmovlbc", OPRL(0x11,0x16), BASE, ARG_OPRL }, + { "nop", OPR(0x11,0x20), BASE, { ZA, ZB, ZC } }, /* pseudo */ + { "clr", OPR(0x11,0x20), BASE, { ZA, ZB, RC } }, /* pseudo */ + { "mov", OPR(0x11,0x20), BASE, { ZA, RB, RC } }, /* pseudo */ + { "mov", OPR(0x11,0x20), BASE, { RA, RBA, RC } }, /* pseudo */ + { "mov", OPRL(0x11,0x20), BASE, { ZA, LIT, RC } }, /* pseudo */ + { "or", OPR(0x11,0x20), BASE, ARG_OPR }, /* alias */ + { "or", OPRL(0x11,0x20), BASE, ARG_OPRL }, /* alias */ + { "bis", OPR(0x11,0x20), BASE, ARG_OPR }, + { "bis", OPRL(0x11,0x20), BASE, ARG_OPRL }, + { "cmoveq", OPR(0x11,0x24), BASE, ARG_OPR }, + { "cmoveq", OPRL(0x11,0x24), BASE, ARG_OPRL }, + { "cmovne", OPR(0x11,0x26), BASE, ARG_OPR }, + { "cmovne", OPRL(0x11,0x26), BASE, ARG_OPRL }, + { "not", OPR(0x11,0x28), BASE, ARG_OPRZ1 }, /* pseudo */ + { "not", OPRL(0x11,0x28), BASE, ARG_OPRLZ1 }, /* pseudo */ + { "ornot", OPR(0x11,0x28), BASE, ARG_OPR }, + { "ornot", OPRL(0x11,0x28), BASE, ARG_OPRL }, + { "xor", OPR(0x11,0x40), BASE, ARG_OPR }, + { "xor", OPRL(0x11,0x40), BASE, ARG_OPRL }, + { "cmovlt", OPR(0x11,0x44), BASE, ARG_OPR }, + { "cmovlt", OPRL(0x11,0x44), BASE, ARG_OPRL }, + { "cmovge", OPR(0x11,0x46), BASE, ARG_OPR }, + { "cmovge", OPRL(0x11,0x46), BASE, ARG_OPRL }, + { "eqv", OPR(0x11,0x48), BASE, ARG_OPR }, + { "eqv", OPRL(0x11,0x48), BASE, ARG_OPRL }, + { "xornot", OPR(0x11,0x48), BASE, ARG_OPR }, /* alias */ + { "xornot", OPRL(0x11,0x48), BASE, ARG_OPRL }, /* alias */ + { "amask", OPR(0x11,0x61), BASE, ARG_OPRZ1 }, /* ev56 but */ + { "amask", OPRL(0x11,0x61), BASE, ARG_OPRLZ1 }, /* ev56 but */ + { "cmovle", OPR(0x11,0x64), BASE, ARG_OPR }, + { "cmovle", OPRL(0x11,0x64), BASE, ARG_OPRL }, + { "cmovgt", OPR(0x11,0x66), BASE, ARG_OPR }, + { "cmovgt", OPRL(0x11,0x66), BASE, ARG_OPRL }, + { "implver", OPRL_(0x11,0x6C)|(31<<21)|(1<<13), + 0xFFFFFFE0, BASE, { RC } }, /* ev56 but */ + + { "mskbl", OPR(0x12,0x02), BASE, ARG_OPR }, + { "mskbl", OPRL(0x12,0x02), BASE, ARG_OPRL }, + { "extbl", OPR(0x12,0x06), BASE, ARG_OPR }, + { "extbl", OPRL(0x12,0x06), BASE, ARG_OPRL }, + { "insbl", OPR(0x12,0x0B), BASE, ARG_OPR }, + { "insbl", OPRL(0x12,0x0B), BASE, ARG_OPRL }, + { "mskwl", OPR(0x12,0x12), BASE, ARG_OPR }, + { "mskwl", OPRL(0x12,0x12), BASE, ARG_OPRL }, + { "extwl", OPR(0x12,0x16), BASE, ARG_OPR }, + { "extwl", OPRL(0x12,0x16), BASE, ARG_OPRL }, + { "inswl", OPR(0x12,0x1B), BASE, ARG_OPR }, + { "inswl", OPRL(0x12,0x1B), BASE, ARG_OPRL }, + { "mskll", OPR(0x12,0x22), BASE, ARG_OPR }, + { "mskll", OPRL(0x12,0x22), BASE, ARG_OPRL }, + { "extll", OPR(0x12,0x26), BASE, ARG_OPR }, + { "extll", OPRL(0x12,0x26), BASE, ARG_OPRL }, + { "insll", OPR(0x12,0x2B), BASE, ARG_OPR }, + { "insll", OPRL(0x12,0x2B), BASE, ARG_OPRL }, + { "zap", OPR(0x12,0x30), BASE, ARG_OPR }, + { "zap", OPRL(0x12,0x30), BASE, ARG_OPRL }, + { "zapnot", OPR(0x12,0x31), BASE, ARG_OPR }, + { "zapnot", OPRL(0x12,0x31), BASE, ARG_OPRL }, + { "mskql", OPR(0x12,0x32), BASE, ARG_OPR }, + { "mskql", OPRL(0x12,0x32), BASE, ARG_OPRL }, + { "srl", OPR(0x12,0x34), BASE, ARG_OPR }, + { "srl", OPRL(0x12,0x34), BASE, ARG_OPRL }, + { "extql", OPR(0x12,0x36), BASE, ARG_OPR }, + { "extql", OPRL(0x12,0x36), BASE, ARG_OPRL }, + { "sll", OPR(0x12,0x39), BASE, ARG_OPR }, + { "sll", OPRL(0x12,0x39), BASE, ARG_OPRL }, + { "insql", OPR(0x12,0x3B), BASE, ARG_OPR }, + { "insql", OPRL(0x12,0x3B), BASE, ARG_OPRL }, + { "sra", OPR(0x12,0x3C), BASE, ARG_OPR }, + { "sra", OPRL(0x12,0x3C), BASE, ARG_OPRL }, + { "mskwh", OPR(0x12,0x52), BASE, ARG_OPR }, + { "mskwh", OPRL(0x12,0x52), BASE, ARG_OPRL }, + { "inswh", OPR(0x12,0x57), BASE, ARG_OPR }, + { "inswh", OPRL(0x12,0x57), BASE, ARG_OPRL }, + { "extwh", OPR(0x12,0x5A), BASE, ARG_OPR }, + { "extwh", OPRL(0x12,0x5A), BASE, ARG_OPRL }, + { "msklh", OPR(0x12,0x62), BASE, ARG_OPR }, + { "msklh", OPRL(0x12,0x62), BASE, ARG_OPRL }, + { "inslh", OPR(0x12,0x67), BASE, ARG_OPR }, + { "inslh", OPRL(0x12,0x67), BASE, ARG_OPRL }, + { "extlh", OPR(0x12,0x6A), BASE, ARG_OPR }, + { "extlh", OPRL(0x12,0x6A), BASE, ARG_OPRL }, + { "mskqh", OPR(0x12,0x72), BASE, ARG_OPR }, + { "mskqh", OPRL(0x12,0x72), BASE, ARG_OPRL }, + { "insqh", OPR(0x12,0x77), BASE, ARG_OPR }, + { "insqh", OPRL(0x12,0x77), BASE, ARG_OPRL }, + { "extqh", OPR(0x12,0x7A), BASE, ARG_OPR }, + { "extqh", OPRL(0x12,0x7A), BASE, ARG_OPRL }, + + { "mull", OPR(0x13,0x00), BASE, ARG_OPR }, + { "mull", OPRL(0x13,0x00), BASE, ARG_OPRL }, + { "mulq", OPR(0x13,0x20), BASE, ARG_OPR }, + { "mulq", OPRL(0x13,0x20), BASE, ARG_OPRL }, + { "umulh", OPR(0x13,0x30), BASE, ARG_OPR }, + { "umulh", OPRL(0x13,0x30), BASE, ARG_OPRL }, + { "mull/v", OPR(0x13,0x40), BASE, ARG_OPR }, + { "mull/v", OPRL(0x13,0x40), BASE, ARG_OPRL }, + { "mulq/v", OPR(0x13,0x60), BASE, ARG_OPR }, + { "mulq/v", OPRL(0x13,0x60), BASE, ARG_OPRL }, + + { "itofs", FP(0x14,0x004), CIX, { RA, ZB, FC } }, + { "sqrtf/c", FP(0x14,0x00A), CIX, ARG_FPZ1 }, + { "sqrts/c", FP(0x14,0x00B), CIX, ARG_FPZ1 }, + { "itoff", FP(0x14,0x014), CIX, { RA, ZB, FC } }, + { "itoft", FP(0x14,0x024), CIX, { RA, ZB, FC } }, + { "sqrtg/c", FP(0x14,0x02A), CIX, ARG_FPZ1 }, + { "sqrtt/c", FP(0x14,0x02B), CIX, ARG_FPZ1 }, + { "sqrts/m", FP(0x14,0x04B), CIX, ARG_FPZ1 }, + { "sqrtt/m", FP(0x14,0x06B), CIX, ARG_FPZ1 }, + { "sqrtf", FP(0x14,0x08A), CIX, ARG_FPZ1 }, + { "sqrts", FP(0x14,0x08B), CIX, ARG_FPZ1 }, + { "sqrtg", FP(0x14,0x0AA), CIX, ARG_FPZ1 }, + { "sqrtt", FP(0x14,0x0AB), CIX, ARG_FPZ1 }, + { "sqrts/d", FP(0x14,0x0CB), CIX, ARG_FPZ1 }, + { "sqrtt/d", FP(0x14,0x0EB), CIX, ARG_FPZ1 }, + { "sqrtf/uc", FP(0x14,0x10A), CIX, ARG_FPZ1 }, + { "sqrts/uc", FP(0x14,0x10B), CIX, ARG_FPZ1 }, + { "sqrtg/uc", FP(0x14,0x12A), CIX, ARG_FPZ1 }, + { "sqrtt/uc", FP(0x14,0x12B), CIX, ARG_FPZ1 }, + { "sqrts/um", FP(0x14,0x14B), CIX, ARG_FPZ1 }, + { "sqrtt/um", FP(0x14,0x16B), CIX, ARG_FPZ1 }, + { "sqrtf/u", FP(0x14,0x18A), CIX, ARG_FPZ1 }, + { "sqrts/u", FP(0x14,0x18B), CIX, ARG_FPZ1 }, + { "sqrtg/u", FP(0x14,0x1AA), CIX, ARG_FPZ1 }, + { "sqrtt/u", FP(0x14,0x1AB), CIX, ARG_FPZ1 }, + { "sqrts/ud", FP(0x14,0x1CB), CIX, ARG_FPZ1 }, + { "sqrtt/ud", FP(0x14,0x1EB), CIX, ARG_FPZ1 }, + { "sqrtf/sc", FP(0x14,0x40A), CIX, ARG_FPZ1 }, + { "sqrtg/sc", FP(0x14,0x42A), CIX, ARG_FPZ1 }, + { "sqrtf/s", FP(0x14,0x48A), CIX, ARG_FPZ1 }, + { "sqrtg/s", FP(0x14,0x4AA), CIX, ARG_FPZ1 }, + { "sqrtf/suc", FP(0x14,0x50A), CIX, ARG_FPZ1 }, + { "sqrts/suc", FP(0x14,0x50B), CIX, ARG_FPZ1 }, + { "sqrtg/suc", FP(0x14,0x52A), CIX, ARG_FPZ1 }, + { "sqrtt/suc", FP(0x14,0x52B), CIX, ARG_FPZ1 }, + { "sqrts/sum", FP(0x14,0x54B), CIX, ARG_FPZ1 }, + { "sqrtt/sum", FP(0x14,0x56B), CIX, ARG_FPZ1 }, + { "sqrtf/su", FP(0x14,0x58A), CIX, ARG_FPZ1 }, + { "sqrts/su", FP(0x14,0x58B), CIX, ARG_FPZ1 }, + { "sqrtg/su", FP(0x14,0x5AA), CIX, ARG_FPZ1 }, + { "sqrtt/su", FP(0x14,0x5AB), CIX, ARG_FPZ1 }, + { "sqrts/sud", FP(0x14,0x5CB), CIX, ARG_FPZ1 }, + { "sqrtt/sud", FP(0x14,0x5EB), CIX, ARG_FPZ1 }, + { "sqrts/suic", FP(0x14,0x70B), CIX, ARG_FPZ1 }, + { "sqrtt/suic", FP(0x14,0x72B), CIX, ARG_FPZ1 }, + { "sqrts/suim", FP(0x14,0x74B), CIX, ARG_FPZ1 }, + { "sqrtt/suim", FP(0x14,0x76B), CIX, ARG_FPZ1 }, + { "sqrts/sui", FP(0x14,0x78B), CIX, ARG_FPZ1 }, + { "sqrtt/sui", FP(0x14,0x7AB), CIX, ARG_FPZ1 }, + { "sqrts/suid", FP(0x14,0x7CB), CIX, ARG_FPZ1 }, + { "sqrtt/suid", FP(0x14,0x7EB), CIX, ARG_FPZ1 }, + + { "addf/c", FP(0x15,0x000), BASE, ARG_FP }, + { "subf/c", FP(0x15,0x001), BASE, ARG_FP }, + { "mulf/c", FP(0x15,0x002), BASE, ARG_FP }, + { "divf/c", FP(0x15,0x003), BASE, ARG_FP }, + { "cvtdg/c", FP(0x15,0x01E), BASE, ARG_FPZ1 }, + { "addg/c", FP(0x15,0x020), BASE, ARG_FP }, + { "subg/c", FP(0x15,0x021), BASE, ARG_FP }, + { "mulg/c", FP(0x15,0x022), BASE, ARG_FP }, + { "divg/c", FP(0x15,0x023), BASE, ARG_FP }, + { "cvtgf/c", FP(0x15,0x02C), BASE, ARG_FPZ1 }, + { "cvtgd/c", FP(0x15,0x02D), BASE, ARG_FPZ1 }, + { "cvtgq/c", FP(0x15,0x02F), BASE, ARG_FPZ1 }, + { "cvtqf/c", FP(0x15,0x03C), BASE, ARG_FPZ1 }, + { "cvtqg/c", FP(0x15,0x03E), BASE, ARG_FPZ1 }, + { "addf", FP(0x15,0x080), BASE, ARG_FP }, + { "negf", FP(0x15,0x081), BASE, ARG_FPZ1 }, /* pseudo */ + { "subf", FP(0x15,0x081), BASE, ARG_FP }, + { "mulf", FP(0x15,0x082), BASE, ARG_FP }, + { "divf", FP(0x15,0x083), BASE, ARG_FP }, + { "cvtdg", FP(0x15,0x09E), BASE, ARG_FPZ1 }, + { "addg", FP(0x15,0x0A0), BASE, ARG_FP }, + { "negg", FP(0x15,0x0A1), BASE, ARG_FPZ1 }, /* pseudo */ + { "subg", FP(0x15,0x0A1), BASE, ARG_FP }, + { "mulg", FP(0x15,0x0A2), BASE, ARG_FP }, + { "divg", FP(0x15,0x0A3), BASE, ARG_FP }, + { "cmpgeq", FP(0x15,0x0A5), BASE, ARG_FP }, + { "cmpglt", FP(0x15,0x0A6), BASE, ARG_FP }, + { "cmpgle", FP(0x15,0x0A7), BASE, ARG_FP }, + { "cvtgf", FP(0x15,0x0AC), BASE, ARG_FPZ1 }, + { "cvtgd", FP(0x15,0x0AD), BASE, ARG_FPZ1 }, + { "cvtgq", FP(0x15,0x0AF), BASE, ARG_FPZ1 }, + { "cvtqf", FP(0x15,0x0BC), BASE, ARG_FPZ1 }, + { "cvtqg", FP(0x15,0x0BE), BASE, ARG_FPZ1 }, + { "addf/uc", FP(0x15,0x100), BASE, ARG_FP }, + { "subf/uc", FP(0x15,0x101), BASE, ARG_FP }, + { "mulf/uc", FP(0x15,0x102), BASE, ARG_FP }, + { "divf/uc", FP(0x15,0x103), BASE, ARG_FP }, + { "cvtdg/uc", FP(0x15,0x11E), BASE, ARG_FPZ1 }, + { "addg/uc", FP(0x15,0x120), BASE, ARG_FP }, + { "subg/uc", FP(0x15,0x121), BASE, ARG_FP }, + { "mulg/uc", FP(0x15,0x122), BASE, ARG_FP }, + { "divg/uc", FP(0x15,0x123), BASE, ARG_FP }, + { "cvtgf/uc", FP(0x15,0x12C), BASE, ARG_FPZ1 }, + { "cvtgd/uc", FP(0x15,0x12D), BASE, ARG_FPZ1 }, + { "cvtgq/vc", FP(0x15,0x12F), BASE, ARG_FPZ1 }, + { "addf/u", FP(0x15,0x180), BASE, ARG_FP }, + { "subf/u", FP(0x15,0x181), BASE, ARG_FP }, + { "mulf/u", FP(0x15,0x182), BASE, ARG_FP }, + { "divf/u", FP(0x15,0x183), BASE, ARG_FP }, + { "cvtdg/u", FP(0x15,0x19E), BASE, ARG_FPZ1 }, + { "addg/u", FP(0x15,0x1A0), BASE, ARG_FP }, + { "subg/u", FP(0x15,0x1A1), BASE, ARG_FP }, + { "mulg/u", FP(0x15,0x1A2), BASE, ARG_FP }, + { "divg/u", FP(0x15,0x1A3), BASE, ARG_FP }, + { "cvtgf/u", FP(0x15,0x1AC), BASE, ARG_FPZ1 }, + { "cvtgd/u", FP(0x15,0x1AD), BASE, ARG_FPZ1 }, + { "cvtgq/v", FP(0x15,0x1AF), BASE, ARG_FPZ1 }, + { "addf/sc", FP(0x15,0x400), BASE, ARG_FP }, + { "subf/sc", FP(0x15,0x401), BASE, ARG_FP }, + { "mulf/sc", FP(0x15,0x402), BASE, ARG_FP }, + { "divf/sc", FP(0x15,0x403), BASE, ARG_FP }, + { "cvtdg/sc", FP(0x15,0x41E), BASE, ARG_FPZ1 }, + { "addg/sc", FP(0x15,0x420), BASE, ARG_FP }, + { "subg/sc", FP(0x15,0x421), BASE, ARG_FP }, + { "mulg/sc", FP(0x15,0x422), BASE, ARG_FP }, + { "divg/sc", FP(0x15,0x423), BASE, ARG_FP }, + { "cvtgf/sc", FP(0x15,0x42C), BASE, ARG_FPZ1 }, + { "cvtgd/sc", FP(0x15,0x42D), BASE, ARG_FPZ1 }, + { "cvtgq/sc", FP(0x15,0x42F), BASE, ARG_FPZ1 }, + { "addf/s", FP(0x15,0x480), BASE, ARG_FP }, + { "negf/s", FP(0x15,0x481), BASE, ARG_FPZ1 }, /* pseudo */ + { "subf/s", FP(0x15,0x481), BASE, ARG_FP }, + { "mulf/s", FP(0x15,0x482), BASE, ARG_FP }, + { "divf/s", FP(0x15,0x483), BASE, ARG_FP }, + { "cvtdg/s", FP(0x15,0x49E), BASE, ARG_FPZ1 }, + { "addg/s", FP(0x15,0x4A0), BASE, ARG_FP }, + { "negg/s", FP(0x15,0x4A1), BASE, ARG_FPZ1 }, /* pseudo */ + { "subg/s", FP(0x15,0x4A1), BASE, ARG_FP }, + { "mulg/s", FP(0x15,0x4A2), BASE, ARG_FP }, + { "divg/s", FP(0x15,0x4A3), BASE, ARG_FP }, + { "cmpgeq/s", FP(0x15,0x4A5), BASE, ARG_FP }, + { "cmpglt/s", FP(0x15,0x4A6), BASE, ARG_FP }, + { "cmpgle/s", FP(0x15,0x4A7), BASE, ARG_FP }, + { "cvtgf/s", FP(0x15,0x4AC), BASE, ARG_FPZ1 }, + { "cvtgd/s", FP(0x15,0x4AD), BASE, ARG_FPZ1 }, + { "cvtgq/s", FP(0x15,0x4AF), BASE, ARG_FPZ1 }, + { "addf/suc", FP(0x15,0x500), BASE, ARG_FP }, + { "subf/suc", FP(0x15,0x501), BASE, ARG_FP }, + { "mulf/suc", FP(0x15,0x502), BASE, ARG_FP }, + { "divf/suc", FP(0x15,0x503), BASE, ARG_FP }, + { "cvtdg/suc", FP(0x15,0x51E), BASE, ARG_FPZ1 }, + { "addg/suc", FP(0x15,0x520), BASE, ARG_FP }, + { "subg/suc", FP(0x15,0x521), BASE, ARG_FP }, + { "mulg/suc", FP(0x15,0x522), BASE, ARG_FP }, + { "divg/suc", FP(0x15,0x523), BASE, ARG_FP }, + { "cvtgf/suc", FP(0x15,0x52C), BASE, ARG_FPZ1 }, + { "cvtgd/suc", FP(0x15,0x52D), BASE, ARG_FPZ1 }, + { "cvtgq/svc", FP(0x15,0x52F), BASE, ARG_FPZ1 }, + { "addf/su", FP(0x15,0x580), BASE, ARG_FP }, + { "subf/su", FP(0x15,0x581), BASE, ARG_FP }, + { "mulf/su", FP(0x15,0x582), BASE, ARG_FP }, + { "divf/su", FP(0x15,0x583), BASE, ARG_FP }, + { "cvtdg/su", FP(0x15,0x59E), BASE, ARG_FPZ1 }, + { "addg/su", FP(0x15,0x5A0), BASE, ARG_FP }, + { "subg/su", FP(0x15,0x5A1), BASE, ARG_FP }, + { "mulg/su", FP(0x15,0x5A2), BASE, ARG_FP }, + { "divg/su", FP(0x15,0x5A3), BASE, ARG_FP }, + { "cvtgf/su", FP(0x15,0x5AC), BASE, ARG_FPZ1 }, + { "cvtgd/su", FP(0x15,0x5AD), BASE, ARG_FPZ1 }, + { "cvtgq/sv", FP(0x15,0x5AF), BASE, ARG_FPZ1 }, + + { "adds/c", FP(0x16,0x000), BASE, ARG_FP }, + { "subs/c", FP(0x16,0x001), BASE, ARG_FP }, + { "muls/c", FP(0x16,0x002), BASE, ARG_FP }, + { "divs/c", FP(0x16,0x003), BASE, ARG_FP }, + { "addt/c", FP(0x16,0x020), BASE, ARG_FP }, + { "subt/c", FP(0x16,0x021), BASE, ARG_FP }, + { "mult/c", FP(0x16,0x022), BASE, ARG_FP }, + { "divt/c", FP(0x16,0x023), BASE, ARG_FP }, + { "cvtts/c", FP(0x16,0x02C), BASE, ARG_FPZ1 }, + { "cvttq/c", FP(0x16,0x02F), BASE, ARG_FPZ1 }, + { "cvtqs/c", FP(0x16,0x03C), BASE, ARG_FPZ1 }, + { "cvtqt/c", FP(0x16,0x03E), BASE, ARG_FPZ1 }, + { "adds/m", FP(0x16,0x040), BASE, ARG_FP }, + { "subs/m", FP(0x16,0x041), BASE, ARG_FP }, + { "muls/m", FP(0x16,0x042), BASE, ARG_FP }, + { "divs/m", FP(0x16,0x043), BASE, ARG_FP }, + { "addt/m", FP(0x16,0x060), BASE, ARG_FP }, + { "subt/m", FP(0x16,0x061), BASE, ARG_FP }, + { "mult/m", FP(0x16,0x062), BASE, ARG_FP }, + { "divt/m", FP(0x16,0x063), BASE, ARG_FP }, + { "cvtts/m", FP(0x16,0x06C), BASE, ARG_FPZ1 }, + { "cvttq/m", FP(0x16,0x06F), BASE, ARG_FPZ1 }, + { "cvtqs/m", FP(0x16,0x07C), BASE, ARG_FPZ1 }, + { "cvtqt/m", FP(0x16,0x07E), BASE, ARG_FPZ1 }, + { "adds", FP(0x16,0x080), BASE, ARG_FP }, + { "negs", FP(0x16,0x081), BASE, ARG_FPZ1 }, /* pseudo */ + { "subs", FP(0x16,0x081), BASE, ARG_FP }, + { "muls", FP(0x16,0x082), BASE, ARG_FP }, + { "divs", FP(0x16,0x083), BASE, ARG_FP }, + { "addt", FP(0x16,0x0A0), BASE, ARG_FP }, + { "negt", FP(0x16,0x0A1), BASE, ARG_FPZ1 }, /* pseudo */ + { "subt", FP(0x16,0x0A1), BASE, ARG_FP }, + { "mult", FP(0x16,0x0A2), BASE, ARG_FP }, + { "divt", FP(0x16,0x0A3), BASE, ARG_FP }, + { "cmptun", FP(0x16,0x0A4), BASE, ARG_FP }, + { "cmpteq", FP(0x16,0x0A5), BASE, ARG_FP }, + { "cmptlt", FP(0x16,0x0A6), BASE, ARG_FP }, + { "cmptle", FP(0x16,0x0A7), BASE, ARG_FP }, + { "cvtts", FP(0x16,0x0AC), BASE, ARG_FPZ1 }, + { "cvttq", FP(0x16,0x0AF), BASE, ARG_FPZ1 }, + { "cvtqs", FP(0x16,0x0BC), BASE, ARG_FPZ1 }, + { "cvtqt", FP(0x16,0x0BE), BASE, ARG_FPZ1 }, + { "adds/d", FP(0x16,0x0C0), BASE, ARG_FP }, + { "subs/d", FP(0x16,0x0C1), BASE, ARG_FP }, + { "muls/d", FP(0x16,0x0C2), BASE, ARG_FP }, + { "divs/d", FP(0x16,0x0C3), BASE, ARG_FP }, + { "addt/d", FP(0x16,0x0E0), BASE, ARG_FP }, + { "subt/d", FP(0x16,0x0E1), BASE, ARG_FP }, + { "mult/d", FP(0x16,0x0E2), BASE, ARG_FP }, + { "divt/d", FP(0x16,0x0E3), BASE, ARG_FP }, + { "cvtts/d", FP(0x16,0x0EC), BASE, ARG_FPZ1 }, + { "cvttq/d", FP(0x16,0x0EF), BASE, ARG_FPZ1 }, + { "cvtqs/d", FP(0x16,0x0FC), BASE, ARG_FPZ1 }, + { "cvtqt/d", FP(0x16,0x0FE), BASE, ARG_FPZ1 }, + { "adds/uc", FP(0x16,0x100), BASE, ARG_FP }, + { "subs/uc", FP(0x16,0x101), BASE, ARG_FP }, + { "muls/uc", FP(0x16,0x102), BASE, ARG_FP }, + { "divs/uc", FP(0x16,0x103), BASE, ARG_FP }, + { "addt/uc", FP(0x16,0x120), BASE, ARG_FP }, + { "subt/uc", FP(0x16,0x121), BASE, ARG_FP }, + { "mult/uc", FP(0x16,0x122), BASE, ARG_FP }, + { "divt/uc", FP(0x16,0x123), BASE, ARG_FP }, + { "cvtts/uc", FP(0x16,0x12C), BASE, ARG_FPZ1 }, + { "cvttq/vc", FP(0x16,0x12F), BASE, ARG_FPZ1 }, + { "adds/um", FP(0x16,0x140), BASE, ARG_FP }, + { "subs/um", FP(0x16,0x141), BASE, ARG_FP }, + { "muls/um", FP(0x16,0x142), BASE, ARG_FP }, + { "divs/um", FP(0x16,0x143), BASE, ARG_FP }, + { "addt/um", FP(0x16,0x160), BASE, ARG_FP }, + { "subt/um", FP(0x16,0x161), BASE, ARG_FP }, + { "mult/um", FP(0x16,0x162), BASE, ARG_FP }, + { "divt/um", FP(0x16,0x163), BASE, ARG_FP }, + { "cvtts/um", FP(0x16,0x16C), BASE, ARG_FPZ1 }, + { "cvttq/vm", FP(0x16,0x16F), BASE, ARG_FPZ1 }, + { "adds/u", FP(0x16,0x180), BASE, ARG_FP }, + { "subs/u", FP(0x16,0x181), BASE, ARG_FP }, + { "muls/u", FP(0x16,0x182), BASE, ARG_FP }, + { "divs/u", FP(0x16,0x183), BASE, ARG_FP }, + { "addt/u", FP(0x16,0x1A0), BASE, ARG_FP }, + { "subt/u", FP(0x16,0x1A1), BASE, ARG_FP }, + { "mult/u", FP(0x16,0x1A2), BASE, ARG_FP }, + { "divt/u", FP(0x16,0x1A3), BASE, ARG_FP }, + { "cvtts/u", FP(0x16,0x1AC), BASE, ARG_FPZ1 }, + { "cvttq/v", FP(0x16,0x1AF), BASE, ARG_FPZ1 }, + { "adds/ud", FP(0x16,0x1C0), BASE, ARG_FP }, + { "subs/ud", FP(0x16,0x1C1), BASE, ARG_FP }, + { "muls/ud", FP(0x16,0x1C2), BASE, ARG_FP }, + { "divs/ud", FP(0x16,0x1C3), BASE, ARG_FP }, + { "addt/ud", FP(0x16,0x1E0), BASE, ARG_FP }, + { "subt/ud", FP(0x16,0x1E1), BASE, ARG_FP }, + { "mult/ud", FP(0x16,0x1E2), BASE, ARG_FP }, + { "divt/ud", FP(0x16,0x1E3), BASE, ARG_FP }, + { "cvtts/ud", FP(0x16,0x1EC), BASE, ARG_FPZ1 }, + { "cvttq/vd", FP(0x16,0x1EF), BASE, ARG_FPZ1 }, + { "cvtst", FP(0x16,0x2AC), BASE, ARG_FPZ1 }, + { "adds/suc", FP(0x16,0x500), BASE, ARG_FP }, + { "subs/suc", FP(0x16,0x501), BASE, ARG_FP }, + { "muls/suc", FP(0x16,0x502), BASE, ARG_FP }, + { "divs/suc", FP(0x16,0x503), BASE, ARG_FP }, + { "addt/suc", FP(0x16,0x520), BASE, ARG_FP }, + { "subt/suc", FP(0x16,0x521), BASE, ARG_FP }, + { "mult/suc", FP(0x16,0x522), BASE, ARG_FP }, + { "divt/suc", FP(0x16,0x523), BASE, ARG_FP }, + { "cvtts/suc", FP(0x16,0x52C), BASE, ARG_FPZ1 }, + { "cvttq/svc", FP(0x16,0x52F), BASE, ARG_FPZ1 }, + { "adds/sum", FP(0x16,0x540), BASE, ARG_FP }, + { "subs/sum", FP(0x16,0x541), BASE, ARG_FP }, + { "muls/sum", FP(0x16,0x542), BASE, ARG_FP }, + { "divs/sum", FP(0x16,0x543), BASE, ARG_FP }, + { "addt/sum", FP(0x16,0x560), BASE, ARG_FP }, + { "subt/sum", FP(0x16,0x561), BASE, ARG_FP }, + { "mult/sum", FP(0x16,0x562), BASE, ARG_FP }, + { "divt/sum", FP(0x16,0x563), BASE, ARG_FP }, + { "cvtts/sum", FP(0x16,0x56C), BASE, ARG_FPZ1 }, + { "cvttq/svm", FP(0x16,0x56F), BASE, ARG_FPZ1 }, + { "adds/su", FP(0x16,0x580), BASE, ARG_FP }, + { "negs/su", FP(0x16,0x581), BASE, ARG_FPZ1 }, /* pseudo */ + { "subs/su", FP(0x16,0x581), BASE, ARG_FP }, + { "muls/su", FP(0x16,0x582), BASE, ARG_FP }, + { "divs/su", FP(0x16,0x583), BASE, ARG_FP }, + { "addt/su", FP(0x16,0x5A0), BASE, ARG_FP }, + { "negt/su", FP(0x16,0x5A1), BASE, ARG_FPZ1 }, /* pseudo */ + { "subt/su", FP(0x16,0x5A1), BASE, ARG_FP }, + { "mult/su", FP(0x16,0x5A2), BASE, ARG_FP }, + { "divt/su", FP(0x16,0x5A3), BASE, ARG_FP }, + { "cmptun/su", FP(0x16,0x5A4), BASE, ARG_FP }, + { "cmpteq/su", FP(0x16,0x5A5), BASE, ARG_FP }, + { "cmptlt/su", FP(0x16,0x5A6), BASE, ARG_FP }, + { "cmptle/su", FP(0x16,0x5A7), BASE, ARG_FP }, + { "cvtts/su", FP(0x16,0x5AC), BASE, ARG_FPZ1 }, + { "cvttq/sv", FP(0x16,0x5AF), BASE, ARG_FPZ1 }, + { "adds/sud", FP(0x16,0x5C0), BASE, ARG_FP }, + { "subs/sud", FP(0x16,0x5C1), BASE, ARG_FP }, + { "muls/sud", FP(0x16,0x5C2), BASE, ARG_FP }, + { "divs/sud", FP(0x16,0x5C3), BASE, ARG_FP }, + { "addt/sud", FP(0x16,0x5E0), BASE, ARG_FP }, + { "subt/sud", FP(0x16,0x5E1), BASE, ARG_FP }, + { "mult/sud", FP(0x16,0x5E2), BASE, ARG_FP }, + { "divt/sud", FP(0x16,0x5E3), BASE, ARG_FP }, + { "cvtts/sud", FP(0x16,0x5EC), BASE, ARG_FPZ1 }, + { "cvttq/svd", FP(0x16,0x5EF), BASE, ARG_FPZ1 }, + { "cvtst/s", FP(0x16,0x6AC), BASE, ARG_FPZ1 }, + { "adds/suic", FP(0x16,0x700), BASE, ARG_FP }, + { "subs/suic", FP(0x16,0x701), BASE, ARG_FP }, + { "muls/suic", FP(0x16,0x702), BASE, ARG_FP }, + { "divs/suic", FP(0x16,0x703), BASE, ARG_FP }, + { "addt/suic", FP(0x16,0x720), BASE, ARG_FP }, + { "subt/suic", FP(0x16,0x721), BASE, ARG_FP }, + { "mult/suic", FP(0x16,0x722), BASE, ARG_FP }, + { "divt/suic", FP(0x16,0x723), BASE, ARG_FP }, + { "cvtts/suic", FP(0x16,0x72C), BASE, ARG_FPZ1 }, + { "cvttq/svic", FP(0x16,0x72F), BASE, ARG_FPZ1 }, + { "cvtqs/suic", FP(0x16,0x73C), BASE, ARG_FPZ1 }, + { "cvtqt/suic", FP(0x16,0x73E), BASE, ARG_FPZ1 }, + { "adds/suim", FP(0x16,0x740), BASE, ARG_FP }, + { "subs/suim", FP(0x16,0x741), BASE, ARG_FP }, + { "muls/suim", FP(0x16,0x742), BASE, ARG_FP }, + { "divs/suim", FP(0x16,0x743), BASE, ARG_FP }, + { "addt/suim", FP(0x16,0x760), BASE, ARG_FP }, + { "subt/suim", FP(0x16,0x761), BASE, ARG_FP }, + { "mult/suim", FP(0x16,0x762), BASE, ARG_FP }, + { "divt/suim", FP(0x16,0x763), BASE, ARG_FP }, + { "cvtts/suim", FP(0x16,0x76C), BASE, ARG_FPZ1 }, + { "cvttq/svim", FP(0x16,0x76F), BASE, ARG_FPZ1 }, + { "cvtqs/suim", FP(0x16,0x77C), BASE, ARG_FPZ1 }, + { "cvtqt/suim", FP(0x16,0x77E), BASE, ARG_FPZ1 }, + { "adds/sui", FP(0x16,0x780), BASE, ARG_FP }, + { "negs/sui", FP(0x16,0x781), BASE, ARG_FPZ1 }, /* pseudo */ + { "subs/sui", FP(0x16,0x781), BASE, ARG_FP }, + { "muls/sui", FP(0x16,0x782), BASE, ARG_FP }, + { "divs/sui", FP(0x16,0x783), BASE, ARG_FP }, + { "addt/sui", FP(0x16,0x7A0), BASE, ARG_FP }, + { "negt/sui", FP(0x16,0x7A1), BASE, ARG_FPZ1 }, /* pseudo */ + { "subt/sui", FP(0x16,0x7A1), BASE, ARG_FP }, + { "mult/sui", FP(0x16,0x7A2), BASE, ARG_FP }, + { "divt/sui", FP(0x16,0x7A3), BASE, ARG_FP }, + { "cvtts/sui", FP(0x16,0x7AC), BASE, ARG_FPZ1 }, + { "cvttq/svi", FP(0x16,0x7AF), BASE, ARG_FPZ1 }, + { "cvtqs/sui", FP(0x16,0x7BC), BASE, ARG_FPZ1 }, + { "cvtqt/sui", FP(0x16,0x7BE), BASE, ARG_FPZ1 }, + { "adds/suid", FP(0x16,0x7C0), BASE, ARG_FP }, + { "subs/suid", FP(0x16,0x7C1), BASE, ARG_FP }, + { "muls/suid", FP(0x16,0x7C2), BASE, ARG_FP }, + { "divs/suid", FP(0x16,0x7C3), BASE, ARG_FP }, + { "addt/suid", FP(0x16,0x7E0), BASE, ARG_FP }, + { "subt/suid", FP(0x16,0x7E1), BASE, ARG_FP }, + { "mult/suid", FP(0x16,0x7E2), BASE, ARG_FP }, + { "divt/suid", FP(0x16,0x7E3), BASE, ARG_FP }, + { "cvtts/suid", FP(0x16,0x7EC), BASE, ARG_FPZ1 }, + { "cvttq/svid", FP(0x16,0x7EF), BASE, ARG_FPZ1 }, + { "cvtqs/suid", FP(0x16,0x7FC), BASE, ARG_FPZ1 }, + { "cvtqt/suid", FP(0x16,0x7FE), BASE, ARG_FPZ1 }, + + { "cvtlq", FP(0x17,0x010), BASE, ARG_FPZ1 }, + { "fnop", FP(0x17,0x020), BASE, { ZA, ZB, ZC } }, /* pseudo */ + { "fclr", FP(0x17,0x020), BASE, { ZA, ZB, FC } }, /* pseudo */ + { "fabs", FP(0x17,0x020), BASE, ARG_FPZ1 }, /* pseudo */ + { "fmov", FP(0x17,0x020), BASE, { FA, RBA, FC } }, /* pseudo */ + { "cpys", FP(0x17,0x020), BASE, ARG_FP }, + { "fneg", FP(0x17,0x021), BASE, { FA, RBA, FC } }, /* pseudo */ + { "cpysn", FP(0x17,0x021), BASE, ARG_FP }, + { "cpyse", FP(0x17,0x022), BASE, ARG_FP }, + { "mt_fpcr", FP(0x17,0x024), BASE, { FA, RBA, RCA } }, + { "mf_fpcr", FP(0x17,0x025), BASE, { FA, RBA, RCA } }, + { "fcmoveq", FP(0x17,0x02A), BASE, ARG_FP }, + { "fcmovne", FP(0x17,0x02B), BASE, ARG_FP }, + { "fcmovlt", FP(0x17,0x02C), BASE, ARG_FP }, + { "fcmovge", FP(0x17,0x02D), BASE, ARG_FP }, + { "fcmovle", FP(0x17,0x02E), BASE, ARG_FP }, + { "fcmovgt", FP(0x17,0x02F), BASE, ARG_FP }, + { "cvtql", FP(0x17,0x030), BASE, ARG_FPZ1 }, + { "cvtql/v", FP(0x17,0x130), BASE, ARG_FPZ1 }, + { "cvtql/sv", FP(0x17,0x530), BASE, ARG_FPZ1 }, + + { "trapb", MFC(0x18,0x0000), BASE, ARG_NONE }, + { "draint", MFC(0x18,0x0000), BASE, ARG_NONE }, /* alias */ + { "excb", MFC(0x18,0x0400), BASE, ARG_NONE }, + { "mb", MFC(0x18,0x4000), BASE, ARG_NONE }, + { "wmb", MFC(0x18,0x4400), BASE, ARG_NONE }, + { "fetch", MFC(0x18,0x8000), BASE, { ZA, PRB } }, + { "fetch_m", MFC(0x18,0xA000), BASE, { ZA, PRB } }, + { "rpcc", MFC(0x18,0xC000), BASE, { RA } }, + { "rc", MFC(0x18,0xE000), BASE, { RA } }, + { "ecb", MFC(0x18,0xE800), BASE, { ZA, PRB } }, /* ev56 una */ + { "rs", MFC(0x18,0xF000), BASE, { RA } }, + { "wh64", MFC(0x18,0xF800), BASE, { ZA, PRB } }, /* ev56 una */ + { "wh64en", MFC(0x18,0xFC00), BASE, { ZA, PRB } }, /* ev7 una */ + + { "hw_mfpr", OPR(0x19,0x00), EV4, { RA, RBA, EV4EXTHWINDEX } }, + { "hw_mfpr", OP(0x19), OP_MASK, EV5, { RA, RBA, EV5HWINDEX } }, + { "hw_mfpr", OP(0x19), OP_MASK, EV6, { RA, ZB, EV6HWINDEX } }, + { "hw_mfpr/i", OPR(0x19,0x01), EV4, ARG_EV4HWMPR }, + { "hw_mfpr/a", OPR(0x19,0x02), EV4, ARG_EV4HWMPR }, + { "hw_mfpr/ai", OPR(0x19,0x03), EV4, ARG_EV4HWMPR }, + { "hw_mfpr/p", OPR(0x19,0x04), EV4, ARG_EV4HWMPR }, + { "hw_mfpr/pi", OPR(0x19,0x05), EV4, ARG_EV4HWMPR }, + { "hw_mfpr/pa", OPR(0x19,0x06), EV4, ARG_EV4HWMPR }, + { "hw_mfpr/pai", OPR(0x19,0x07), EV4, ARG_EV4HWMPR }, + { "pal19", PCD(0x19), BASE, ARG_PCD }, + + { "jmp", MBR_(0x1A,0), MBR_MASK | 0x3FFF, /* pseudo */ + BASE, { ZA, CPRB } }, + { "jmp", MBR(0x1A,0), BASE, { RA, CPRB, JMPHINT } }, + { "jsr", MBR(0x1A,1), BASE, { RA, CPRB, JMPHINT } }, + { "ret", MBR_(0x1A,2) | (31 << 21) | (26 << 16) | 1,/* pseudo */ + 0xFFFFFFFF, BASE, { 0 } }, + { "ret", MBR(0x1A,2), BASE, { RA, CPRB, RETHINT } }, + { "jcr", MBR(0x1A,3), BASE, { RA, CPRB, RETHINT } }, /* alias */ + { "jsr_coroutine", MBR(0x1A,3), BASE, { RA, CPRB, RETHINT } }, + + { "hw_ldl", EV4HWMEM(0x1B,0x0), EV4, ARG_EV4HWMEM }, + { "hw_ldl", EV5HWMEM(0x1B,0x00), EV5, ARG_EV5HWMEM }, + { "hw_ldl", EV6HWMEM(0x1B,0x8), EV6, ARG_EV6HWMEM }, + { "hw_ldl/a", EV4HWMEM(0x1B,0x4), EV4, ARG_EV4HWMEM }, + { "hw_ldl/a", EV5HWMEM(0x1B,0x10), EV5, ARG_EV5HWMEM }, + { "hw_ldl/a", EV6HWMEM(0x1B,0xC), EV6, ARG_EV6HWMEM }, + { "hw_ldl/al", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM }, + { "hw_ldl/ar", EV4HWMEM(0x1B,0x6), EV4, ARG_EV4HWMEM }, + { "hw_ldl/av", EV5HWMEM(0x1B,0x12), EV5, ARG_EV5HWMEM }, + { "hw_ldl/avl", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM }, + { "hw_ldl/aw", EV5HWMEM(0x1B,0x18), EV5, ARG_EV5HWMEM }, + { "hw_ldl/awl", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM }, + { "hw_ldl/awv", EV5HWMEM(0x1B,0x1a), EV5, ARG_EV5HWMEM }, + { "hw_ldl/awvl", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM }, + { "hw_ldl/l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM }, + { "hw_ldl/p", EV4HWMEM(0x1B,0x8), EV4, ARG_EV4HWMEM }, + { "hw_ldl/p", EV5HWMEM(0x1B,0x20), EV5, ARG_EV5HWMEM }, + { "hw_ldl/p", EV6HWMEM(0x1B,0x0), EV6, ARG_EV6HWMEM }, + { "hw_ldl/pa", EV4HWMEM(0x1B,0xC), EV4, ARG_EV4HWMEM }, + { "hw_ldl/pa", EV5HWMEM(0x1B,0x30), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pal", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM }, + { "hw_ldl/par", EV4HWMEM(0x1B,0xE), EV4, ARG_EV4HWMEM }, + { "hw_ldl/pav", EV5HWMEM(0x1B,0x32), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pavl", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM }, + { "hw_ldl/paw", EV5HWMEM(0x1B,0x38), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pawl", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pawv", EV5HWMEM(0x1B,0x3a), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pawvl", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pl", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pr", EV4HWMEM(0x1B,0xA), EV4, ARG_EV4HWMEM }, + { "hw_ldl/pv", EV5HWMEM(0x1B,0x22), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pvl", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pw", EV5HWMEM(0x1B,0x28), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pwl", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pwv", EV5HWMEM(0x1B,0x2a), EV5, ARG_EV5HWMEM }, + { "hw_ldl/pwvl", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM }, + { "hw_ldl/r", EV4HWMEM(0x1B,0x2), EV4, ARG_EV4HWMEM }, + { "hw_ldl/v", EV5HWMEM(0x1B,0x02), EV5, ARG_EV5HWMEM }, + { "hw_ldl/v", EV6HWMEM(0x1B,0x4), EV6, ARG_EV6HWMEM }, + { "hw_ldl/vl", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM }, + { "hw_ldl/w", EV5HWMEM(0x1B,0x08), EV5, ARG_EV5HWMEM }, + { "hw_ldl/w", EV6HWMEM(0x1B,0xA), EV6, ARG_EV6HWMEM }, + { "hw_ldl/wa", EV6HWMEM(0x1B,0xE), EV6, ARG_EV6HWMEM }, + { "hw_ldl/wl", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM }, + { "hw_ldl/wv", EV5HWMEM(0x1B,0x0a), EV5, ARG_EV5HWMEM }, + { "hw_ldl/wvl", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/a", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/av", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/aw", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/awv", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/p", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/p", EV6HWMEM(0x1B,0x2), EV6, ARG_EV6HWMEM }, + { "hw_ldl_l/pa", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/pav", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/paw", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/pawv", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/pv", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/pw", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/pwv", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/v", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/w", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM }, + { "hw_ldl_l/wv", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM }, + { "hw_ldq", EV4HWMEM(0x1B,0x1), EV4, ARG_EV4HWMEM }, + { "hw_ldq", EV5HWMEM(0x1B,0x04), EV5, ARG_EV5HWMEM }, + { "hw_ldq", EV6HWMEM(0x1B,0x9), EV6, ARG_EV6HWMEM }, + { "hw_ldq/a", EV4HWMEM(0x1B,0x5), EV4, ARG_EV4HWMEM }, + { "hw_ldq/a", EV5HWMEM(0x1B,0x14), EV5, ARG_EV5HWMEM }, + { "hw_ldq/a", EV6HWMEM(0x1B,0xD), EV6, ARG_EV6HWMEM }, + { "hw_ldq/al", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM }, + { "hw_ldq/ar", EV4HWMEM(0x1B,0x7), EV4, ARG_EV4HWMEM }, + { "hw_ldq/av", EV5HWMEM(0x1B,0x16), EV5, ARG_EV5HWMEM }, + { "hw_ldq/avl", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM }, + { "hw_ldq/aw", EV5HWMEM(0x1B,0x1c), EV5, ARG_EV5HWMEM }, + { "hw_ldq/awl", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM }, + { "hw_ldq/awv", EV5HWMEM(0x1B,0x1e), EV5, ARG_EV5HWMEM }, + { "hw_ldq/awvl", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM }, + { "hw_ldq/l", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM }, + { "hw_ldq/p", EV4HWMEM(0x1B,0x9), EV4, ARG_EV4HWMEM }, + { "hw_ldq/p", EV5HWMEM(0x1B,0x24), EV5, ARG_EV5HWMEM }, + { "hw_ldq/p", EV6HWMEM(0x1B,0x1), EV6, ARG_EV6HWMEM }, + { "hw_ldq/pa", EV4HWMEM(0x1B,0xD), EV4, ARG_EV4HWMEM }, + { "hw_ldq/pa", EV5HWMEM(0x1B,0x34), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pal", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM }, + { "hw_ldq/par", EV4HWMEM(0x1B,0xF), EV4, ARG_EV4HWMEM }, + { "hw_ldq/pav", EV5HWMEM(0x1B,0x36), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pavl", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM }, + { "hw_ldq/paw", EV5HWMEM(0x1B,0x3c), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pawl", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pawv", EV5HWMEM(0x1B,0x3e), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pawvl", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pl", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pr", EV4HWMEM(0x1B,0xB), EV4, ARG_EV4HWMEM }, + { "hw_ldq/pv", EV5HWMEM(0x1B,0x26), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pvl", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pw", EV5HWMEM(0x1B,0x2c), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pwl", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pwv", EV5HWMEM(0x1B,0x2e), EV5, ARG_EV5HWMEM }, + { "hw_ldq/pwvl", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM }, + { "hw_ldq/r", EV4HWMEM(0x1B,0x3), EV4, ARG_EV4HWMEM }, + { "hw_ldq/v", EV5HWMEM(0x1B,0x06), EV5, ARG_EV5HWMEM }, + { "hw_ldq/v", EV6HWMEM(0x1B,0x5), EV6, ARG_EV6HWMEM }, + { "hw_ldq/vl", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM }, + { "hw_ldq/w", EV5HWMEM(0x1B,0x0c), EV5, ARG_EV5HWMEM }, + { "hw_ldq/w", EV6HWMEM(0x1B,0xB), EV6, ARG_EV6HWMEM }, + { "hw_ldq/wa", EV6HWMEM(0x1B,0xF), EV6, ARG_EV6HWMEM }, + { "hw_ldq/wl", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM }, + { "hw_ldq/wv", EV5HWMEM(0x1B,0x0e), EV5, ARG_EV5HWMEM }, + { "hw_ldq/wvl", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/a", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/av", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/aw", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/awv", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/p", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/p", EV6HWMEM(0x1B,0x3), EV6, ARG_EV6HWMEM }, + { "hw_ldq_l/pa", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/pav", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/paw", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/pawv", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/pv", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/pw", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/pwv", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/v", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/w", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM }, + { "hw_ldq_l/wv", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM }, + { "hw_ld", EV4HWMEM(0x1B,0x0), EV4, ARG_EV4HWMEM }, + { "hw_ld", EV5HWMEM(0x1B,0x00), EV5, ARG_EV5HWMEM }, + { "hw_ld/a", EV4HWMEM(0x1B,0x4), EV4, ARG_EV4HWMEM }, + { "hw_ld/a", EV5HWMEM(0x1B,0x10), EV5, ARG_EV5HWMEM }, + { "hw_ld/al", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM }, + { "hw_ld/aq", EV4HWMEM(0x1B,0x5), EV4, ARG_EV4HWMEM }, + { "hw_ld/aq", EV5HWMEM(0x1B,0x14), EV5, ARG_EV5HWMEM }, + { "hw_ld/aql", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM }, + { "hw_ld/aqv", EV5HWMEM(0x1B,0x16), EV5, ARG_EV5HWMEM }, + { "hw_ld/aqvl", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM }, + { "hw_ld/ar", EV4HWMEM(0x1B,0x6), EV4, ARG_EV4HWMEM }, + { "hw_ld/arq", EV4HWMEM(0x1B,0x7), EV4, ARG_EV4HWMEM }, + { "hw_ld/av", EV5HWMEM(0x1B,0x12), EV5, ARG_EV5HWMEM }, + { "hw_ld/avl", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM }, + { "hw_ld/aw", EV5HWMEM(0x1B,0x18), EV5, ARG_EV5HWMEM }, + { "hw_ld/awl", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM }, + { "hw_ld/awq", EV5HWMEM(0x1B,0x1c), EV5, ARG_EV5HWMEM }, + { "hw_ld/awql", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM }, + { "hw_ld/awqv", EV5HWMEM(0x1B,0x1e), EV5, ARG_EV5HWMEM }, + { "hw_ld/awqvl", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM }, + { "hw_ld/awv", EV5HWMEM(0x1B,0x1a), EV5, ARG_EV5HWMEM }, + { "hw_ld/awvl", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM }, + { "hw_ld/l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM }, + { "hw_ld/p", EV4HWMEM(0x1B,0x8), EV4, ARG_EV4HWMEM }, + { "hw_ld/p", EV5HWMEM(0x1B,0x20), EV5, ARG_EV5HWMEM }, + { "hw_ld/pa", EV4HWMEM(0x1B,0xC), EV4, ARG_EV4HWMEM }, + { "hw_ld/pa", EV5HWMEM(0x1B,0x30), EV5, ARG_EV5HWMEM }, + { "hw_ld/pal", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM }, + { "hw_ld/paq", EV4HWMEM(0x1B,0xD), EV4, ARG_EV4HWMEM }, + { "hw_ld/paq", EV5HWMEM(0x1B,0x34), EV5, ARG_EV5HWMEM }, + { "hw_ld/paql", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM }, + { "hw_ld/paqv", EV5HWMEM(0x1B,0x36), EV5, ARG_EV5HWMEM }, + { "hw_ld/paqvl", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM }, + { "hw_ld/par", EV4HWMEM(0x1B,0xE), EV4, ARG_EV4HWMEM }, + { "hw_ld/parq", EV4HWMEM(0x1B,0xF), EV4, ARG_EV4HWMEM }, + { "hw_ld/pav", EV5HWMEM(0x1B,0x32), EV5, ARG_EV5HWMEM }, + { "hw_ld/pavl", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM }, + { "hw_ld/paw", EV5HWMEM(0x1B,0x38), EV5, ARG_EV5HWMEM }, + { "hw_ld/pawl", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM }, + { "hw_ld/pawq", EV5HWMEM(0x1B,0x3c), EV5, ARG_EV5HWMEM }, + { "hw_ld/pawql", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM }, + { "hw_ld/pawqv", EV5HWMEM(0x1B,0x3e), EV5, ARG_EV5HWMEM }, + { "hw_ld/pawqvl", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM }, + { "hw_ld/pawv", EV5HWMEM(0x1B,0x3a), EV5, ARG_EV5HWMEM }, + { "hw_ld/pawvl", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM }, + { "hw_ld/pl", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM }, + { "hw_ld/pq", EV4HWMEM(0x1B,0x9), EV4, ARG_EV4HWMEM }, + { "hw_ld/pq", EV5HWMEM(0x1B,0x24), EV5, ARG_EV5HWMEM }, + { "hw_ld/pql", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM }, + { "hw_ld/pqv", EV5HWMEM(0x1B,0x26), EV5, ARG_EV5HWMEM }, + { "hw_ld/pqvl", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM }, + { "hw_ld/pr", EV4HWMEM(0x1B,0xA), EV4, ARG_EV4HWMEM }, + { "hw_ld/prq", EV4HWMEM(0x1B,0xB), EV4, ARG_EV4HWMEM }, + { "hw_ld/pv", EV5HWMEM(0x1B,0x22), EV5, ARG_EV5HWMEM }, + { "hw_ld/pvl", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM }, + { "hw_ld/pw", EV5HWMEM(0x1B,0x28), EV5, ARG_EV5HWMEM }, + { "hw_ld/pwl", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM }, + { "hw_ld/pwq", EV5HWMEM(0x1B,0x2c), EV5, ARG_EV5HWMEM }, + { "hw_ld/pwql", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM }, + { "hw_ld/pwqv", EV5HWMEM(0x1B,0x2e), EV5, ARG_EV5HWMEM }, + { "hw_ld/pwqvl", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM }, + { "hw_ld/pwv", EV5HWMEM(0x1B,0x2a), EV5, ARG_EV5HWMEM }, + { "hw_ld/pwvl", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM }, + { "hw_ld/q", EV4HWMEM(0x1B,0x1), EV4, ARG_EV4HWMEM }, + { "hw_ld/q", EV5HWMEM(0x1B,0x04), EV5, ARG_EV5HWMEM }, + { "hw_ld/ql", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM }, + { "hw_ld/qv", EV5HWMEM(0x1B,0x06), EV5, ARG_EV5HWMEM }, + { "hw_ld/qvl", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM }, + { "hw_ld/r", EV4HWMEM(0x1B,0x2), EV4, ARG_EV4HWMEM }, + { "hw_ld/rq", EV4HWMEM(0x1B,0x3), EV4, ARG_EV4HWMEM }, + { "hw_ld/v", EV5HWMEM(0x1B,0x02), EV5, ARG_EV5HWMEM }, + { "hw_ld/vl", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM }, + { "hw_ld/w", EV5HWMEM(0x1B,0x08), EV5, ARG_EV5HWMEM }, + { "hw_ld/wl", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM }, + { "hw_ld/wq", EV5HWMEM(0x1B,0x0c), EV5, ARG_EV5HWMEM }, + { "hw_ld/wql", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM }, + { "hw_ld/wqv", EV5HWMEM(0x1B,0x0e), EV5, ARG_EV5HWMEM }, + { "hw_ld/wqvl", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM }, + { "hw_ld/wv", EV5HWMEM(0x1B,0x0a), EV5, ARG_EV5HWMEM }, + { "hw_ld/wvl", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM }, + { "pal1b", PCD(0x1B), BASE, ARG_PCD }, + + { "sextb", OPR(0x1C, 0x00), BWX, ARG_OPRZ1 }, + { "sextw", OPR(0x1C, 0x01), BWX, ARG_OPRZ1 }, + { "ctpop", OPR(0x1C, 0x30), CIX, ARG_OPRZ1 }, + { "perr", OPR(0x1C, 0x31), MAX, ARG_OPR }, + { "ctlz", OPR(0x1C, 0x32), CIX, ARG_OPRZ1 }, + { "cttz", OPR(0x1C, 0x33), CIX, ARG_OPRZ1 }, + { "unpkbw", OPR(0x1C, 0x34), MAX, ARG_OPRZ1 }, + { "unpkbl", OPR(0x1C, 0x35), MAX, ARG_OPRZ1 }, + { "pkwb", OPR(0x1C, 0x36), MAX, ARG_OPRZ1 }, + { "pklb", OPR(0x1C, 0x37), MAX, ARG_OPRZ1 }, + { "minsb8", OPR(0x1C, 0x38), MAX, ARG_OPR }, + { "minsb8", OPRL(0x1C, 0x38), MAX, ARG_OPRL }, + { "minsw4", OPR(0x1C, 0x39), MAX, ARG_OPR }, + { "minsw4", OPRL(0x1C, 0x39), MAX, ARG_OPRL }, + { "minub8", OPR(0x1C, 0x3A), MAX, ARG_OPR }, + { "minub8", OPRL(0x1C, 0x3A), MAX, ARG_OPRL }, + { "minuw4", OPR(0x1C, 0x3B), MAX, ARG_OPR }, + { "minuw4", OPRL(0x1C, 0x3B), MAX, ARG_OPRL }, + { "maxub8", OPR(0x1C, 0x3C), MAX, ARG_OPR }, + { "maxub8", OPRL(0x1C, 0x3C), MAX, ARG_OPRL }, + { "maxuw4", OPR(0x1C, 0x3D), MAX, ARG_OPR }, + { "maxuw4", OPRL(0x1C, 0x3D), MAX, ARG_OPRL }, + { "maxsb8", OPR(0x1C, 0x3E), MAX, ARG_OPR }, + { "maxsb8", OPRL(0x1C, 0x3E), MAX, ARG_OPRL }, + { "maxsw4", OPR(0x1C, 0x3F), MAX, ARG_OPR }, + { "maxsw4", OPRL(0x1C, 0x3F), MAX, ARG_OPRL }, + { "ftoit", FP(0x1C, 0x70), CIX, { FA, ZB, RC } }, + { "ftois", FP(0x1C, 0x78), CIX, { FA, ZB, RC } }, + + { "hw_mtpr", OPR(0x1D,0x00), EV4, { RA, RBA, EV4EXTHWINDEX } }, + { "hw_mtpr", OP(0x1D), OP_MASK, EV5, { RA, RBA, EV5HWINDEX } }, + { "hw_mtpr", OP(0x1D), OP_MASK, EV6, { ZA, RB, EV6HWINDEX } }, + { "hw_mtpr/i", OPR(0x1D,0x01), EV4, ARG_EV4HWMPR }, + { "hw_mtpr/a", OPR(0x1D,0x02), EV4, ARG_EV4HWMPR }, + { "hw_mtpr/ai", OPR(0x1D,0x03), EV4, ARG_EV4HWMPR }, + { "hw_mtpr/p", OPR(0x1D,0x04), EV4, ARG_EV4HWMPR }, + { "hw_mtpr/pi", OPR(0x1D,0x05), EV4, ARG_EV4HWMPR }, + { "hw_mtpr/pa", OPR(0x1D,0x06), EV4, ARG_EV4HWMPR }, + { "hw_mtpr/pai", OPR(0x1D,0x07), EV4, ARG_EV4HWMPR }, + { "pal1d", PCD(0x1D), BASE, ARG_PCD }, + + { "hw_rei", SPCD(0x1E,0x3FF8000), EV4|EV5, ARG_NONE }, + { "hw_rei_stall", SPCD(0x1E,0x3FFC000), EV5, ARG_NONE }, + { "hw_jmp", EV6HWMBR(0x1E,0x0), EV6, { ZA, PRB, EV6HWJMPHINT } }, + { "hw_jsr", EV6HWMBR(0x1E,0x2), EV6, { ZA, PRB, EV6HWJMPHINT } }, + { "hw_ret", EV6HWMBR(0x1E,0x4), EV6, { ZA, PRB } }, + { "hw_jcr", EV6HWMBR(0x1E,0x6), EV6, { ZA, PRB } }, + { "hw_coroutine", EV6HWMBR(0x1E,0x6), EV6, { ZA, PRB } }, /* alias */ + { "hw_jmp/stall", EV6HWMBR(0x1E,0x1), EV6, { ZA, PRB, EV6HWJMPHINT } }, + { "hw_jsr/stall", EV6HWMBR(0x1E,0x3), EV6, { ZA, PRB, EV6HWJMPHINT } }, + { "hw_ret/stall", EV6HWMBR(0x1E,0x5), EV6, { ZA, PRB } }, + { "hw_jcr/stall", EV6HWMBR(0x1E,0x7), EV6, { ZA, PRB } }, + { "hw_coroutine/stall", EV6HWMBR(0x1E,0x7), EV6, { ZA, PRB } }, /* alias */ + { "pal1e", PCD(0x1E), BASE, ARG_PCD }, + + { "hw_stl", EV4HWMEM(0x1F,0x0), EV4, ARG_EV4HWMEM }, + { "hw_stl", EV5HWMEM(0x1F,0x00), EV5, ARG_EV5HWMEM }, + { "hw_stl", EV6HWMEM(0x1F,0x4), EV6, ARG_EV6HWMEM }, /* ??? 8 */ + { "hw_stl/a", EV4HWMEM(0x1F,0x4), EV4, ARG_EV4HWMEM }, + { "hw_stl/a", EV5HWMEM(0x1F,0x10), EV5, ARG_EV5HWMEM }, + { "hw_stl/a", EV6HWMEM(0x1F,0xC), EV6, ARG_EV6HWMEM }, + { "hw_stl/ac", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM }, + { "hw_stl/ar", EV4HWMEM(0x1F,0x6), EV4, ARG_EV4HWMEM }, + { "hw_stl/av", EV5HWMEM(0x1F,0x12), EV5, ARG_EV5HWMEM }, + { "hw_stl/avc", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM }, + { "hw_stl/c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM }, + { "hw_stl/p", EV4HWMEM(0x1F,0x8), EV4, ARG_EV4HWMEM }, + { "hw_stl/p", EV5HWMEM(0x1F,0x20), EV5, ARG_EV5HWMEM }, + { "hw_stl/p", EV6HWMEM(0x1F,0x0), EV6, ARG_EV6HWMEM }, + { "hw_stl/pa", EV4HWMEM(0x1F,0xC), EV4, ARG_EV4HWMEM }, + { "hw_stl/pa", EV5HWMEM(0x1F,0x30), EV5, ARG_EV5HWMEM }, + { "hw_stl/pac", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM }, + { "hw_stl/pav", EV5HWMEM(0x1F,0x32), EV5, ARG_EV5HWMEM }, + { "hw_stl/pavc", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM }, + { "hw_stl/pc", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM }, + { "hw_stl/pr", EV4HWMEM(0x1F,0xA), EV4, ARG_EV4HWMEM }, + { "hw_stl/pv", EV5HWMEM(0x1F,0x22), EV5, ARG_EV5HWMEM }, + { "hw_stl/pvc", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM }, + { "hw_stl/r", EV4HWMEM(0x1F,0x2), EV4, ARG_EV4HWMEM }, + { "hw_stl/v", EV5HWMEM(0x1F,0x02), EV5, ARG_EV5HWMEM }, + { "hw_stl/vc", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM }, + { "hw_stl_c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM }, + { "hw_stl_c/a", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM }, + { "hw_stl_c/av", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM }, + { "hw_stl_c/p", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM }, + { "hw_stl_c/p", EV6HWMEM(0x1F,0x2), EV6, ARG_EV6HWMEM }, + { "hw_stl_c/pa", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM }, + { "hw_stl_c/pav", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM }, + { "hw_stl_c/pv", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM }, + { "hw_stl_c/v", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM }, + { "hw_stq", EV4HWMEM(0x1F,0x1), EV4, ARG_EV4HWMEM }, + { "hw_stq", EV5HWMEM(0x1F,0x04), EV5, ARG_EV5HWMEM }, + { "hw_stq", EV6HWMEM(0x1F,0x5), EV6, ARG_EV6HWMEM }, /* ??? 9 */ + { "hw_stq/a", EV4HWMEM(0x1F,0x5), EV4, ARG_EV4HWMEM }, + { "hw_stq/a", EV5HWMEM(0x1F,0x14), EV5, ARG_EV5HWMEM }, + { "hw_stq/a", EV6HWMEM(0x1F,0xD), EV6, ARG_EV6HWMEM }, + { "hw_stq/ac", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM }, + { "hw_stq/ar", EV4HWMEM(0x1F,0x7), EV4, ARG_EV4HWMEM }, + { "hw_stq/av", EV5HWMEM(0x1F,0x16), EV5, ARG_EV5HWMEM }, + { "hw_stq/avc", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM }, + { "hw_stq/c", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM }, + { "hw_stq/p", EV4HWMEM(0x1F,0x9), EV4, ARG_EV4HWMEM }, + { "hw_stq/p", EV5HWMEM(0x1F,0x24), EV5, ARG_EV5HWMEM }, + { "hw_stq/p", EV6HWMEM(0x1F,0x1), EV6, ARG_EV6HWMEM }, + { "hw_stq/pa", EV4HWMEM(0x1F,0xD), EV4, ARG_EV4HWMEM }, + { "hw_stq/pa", EV5HWMEM(0x1F,0x34), EV5, ARG_EV5HWMEM }, + { "hw_stq/pac", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM }, + { "hw_stq/par", EV4HWMEM(0x1F,0xE), EV4, ARG_EV4HWMEM }, + { "hw_stq/par", EV4HWMEM(0x1F,0xF), EV4, ARG_EV4HWMEM }, + { "hw_stq/pav", EV5HWMEM(0x1F,0x36), EV5, ARG_EV5HWMEM }, + { "hw_stq/pavc", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM }, + { "hw_stq/pc", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM }, + { "hw_stq/pr", EV4HWMEM(0x1F,0xB), EV4, ARG_EV4HWMEM }, + { "hw_stq/pv", EV5HWMEM(0x1F,0x26), EV5, ARG_EV5HWMEM }, + { "hw_stq/pvc", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM }, + { "hw_stq/r", EV4HWMEM(0x1F,0x3), EV4, ARG_EV4HWMEM }, + { "hw_stq/v", EV5HWMEM(0x1F,0x06), EV5, ARG_EV5HWMEM }, + { "hw_stq/vc", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM }, + { "hw_stq_c", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM }, + { "hw_stq_c/a", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM }, + { "hw_stq_c/av", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM }, + { "hw_stq_c/p", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM }, + { "hw_stq_c/p", EV6HWMEM(0x1F,0x3), EV6, ARG_EV6HWMEM }, + { "hw_stq_c/pa", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM }, + { "hw_stq_c/pav", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM }, + { "hw_stq_c/pv", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM }, + { "hw_stq_c/v", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM }, + { "hw_st", EV4HWMEM(0x1F,0x0), EV4, ARG_EV4HWMEM }, + { "hw_st", EV5HWMEM(0x1F,0x00), EV5, ARG_EV5HWMEM }, + { "hw_st/a", EV4HWMEM(0x1F,0x4), EV4, ARG_EV4HWMEM }, + { "hw_st/a", EV5HWMEM(0x1F,0x10), EV5, ARG_EV5HWMEM }, + { "hw_st/ac", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM }, + { "hw_st/aq", EV4HWMEM(0x1F,0x5), EV4, ARG_EV4HWMEM }, + { "hw_st/aq", EV5HWMEM(0x1F,0x14), EV5, ARG_EV5HWMEM }, + { "hw_st/aqc", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM }, + { "hw_st/aqv", EV5HWMEM(0x1F,0x16), EV5, ARG_EV5HWMEM }, + { "hw_st/aqvc", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM }, + { "hw_st/ar", EV4HWMEM(0x1F,0x6), EV4, ARG_EV4HWMEM }, + { "hw_st/arq", EV4HWMEM(0x1F,0x7), EV4, ARG_EV4HWMEM }, + { "hw_st/av", EV5HWMEM(0x1F,0x12), EV5, ARG_EV5HWMEM }, + { "hw_st/avc", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM }, + { "hw_st/c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM }, + { "hw_st/p", EV4HWMEM(0x1F,0x8), EV4, ARG_EV4HWMEM }, + { "hw_st/p", EV5HWMEM(0x1F,0x20), EV5, ARG_EV5HWMEM }, + { "hw_st/pa", EV4HWMEM(0x1F,0xC), EV4, ARG_EV4HWMEM }, + { "hw_st/pa", EV5HWMEM(0x1F,0x30), EV5, ARG_EV5HWMEM }, + { "hw_st/pac", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM }, + { "hw_st/paq", EV4HWMEM(0x1F,0xD), EV4, ARG_EV4HWMEM }, + { "hw_st/paq", EV5HWMEM(0x1F,0x34), EV5, ARG_EV5HWMEM }, + { "hw_st/paqc", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM }, + { "hw_st/paqv", EV5HWMEM(0x1F,0x36), EV5, ARG_EV5HWMEM }, + { "hw_st/paqvc", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM }, + { "hw_st/par", EV4HWMEM(0x1F,0xE), EV4, ARG_EV4HWMEM }, + { "hw_st/parq", EV4HWMEM(0x1F,0xF), EV4, ARG_EV4HWMEM }, + { "hw_st/pav", EV5HWMEM(0x1F,0x32), EV5, ARG_EV5HWMEM }, + { "hw_st/pavc", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM }, + { "hw_st/pc", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM }, + { "hw_st/pq", EV4HWMEM(0x1F,0x9), EV4, ARG_EV4HWMEM }, + { "hw_st/pq", EV5HWMEM(0x1F,0x24), EV5, ARG_EV5HWMEM }, + { "hw_st/pqc", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM }, + { "hw_st/pqv", EV5HWMEM(0x1F,0x26), EV5, ARG_EV5HWMEM }, + { "hw_st/pqvc", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM }, + { "hw_st/pr", EV4HWMEM(0x1F,0xA), EV4, ARG_EV4HWMEM }, + { "hw_st/prq", EV4HWMEM(0x1F,0xB), EV4, ARG_EV4HWMEM }, + { "hw_st/pv", EV5HWMEM(0x1F,0x22), EV5, ARG_EV5HWMEM }, + { "hw_st/pvc", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM }, + { "hw_st/q", EV4HWMEM(0x1F,0x1), EV4, ARG_EV4HWMEM }, + { "hw_st/q", EV5HWMEM(0x1F,0x04), EV5, ARG_EV5HWMEM }, + { "hw_st/qc", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM }, + { "hw_st/qv", EV5HWMEM(0x1F,0x06), EV5, ARG_EV5HWMEM }, + { "hw_st/qvc", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM }, + { "hw_st/r", EV4HWMEM(0x1F,0x2), EV4, ARG_EV4HWMEM }, + { "hw_st/v", EV5HWMEM(0x1F,0x02), EV5, ARG_EV5HWMEM }, + { "hw_st/vc", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM }, + { "pal1f", PCD(0x1F), BASE, ARG_PCD }, + + { "ldf", MEM(0x20), BASE, ARG_FMEM }, + { "ldg", MEM(0x21), BASE, ARG_FMEM }, + { "lds", MEM(0x22), BASE, ARG_FMEM }, + { "ldt", MEM(0x23), BASE, ARG_FMEM }, + { "stf", MEM(0x24), BASE, ARG_FMEM }, + { "stg", MEM(0x25), BASE, ARG_FMEM }, + { "sts", MEM(0x26), BASE, ARG_FMEM }, + { "stt", MEM(0x27), BASE, ARG_FMEM }, + + { "ldl", MEM(0x28), BASE, ARG_MEM }, + { "ldq", MEM(0x29), BASE, ARG_MEM }, + { "ldl_l", MEM(0x2A), BASE, ARG_MEM }, + { "ldq_l", MEM(0x2B), BASE, ARG_MEM }, + { "stl", MEM(0x2C), BASE, ARG_MEM }, + { "stq", MEM(0x2D), BASE, ARG_MEM }, + { "stl_c", MEM(0x2E), BASE, ARG_MEM }, + { "stq_c", MEM(0x2F), BASE, ARG_MEM }, + + { "br", BRA(0x30), BASE, { ZA, BDISP } }, /* pseudo */ + { "br", BRA(0x30), BASE, ARG_BRA }, + { "fbeq", BRA(0x31), BASE, ARG_FBRA }, + { "fblt", BRA(0x32), BASE, ARG_FBRA }, + { "fble", BRA(0x33), BASE, ARG_FBRA }, + { "bsr", BRA(0x34), BASE, ARG_BRA }, + { "fbne", BRA(0x35), BASE, ARG_FBRA }, + { "fbge", BRA(0x36), BASE, ARG_FBRA }, + { "fbgt", BRA(0x37), BASE, ARG_FBRA }, + { "blbc", BRA(0x38), BASE, ARG_BRA }, + { "beq", BRA(0x39), BASE, ARG_BRA }, + { "blt", BRA(0x3A), BASE, ARG_BRA }, + { "ble", BRA(0x3B), BASE, ARG_BRA }, + { "blbs", BRA(0x3C), BASE, ARG_BRA }, + { "bne", BRA(0x3D), BASE, ARG_BRA }, + { "bge", BRA(0x3E), BASE, ARG_BRA }, + { "bgt", BRA(0x3F), BASE, ARG_BRA }, +}; + +const unsigned alpha_num_opcodes = sizeof(alpha_opcodes)/sizeof(*alpha_opcodes); + +/* OSF register names. */ + +static const char * const osf_regnames[64] = { + "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", + "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp", + "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", + "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero", + "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", + "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", + "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", + "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31" +}; + +/* VMS register names. */ + +static const char * const vms_regnames[64] = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", + "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23", + "R24", "AI", "RA", "PV", "AT", "FP", "SP", "RZ", + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", + "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", + "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", + "F24", "F25", "F26", "F27", "F28", "F29", "F30", "FZ" +}; + +/* Disassemble Alpha instructions. */ + +int +print_insn_alpha (bfd_vma memaddr, struct disassemble_info *info) +{ + static const struct alpha_opcode *opcode_index[AXP_NOPS+1]; + const char * const * regnames; + const struct alpha_opcode *opcode, *opcode_end; + const unsigned char *opindex; + unsigned insn, op, isa_mask; + int need_comma; + + /* Initialize the majorop table the first time through */ + if (!opcode_index[0]) + { + opcode = alpha_opcodes; + opcode_end = opcode + alpha_num_opcodes; + + for (op = 0; op < AXP_NOPS; ++op) + { + opcode_index[op] = opcode; + while (opcode < opcode_end && op == AXP_OP (opcode->opcode)) + ++opcode; + } + opcode_index[op] = opcode; + } + + if (info->flavour == bfd_target_evax_flavour) + regnames = vms_regnames; + else + regnames = osf_regnames; + + isa_mask = AXP_OPCODE_NOPAL; + switch (info->mach) + { + case bfd_mach_alpha_ev4: + isa_mask |= AXP_OPCODE_EV4; + break; + case bfd_mach_alpha_ev5: + isa_mask |= AXP_OPCODE_EV5; + break; + case bfd_mach_alpha_ev6: + isa_mask |= AXP_OPCODE_EV6; + break; + } + + /* Read the insn into a host word */ + { + bfd_byte buffer[4]; + int status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + insn = bfd_getl32 (buffer); + } + + /* Get the major opcode of the instruction. */ + op = AXP_OP (insn); + + /* Find the first match in the opcode table. */ + opcode_end = opcode_index[op + 1]; + for (opcode = opcode_index[op]; opcode < opcode_end; ++opcode) + { + if ((insn ^ opcode->opcode) & opcode->mask) + continue; + + if (!(opcode->flags & isa_mask)) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + { + int invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + const struct alpha_operand *operand = alpha_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + } + + /* The instruction is valid. */ + goto found; + } + + /* No instruction found */ + (*info->fprintf_func) (info->stream, ".long %#08x", insn); + + return 4; + +found: + (*info->fprintf_func) (info->stream, "%s", opcode->name); + if (opcode->operands[0] != 0) + (*info->fprintf_func) (info->stream, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + const struct alpha_operand *operand = alpha_operands + *opindex; + int value; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & AXP_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) NULL); + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if (operand->flags & AXP_OPERAND_SIGNED) + { + int signbit = 1 << (operand->bits - 1); + value = (value ^ signbit) - signbit; + } + } + + if (need_comma && + ((operand->flags & (AXP_OPERAND_PARENS | AXP_OPERAND_COMMA)) + != AXP_OPERAND_PARENS)) + { + (*info->fprintf_func) (info->stream, ","); + } + if (operand->flags & AXP_OPERAND_PARENS) + (*info->fprintf_func) (info->stream, "("); + + /* Print the operand as directed by the flags. */ + if (operand->flags & AXP_OPERAND_IR) + (*info->fprintf_func) (info->stream, "%s", regnames[value]); + else if (operand->flags & AXP_OPERAND_FPR) + (*info->fprintf_func) (info->stream, "%s", regnames[value + 32]); + else if (operand->flags & AXP_OPERAND_RELATIVE) + (*info->print_address_func) (memaddr + 4 + value, info); + else if (operand->flags & AXP_OPERAND_SIGNED) + (*info->fprintf_func) (info->stream, "%d", value); + else + (*info->fprintf_func) (info->stream, "%#x", value); + + if (operand->flags & AXP_OPERAND_PARENS) + (*info->fprintf_func) (info->stream, ")"); + need_comma = 1; + } + + return 4; +} diff --git a/disas/arm-a64.cc b/disas/arm-a64.cc new file mode 100644 index 0000000..e04f946 --- /dev/null +++ b/disas/arm-a64.cc @@ -0,0 +1,90 @@ +/* + * ARM A64 disassembly output wrapper to libvixl + * Copyright (c) 2013 Linaro Limited + * Written by Claudio Fontana + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "a64/disasm-a64.h" + +extern "C" { +#include "disas/bfd.h" +} + +using namespace vixl; + +static Decoder *vixl_decoder = NULL; +static Disassembler *vixl_disasm = NULL; + +/* We don't use libvixl's PrintDisassembler because its output + * is a little unhelpful (trailing newlines, for example). + * Instead we use our own very similar variant so we have + * control over the format. + */ +class QEMUDisassembler : public Disassembler { +public: + explicit QEMUDisassembler(FILE *stream) : stream_(stream) { } + ~QEMUDisassembler() { } + +protected: + virtual void ProcessOutput(const Instruction *instr) { + fprintf(stream_, "%08" PRIx32 " %s", + instr->InstructionBits(), GetOutput()); + } + +private: + FILE *stream_; +}; + +static int vixl_is_initialized(void) +{ + return vixl_decoder != NULL; +} + +static void vixl_init(FILE *f) { + vixl_decoder = new Decoder(); + vixl_disasm = new QEMUDisassembler(f); + vixl_decoder->AppendVisitor(vixl_disasm); +} + +#define INSN_SIZE 4 + +/* Disassemble ARM A64 instruction. This is our only entry + * point from QEMU's C code. + */ +int print_insn_arm_a64(uint64_t addr, disassemble_info *info) +{ + uint8_t bytes[INSN_SIZE]; + uint32_t instrval; + const Instruction *instr; + int status; + + status = info->read_memory_func(addr, bytes, INSN_SIZE, info); + if (status != 0) { + info->memory_error_func(status, addr, info); + return -1; + } + + if (!vixl_is_initialized()) { + vixl_init(info->stream); + } + + instrval = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; + instr = reinterpret_cast(&instrval); + vixl_disasm->MapCodeAddress(addr, instr); + vixl_decoder->Decode(instr); + + return INSN_SIZE; +} diff --git a/disas/arm.c b/disas/arm.c new file mode 100644 index 0000000..6165246 --- /dev/null +++ b/disas/arm.c @@ -0,0 +1,4012 @@ +/* Instruction printing code for the ARM + Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + 2007, Free Software Foundation, Inc. + Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) + Modification by James G. Smith (jsmith@cygnus.co.uk) + + This file is part of libopcodes. + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +/* Start of qemu specific additions. Mostly this is stub definitions + for things we don't care about. */ + +#include "disas/bfd.h" +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#define ISSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\n') + +#define ARM_EXT_V1 0 +#define ARM_EXT_V2 0 +#define ARM_EXT_V2S 0 +#define ARM_EXT_V3 0 +#define ARM_EXT_V3M 0 +#define ARM_EXT_V4 0 +#define ARM_EXT_V4T 0 +#define ARM_EXT_V5 0 +#define ARM_EXT_V5T 0 +#define ARM_EXT_V5ExP 0 +#define ARM_EXT_V5E 0 +#define ARM_EXT_V5J 0 +#define ARM_EXT_V6 0 +#define ARM_EXT_V6K 0 +#define ARM_EXT_V6Z 0 +#define ARM_EXT_V6T2 0 +#define ARM_EXT_V7 0 +#define ARM_EXT_DIV 0 + +/* Co-processor space extensions. */ +#define ARM_CEXT_XSCALE 0 +#define ARM_CEXT_MAVERICK 0 +#define ARM_CEXT_IWMMXT 0 + +#define FPU_FPA_EXT_V1 0 +#define FPU_FPA_EXT_V2 0 +#define FPU_VFP_EXT_NONE 0 +#define FPU_VFP_EXT_V1xD 0 +#define FPU_VFP_EXT_V1 0 +#define FPU_VFP_EXT_V2 0 +#define FPU_MAVERICK 0 +#define FPU_VFP_EXT_V3 0 +#define FPU_NEON_EXT_V1 0 + +/* Assume host uses ieee float. */ +static void floatformat_to_double (unsigned char *data, double *dest) +{ + union { + uint32_t i; + float f; + } u; + u.i = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + *dest = u.f; +} + +/* End of qemu specific additions. */ + +/* FIXME: Belongs in global header. */ +#ifndef strneq +#define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0) +#endif + +#ifndef NUM_ELEM +#define NUM_ELEM(a) (sizeof (a) / sizeof (a)[0]) +#endif + +struct opcode32 +{ + unsigned long arch; /* Architecture defining this insn. */ + unsigned long value, mask; /* Recognise insn if (op&mask)==value. */ + const char *assembler; /* How to disassemble this insn. */ +}; + +struct opcode16 +{ + unsigned long arch; /* Architecture defining this insn. */ + unsigned short value, mask; /* Recognise insn if (op&mask)==value. */ + const char *assembler; /* How to disassemble this insn. */ +}; + +/* print_insn_coprocessor recognizes the following format control codes: + + %% % + + %c print condition code (always bits 28-31 in ARM mode) + %q print shifter argument + %u print condition code (unconditional in ARM mode) + %A print address for ldc/stc/ldf/stf instruction + %B print vstm/vldm register list + %C print vstr/vldr address operand + %I print cirrus signed shift immediate: bits 0..3|4..6 + %F print the COUNT field of a LFM/SFM instruction. + %P print floating point precision in arithmetic insn + %Q print floating point precision in ldf/stf insn + %R print floating point rounding mode + + %r print as an ARM register + %d print the bitfield in decimal + %k print immediate for VFPv3 conversion instruction + %x print the bitfield in hex + %X print the bitfield as 1 hex digit without leading "0x" + %f print a floating point constant if >7 else a + floating point register + %w print as an iWMMXt width field - [bhwd]ss/us + %g print as an iWMMXt 64-bit register + %G print as an iWMMXt general purpose or control register + %D print as a NEON D register + %Q print as a NEON Q register + + %y print a single precision VFP reg. + Codes: 0=>Sm, 1=>Sd, 2=>Sn, 3=>multi-list, 4=>Sm pair + %z print a double precision VFP reg + Codes: 0=>Dm, 1=>Dd, 2=>Dn, 3=>multi-list + + %'c print specified char iff bitfield is all ones + %`c print specified char iff bitfield is all zeroes + %?ab... select from array of values in big endian order + + %L print as an iWMMXt N/M width field. + %Z print the Immediate of a WSHUFH instruction. + %l like 'A' except use byte offsets for 'B' & 'H' + versions. + %i print 5-bit immediate in bits 8,3..0 + (print "32" when 0) + %r print register offset address for wldt/wstr instruction +*/ + +/* Common coprocessor opcodes shared between Arm and Thumb-2. */ + +static const struct opcode32 coprocessor_opcodes[] = +{ + /* XScale instructions. */ + {ARM_CEXT_XSCALE, 0x0e200010, 0x0fff0ff0, "mia%c\tacc0, %0-3r, %12-15r"}, + {ARM_CEXT_XSCALE, 0x0e280010, 0x0fff0ff0, "miaph%c\tacc0, %0-3r, %12-15r"}, + {ARM_CEXT_XSCALE, 0x0e2c0010, 0x0ffc0ff0, "mia%17'T%17`B%16'T%16`B%c\tacc0, %0-3r, %12-15r"}, + {ARM_CEXT_XSCALE, 0x0c400000, 0x0ff00fff, "mar%c\tacc0, %12-15r, %16-19r"}, + {ARM_CEXT_XSCALE, 0x0c500000, 0x0ff00fff, "mra%c\t%12-15r, %16-19r, acc0"}, + + /* Intel Wireless MMX technology instructions. */ +#define FIRST_IWMMXT_INSN 0x0e130130 +#define IWMMXT_INSN_COUNT 73 + {ARM_CEXT_IWMMXT, 0x0e130130, 0x0f3f0fff, "tandc%22-23w%c\t%12-15r"}, + {ARM_CEXT_XSCALE, 0x0e400010, 0x0ff00f3f, "tbcst%6-7w%c\t%16-19g, %12-15r"}, + {ARM_CEXT_XSCALE, 0x0e130170, 0x0f3f0ff8, "textrc%22-23w%c\t%12-15r, #%0-2d"}, + {ARM_CEXT_XSCALE, 0x0e100070, 0x0f300ff0, "textrm%3?su%22-23w%c\t%12-15r, %16-19g, #%0-2d"}, + {ARM_CEXT_XSCALE, 0x0e600010, 0x0ff00f38, "tinsr%6-7w%c\t%16-19g, %12-15r, #%0-2d"}, + {ARM_CEXT_XSCALE, 0x0e000110, 0x0ff00fff, "tmcr%c\t%16-19G, %12-15r"}, + {ARM_CEXT_XSCALE, 0x0c400000, 0x0ff00ff0, "tmcrr%c\t%0-3g, %12-15r, %16-19r"}, + {ARM_CEXT_XSCALE, 0x0e2c0010, 0x0ffc0e10, "tmia%17?tb%16?tb%c\t%5-8g, %0-3r, %12-15r"}, + {ARM_CEXT_XSCALE, 0x0e200010, 0x0fff0e10, "tmia%c\t%5-8g, %0-3r, %12-15r"}, + {ARM_CEXT_XSCALE, 0x0e280010, 0x0fff0e10, "tmiaph%c\t%5-8g, %0-3r, %12-15r"}, + {ARM_CEXT_XSCALE, 0x0e100030, 0x0f300fff, "tmovmsk%22-23w%c\t%12-15r, %16-19g"}, + {ARM_CEXT_XSCALE, 0x0e100110, 0x0ff00ff0, "tmrc%c\t%12-15r, %16-19G"}, + {ARM_CEXT_XSCALE, 0x0c500000, 0x0ff00ff0, "tmrrc%c\t%12-15r, %16-19r, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e130150, 0x0f3f0fff, "torc%22-23w%c\t%12-15r"}, + {ARM_CEXT_XSCALE, 0x0e130190, 0x0f3f0fff, "torvsc%22-23w%c\t%12-15r"}, + {ARM_CEXT_XSCALE, 0x0e2001c0, 0x0f300fff, "wabs%22-23w%c\t%12-15g, %16-19g"}, + {ARM_CEXT_XSCALE, 0x0e0001c0, 0x0f300fff, "wacc%22-23w%c\t%12-15g, %16-19g"}, + {ARM_CEXT_XSCALE, 0x0e000180, 0x0f000ff0, "wadd%20-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e2001a0, 0x0f300ff0, "waddbhus%22?ml%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0ea001a0, 0x0ff00ff0, "waddsubhx%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e000020, 0x0f800ff0, "waligni%c\t%12-15g, %16-19g, %0-3g, #%20-22d"}, + {ARM_CEXT_XSCALE, 0x0e800020, 0x0fc00ff0, "walignr%20-21d%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e200000, 0x0fe00ff0, "wand%20'n%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e800000, 0x0fa00ff0, "wavg2%22?hb%20'r%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e400000, 0x0fe00ff0, "wavg4%20'r%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e000060, 0x0f300ff0, "wcmpeq%22-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e100060, 0x0f100ff0, "wcmpgt%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0xfc500100, 0xfe500f00, "wldrd\t%12-15g, %r"}, + {ARM_CEXT_XSCALE, 0xfc100100, 0xfe500f00, "wldrw\t%12-15G, %A"}, + {ARM_CEXT_XSCALE, 0x0c100000, 0x0e100e00, "wldr%L%c\t%12-15g, %l"}, + {ARM_CEXT_XSCALE, 0x0e400100, 0x0fc00ff0, "wmac%21?su%20'z%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e800100, 0x0fc00ff0, "wmadd%21?su%20'x%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0ec00100, 0x0fd00ff0, "wmadd%21?sun%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e000160, 0x0f100ff0, "wmax%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e000080, 0x0f100fe0, "wmerge%c\t%12-15g, %16-19g, %0-3g, #%21-23d"}, + {ARM_CEXT_XSCALE, 0x0e0000a0, 0x0f800ff0, "wmia%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e800120, 0x0f800ff0, "wmiaw%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e100160, 0x0f100ff0, "wmin%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e000100, 0x0fc00ff0, "wmul%21?su%20?ml%23'r%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0ed00100, 0x0fd00ff0, "wmul%21?sumr%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0ee000c0, 0x0fe00ff0, "wmulwsm%20`r%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0ec000c0, 0x0fe00ff0, "wmulwum%20`r%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0eb000c0, 0x0ff00ff0, "wmulwl%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e8000a0, 0x0f800ff0, "wqmia%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e100080, 0x0fd00ff0, "wqmulm%21'r%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0ec000e0, 0x0fd00ff0, "wqmulwm%21'r%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e000000, 0x0ff00ff0, "wor%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e000080, 0x0f000ff0, "wpack%20-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0xfe300040, 0xff300ef0, "wror%22-23w\t%12-15g, %16-19g, #%i"}, + {ARM_CEXT_XSCALE, 0x0e300040, 0x0f300ff0, "wror%22-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e300140, 0x0f300ff0, "wror%22-23wg%c\t%12-15g, %16-19g, %0-3G"}, + {ARM_CEXT_XSCALE, 0x0e000120, 0x0fa00ff0, "wsad%22?hb%20'z%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e0001e0, 0x0f000ff0, "wshufh%c\t%12-15g, %16-19g, #%Z"}, + {ARM_CEXT_XSCALE, 0xfe100040, 0xff300ef0, "wsll%22-23w\t%12-15g, %16-19g, #%i"}, + {ARM_CEXT_XSCALE, 0x0e100040, 0x0f300ff0, "wsll%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e100148, 0x0f300ffc, "wsll%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"}, + {ARM_CEXT_XSCALE, 0xfe000040, 0xff300ef0, "wsra%22-23w\t%12-15g, %16-19g, #%i"}, + {ARM_CEXT_XSCALE, 0x0e000040, 0x0f300ff0, "wsra%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e000148, 0x0f300ffc, "wsra%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"}, + {ARM_CEXT_XSCALE, 0xfe200040, 0xff300ef0, "wsrl%22-23w\t%12-15g, %16-19g, #%i"}, + {ARM_CEXT_XSCALE, 0x0e200040, 0x0f300ff0, "wsrl%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e200148, 0x0f300ffc, "wsrl%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"}, + {ARM_CEXT_XSCALE, 0xfc400100, 0xfe500f00, "wstrd\t%12-15g, %r"}, + {ARM_CEXT_XSCALE, 0xfc000100, 0xfe500f00, "wstrw\t%12-15G, %A"}, + {ARM_CEXT_XSCALE, 0x0c000000, 0x0e100e00, "wstr%L%c\t%12-15g, %l"}, + {ARM_CEXT_XSCALE, 0x0e0001a0, 0x0f000ff0, "wsub%20-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0ed001c0, 0x0ff00ff0, "wsubaddhx%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e1001c0, 0x0f300ff0, "wabsdiff%22-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e0000c0, 0x0fd00fff, "wunpckeh%21?sub%c\t%12-15g, %16-19g"}, + {ARM_CEXT_XSCALE, 0x0e4000c0, 0x0fd00fff, "wunpckeh%21?suh%c\t%12-15g, %16-19g"}, + {ARM_CEXT_XSCALE, 0x0e8000c0, 0x0fd00fff, "wunpckeh%21?suw%c\t%12-15g, %16-19g"}, + {ARM_CEXT_XSCALE, 0x0e0000e0, 0x0f100fff, "wunpckel%21?su%22-23w%c\t%12-15g, %16-19g"}, + {ARM_CEXT_XSCALE, 0x0e1000c0, 0x0f300ff0, "wunpckih%22-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e1000e0, 0x0f300ff0, "wunpckil%22-23w%c\t%12-15g, %16-19g, %0-3g"}, + {ARM_CEXT_XSCALE, 0x0e100000, 0x0ff00ff0, "wxor%c\t%12-15g, %16-19g, %0-3g"}, + + /* Floating point coprocessor (FPA) instructions */ + {FPU_FPA_EXT_V1, 0x0e000100, 0x0ff08f10, "adf%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e100100, 0x0ff08f10, "muf%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e200100, 0x0ff08f10, "suf%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e300100, 0x0ff08f10, "rsf%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e400100, 0x0ff08f10, "dvf%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e500100, 0x0ff08f10, "rdf%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e600100, 0x0ff08f10, "pow%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e700100, 0x0ff08f10, "rpw%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e800100, 0x0ff08f10, "rmf%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e900100, 0x0ff08f10, "fml%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ea00100, 0x0ff08f10, "fdv%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0eb00100, 0x0ff08f10, "frd%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ec00100, 0x0ff08f10, "pol%c%P%R\t%12-14f, %16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e008100, 0x0ff08f10, "mvf%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e108100, 0x0ff08f10, "mnf%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e208100, 0x0ff08f10, "abs%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e308100, 0x0ff08f10, "rnd%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e408100, 0x0ff08f10, "sqt%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e508100, 0x0ff08f10, "log%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e608100, 0x0ff08f10, "lgn%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e708100, 0x0ff08f10, "exp%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e808100, 0x0ff08f10, "sin%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e908100, 0x0ff08f10, "cos%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ea08100, 0x0ff08f10, "tan%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0eb08100, 0x0ff08f10, "asn%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ec08100, 0x0ff08f10, "acs%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ed08100, 0x0ff08f10, "atn%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ee08100, 0x0ff08f10, "urd%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ef08100, 0x0ff08f10, "nrm%c%P%R\t%12-14f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0e000110, 0x0ff00f1f, "flt%c%P%R\t%16-18f, %12-15r"}, + {FPU_FPA_EXT_V1, 0x0e100110, 0x0fff0f98, "fix%c%R\t%12-15r, %0-2f"}, + {FPU_FPA_EXT_V1, 0x0e200110, 0x0fff0fff, "wfs%c\t%12-15r"}, + {FPU_FPA_EXT_V1, 0x0e300110, 0x0fff0fff, "rfs%c\t%12-15r"}, + {FPU_FPA_EXT_V1, 0x0e400110, 0x0fff0fff, "wfc%c\t%12-15r"}, + {FPU_FPA_EXT_V1, 0x0e500110, 0x0fff0fff, "rfc%c\t%12-15r"}, + {FPU_FPA_EXT_V1, 0x0e90f110, 0x0ff8fff0, "cmf%c\t%16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0eb0f110, 0x0ff8fff0, "cnf%c\t%16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ed0f110, 0x0ff8fff0, "cmfe%c\t%16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0ef0f110, 0x0ff8fff0, "cnfe%c\t%16-18f, %0-3f"}, + {FPU_FPA_EXT_V1, 0x0c000100, 0x0e100f00, "stf%c%Q\t%12-14f, %A"}, + {FPU_FPA_EXT_V1, 0x0c100100, 0x0e100f00, "ldf%c%Q\t%12-14f, %A"}, + {FPU_FPA_EXT_V2, 0x0c000200, 0x0e100f00, "sfm%c\t%12-14f, %F, %A"}, + {FPU_FPA_EXT_V2, 0x0c100200, 0x0e100f00, "lfm%c\t%12-14f, %F, %A"}, + + /* Register load/store */ + {FPU_NEON_EXT_V1, 0x0d200b00, 0x0fb00f01, "vstmdb%c\t%16-19r%21'!, %B"}, + {FPU_NEON_EXT_V1, 0x0d300b00, 0x0fb00f01, "vldmdb%c\t%16-19r%21'!, %B"}, + {FPU_NEON_EXT_V1, 0x0c800b00, 0x0f900f01, "vstmia%c\t%16-19r%21'!, %B"}, + {FPU_NEON_EXT_V1, 0x0c900b00, 0x0f900f01, "vldmia%c\t%16-19r%21'!, %B"}, + {FPU_NEON_EXT_V1, 0x0d000b00, 0x0f300f00, "vstr%c\t%12-15,22D, %C"}, + {FPU_NEON_EXT_V1, 0x0d100b00, 0x0f300f00, "vldr%c\t%12-15,22D, %C"}, + + /* Data transfer between ARM and NEON registers */ + {FPU_NEON_EXT_V1, 0x0e800b10, 0x0ff00f70, "vdup%c.32\t%16-19,7D, %12-15r"}, + {FPU_NEON_EXT_V1, 0x0e800b30, 0x0ff00f70, "vdup%c.16\t%16-19,7D, %12-15r"}, + {FPU_NEON_EXT_V1, 0x0ea00b10, 0x0ff00f70, "vdup%c.32\t%16-19,7Q, %12-15r"}, + {FPU_NEON_EXT_V1, 0x0ea00b30, 0x0ff00f70, "vdup%c.16\t%16-19,7Q, %12-15r"}, + {FPU_NEON_EXT_V1, 0x0ec00b10, 0x0ff00f70, "vdup%c.8\t%16-19,7D, %12-15r"}, + {FPU_NEON_EXT_V1, 0x0ee00b10, 0x0ff00f70, "vdup%c.8\t%16-19,7Q, %12-15r"}, + {FPU_NEON_EXT_V1, 0x0c400b10, 0x0ff00fd0, "vmov%c\t%0-3,5D, %12-15r, %16-19r"}, + {FPU_NEON_EXT_V1, 0x0c500b10, 0x0ff00fd0, "vmov%c\t%12-15r, %16-19r, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0x0e000b10, 0x0fd00f70, "vmov%c.32\t%16-19,7D[%21d], %12-15r"}, + {FPU_NEON_EXT_V1, 0x0e100b10, 0x0f500f70, "vmov%c.32\t%12-15r, %16-19,7D[%21d]"}, + {FPU_NEON_EXT_V1, 0x0e000b30, 0x0fd00f30, "vmov%c.16\t%16-19,7D[%6,21d], %12-15r"}, + {FPU_NEON_EXT_V1, 0x0e100b30, 0x0f500f30, "vmov%c.%23?us16\t%12-15r, %16-19,7D[%6,21d]"}, + {FPU_NEON_EXT_V1, 0x0e400b10, 0x0fd00f10, "vmov%c.8\t%16-19,7D[%5,6,21d], %12-15r"}, + {FPU_NEON_EXT_V1, 0x0e500b10, 0x0f500f10, "vmov%c.%23?us8\t%12-15r, %16-19,7D[%5,6,21d]"}, + + /* Floating point coprocessor (VFP) instructions */ + {FPU_VFP_EXT_V1xD, 0x0ef1fa10, 0x0fffffff, "fmstat%c"}, + {FPU_VFP_EXT_V1xD, 0x0ee00a10, 0x0fff0fff, "fmxr%c\tfpsid, %12-15r"}, + {FPU_VFP_EXT_V1xD, 0x0ee10a10, 0x0fff0fff, "fmxr%c\tfpscr, %12-15r"}, + {FPU_VFP_EXT_V1xD, 0x0ee60a10, 0x0fff0fff, "fmxr%c\tmvfr1, %12-15r"}, + {FPU_VFP_EXT_V1xD, 0x0ee70a10, 0x0fff0fff, "fmxr%c\tmvfr0, %12-15r"}, + {FPU_VFP_EXT_V1xD, 0x0ee80a10, 0x0fff0fff, "fmxr%c\tfpexc, %12-15r"}, + {FPU_VFP_EXT_V1xD, 0x0ee90a10, 0x0fff0fff, "fmxr%c\tfpinst, %12-15r\t@ Impl def"}, + {FPU_VFP_EXT_V1xD, 0x0eea0a10, 0x0fff0fff, "fmxr%c\tfpinst2, %12-15r\t@ Impl def"}, + {FPU_VFP_EXT_V1xD, 0x0ef00a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpsid"}, + {FPU_VFP_EXT_V1xD, 0x0ef10a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpscr"}, + {FPU_VFP_EXT_V1xD, 0x0ef60a10, 0x0fff0fff, "fmrx%c\t%12-15r, mvfr1"}, + {FPU_VFP_EXT_V1xD, 0x0ef70a10, 0x0fff0fff, "fmrx%c\t%12-15r, mvfr0"}, + {FPU_VFP_EXT_V1xD, 0x0ef80a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpexc"}, + {FPU_VFP_EXT_V1xD, 0x0ef90a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpinst\t@ Impl def"}, + {FPU_VFP_EXT_V1xD, 0x0efa0a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpinst2\t@ Impl def"}, + {FPU_VFP_EXT_V1, 0x0e000b10, 0x0ff00fff, "fmdlr%c\t%z2, %12-15r"}, + {FPU_VFP_EXT_V1, 0x0e100b10, 0x0ff00fff, "fmrdl%c\t%12-15r, %z2"}, + {FPU_VFP_EXT_V1, 0x0e200b10, 0x0ff00fff, "fmdhr%c\t%z2, %12-15r"}, + {FPU_VFP_EXT_V1, 0x0e300b10, 0x0ff00fff, "fmrdh%c\t%12-15r, %z2"}, + {FPU_VFP_EXT_V1xD, 0x0ee00a10, 0x0ff00fff, "fmxr%c\t, %12-15r"}, + {FPU_VFP_EXT_V1xD, 0x0ef00a10, 0x0ff00fff, "fmrx%c\t%12-15r, "}, + {FPU_VFP_EXT_V1xD, 0x0e000a10, 0x0ff00f7f, "fmsr%c\t%y2, %12-15r"}, + {FPU_VFP_EXT_V1xD, 0x0e100a10, 0x0ff00f7f, "fmrs%c\t%12-15r, %y2"}, + {FPU_VFP_EXT_V1xD, 0x0eb50a40, 0x0fbf0f70, "fcmp%7'ezs%c\t%y1"}, + {FPU_VFP_EXT_V1, 0x0eb50b40, 0x0fbf0f70, "fcmp%7'ezd%c\t%z1"}, + {FPU_VFP_EXT_V1xD, 0x0eb00a40, 0x0fbf0fd0, "fcpys%c\t%y1, %y0"}, + {FPU_VFP_EXT_V1xD, 0x0eb00ac0, 0x0fbf0fd0, "fabss%c\t%y1, %y0"}, + {FPU_VFP_EXT_V1, 0x0eb00b40, 0x0fbf0fd0, "fcpyd%c\t%z1, %z0"}, + {FPU_VFP_EXT_V1, 0x0eb00bc0, 0x0fbf0fd0, "fabsd%c\t%z1, %z0"}, + {FPU_VFP_EXT_V1xD, 0x0eb10a40, 0x0fbf0fd0, "fnegs%c\t%y1, %y0"}, + {FPU_VFP_EXT_V1xD, 0x0eb10ac0, 0x0fbf0fd0, "fsqrts%c\t%y1, %y0"}, + {FPU_VFP_EXT_V1, 0x0eb10b40, 0x0fbf0fd0, "fnegd%c\t%z1, %z0"}, + {FPU_VFP_EXT_V1, 0x0eb10bc0, 0x0fbf0fd0, "fsqrtd%c\t%z1, %z0"}, + {FPU_VFP_EXT_V1, 0x0eb70ac0, 0x0fbf0fd0, "fcvtds%c\t%z1, %y0"}, + {FPU_VFP_EXT_V1, 0x0eb70bc0, 0x0fbf0fd0, "fcvtsd%c\t%y1, %z0"}, + {FPU_VFP_EXT_V1xD, 0x0eb80a40, 0x0fbf0fd0, "fuitos%c\t%y1, %y0"}, + {FPU_VFP_EXT_V1xD, 0x0eb80ac0, 0x0fbf0fd0, "fsitos%c\t%y1, %y0"}, + {FPU_VFP_EXT_V1, 0x0eb80b40, 0x0fbf0fd0, "fuitod%c\t%z1, %y0"}, + {FPU_VFP_EXT_V1, 0x0eb80bc0, 0x0fbf0fd0, "fsitod%c\t%z1, %y0"}, + {FPU_VFP_EXT_V1xD, 0x0eb40a40, 0x0fbf0f50, "fcmp%7'es%c\t%y1, %y0"}, + {FPU_VFP_EXT_V1, 0x0eb40b40, 0x0fbf0f50, "fcmp%7'ed%c\t%z1, %z0"}, + {FPU_VFP_EXT_V3, 0x0eba0a40, 0x0fbe0f50, "f%16?us%7?lhtos%c\t%y1, #%5,0-3k"}, + {FPU_VFP_EXT_V3, 0x0eba0b40, 0x0fbe0f50, "f%16?us%7?lhtod%c\t%z1, #%5,0-3k"}, + {FPU_VFP_EXT_V1xD, 0x0ebc0a40, 0x0fbe0f50, "fto%16?sui%7'zs%c\t%y1, %y0"}, + {FPU_VFP_EXT_V1, 0x0ebc0b40, 0x0fbe0f50, "fto%16?sui%7'zd%c\t%y1, %z0"}, + {FPU_VFP_EXT_V3, 0x0ebe0a40, 0x0fbe0f50, "fto%16?us%7?lhs%c\t%y1, #%5,0-3k"}, + {FPU_VFP_EXT_V3, 0x0ebe0b40, 0x0fbe0f50, "fto%16?us%7?lhd%c\t%z1, #%5,0-3k"}, + {FPU_VFP_EXT_V1, 0x0c500b10, 0x0fb00ff0, "fmrrd%c\t%12-15r, %16-19r, %z0"}, + {FPU_VFP_EXT_V3, 0x0eb00a00, 0x0fb00ff0, "fconsts%c\t%y1, #%0-3,16-19d"}, + {FPU_VFP_EXT_V3, 0x0eb00b00, 0x0fb00ff0, "fconstd%c\t%z1, #%0-3,16-19d"}, + {FPU_VFP_EXT_V2, 0x0c400a10, 0x0ff00fd0, "fmsrr%c\t%y4, %12-15r, %16-19r"}, + {FPU_VFP_EXT_V2, 0x0c400b10, 0x0ff00fd0, "fmdrr%c\t%z0, %12-15r, %16-19r"}, + {FPU_VFP_EXT_V2, 0x0c500a10, 0x0ff00fd0, "fmrrs%c\t%12-15r, %16-19r, %y4"}, + {FPU_VFP_EXT_V1xD, 0x0e000a00, 0x0fb00f50, "fmacs%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1xD, 0x0e000a40, 0x0fb00f50, "fnmacs%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1, 0x0e000b00, 0x0fb00f50, "fmacd%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1, 0x0e000b40, 0x0fb00f50, "fnmacd%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1xD, 0x0e100a00, 0x0fb00f50, "fmscs%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1xD, 0x0e100a40, 0x0fb00f50, "fnmscs%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1, 0x0e100b00, 0x0fb00f50, "fmscd%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1, 0x0e100b40, 0x0fb00f50, "fnmscd%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1xD, 0x0e200a00, 0x0fb00f50, "fmuls%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1xD, 0x0e200a40, 0x0fb00f50, "fnmuls%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1, 0x0e200b00, 0x0fb00f50, "fmuld%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1, 0x0e200b40, 0x0fb00f50, "fnmuld%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1xD, 0x0e300a00, 0x0fb00f50, "fadds%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1xD, 0x0e300a40, 0x0fb00f50, "fsubs%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1, 0x0e300b00, 0x0fb00f50, "faddd%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1, 0x0e300b40, 0x0fb00f50, "fsubd%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1xD, 0x0e800a00, 0x0fb00f50, "fdivs%c\t%y1, %y2, %y0"}, + {FPU_VFP_EXT_V1, 0x0e800b00, 0x0fb00f50, "fdivd%c\t%z1, %z2, %z0"}, + {FPU_VFP_EXT_V1xD, 0x0d200a00, 0x0fb00f00, "fstmdbs%c\t%16-19r!, %y3"}, + {FPU_VFP_EXT_V1xD, 0x0d200b00, 0x0fb00f00, "fstmdb%0?xd%c\t%16-19r!, %z3"}, + {FPU_VFP_EXT_V1xD, 0x0d300a00, 0x0fb00f00, "fldmdbs%c\t%16-19r!, %y3"}, + {FPU_VFP_EXT_V1xD, 0x0d300b00, 0x0fb00f00, "fldmdb%0?xd%c\t%16-19r!, %z3"}, + {FPU_VFP_EXT_V1xD, 0x0d000a00, 0x0f300f00, "fsts%c\t%y1, %A"}, + {FPU_VFP_EXT_V1, 0x0d000b00, 0x0f300f00, "fstd%c\t%z1, %A"}, + {FPU_VFP_EXT_V1xD, 0x0d100a00, 0x0f300f00, "flds%c\t%y1, %A"}, + {FPU_VFP_EXT_V1, 0x0d100b00, 0x0f300f00, "fldd%c\t%z1, %A"}, + {FPU_VFP_EXT_V1xD, 0x0c800a00, 0x0f900f00, "fstmias%c\t%16-19r%21'!, %y3"}, + {FPU_VFP_EXT_V1xD, 0x0c800b00, 0x0f900f00, "fstmia%0?xd%c\t%16-19r%21'!, %z3"}, + {FPU_VFP_EXT_V1xD, 0x0c900a00, 0x0f900f00, "fldmias%c\t%16-19r%21'!, %y3"}, + {FPU_VFP_EXT_V1xD, 0x0c900b00, 0x0f900f00, "fldmia%0?xd%c\t%16-19r%21'!, %z3"}, + + /* Cirrus coprocessor instructions. */ + {ARM_CEXT_MAVERICK, 0x0d100400, 0x0f500f00, "cfldrs%c\tmvf%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0c100400, 0x0f500f00, "cfldrs%c\tmvf%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0d500400, 0x0f500f00, "cfldrd%c\tmvd%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0c500400, 0x0f500f00, "cfldrd%c\tmvd%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0d100500, 0x0f500f00, "cfldr32%c\tmvfx%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0c100500, 0x0f500f00, "cfldr32%c\tmvfx%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0d500500, 0x0f500f00, "cfldr64%c\tmvdx%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0c500500, 0x0f500f00, "cfldr64%c\tmvdx%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0d000400, 0x0f500f00, "cfstrs%c\tmvf%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0c000400, 0x0f500f00, "cfstrs%c\tmvf%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0d400400, 0x0f500f00, "cfstrd%c\tmvd%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0c400400, 0x0f500f00, "cfstrd%c\tmvd%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0d000500, 0x0f500f00, "cfstr32%c\tmvfx%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0c000500, 0x0f500f00, "cfstr32%c\tmvfx%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0d400500, 0x0f500f00, "cfstr64%c\tmvdx%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0c400500, 0x0f500f00, "cfstr64%c\tmvdx%12-15d, %A"}, + {ARM_CEXT_MAVERICK, 0x0e000450, 0x0ff00ff0, "cfmvsr%c\tmvf%16-19d, %12-15r"}, + {ARM_CEXT_MAVERICK, 0x0e100450, 0x0ff00ff0, "cfmvrs%c\t%12-15r, mvf%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000410, 0x0ff00ff0, "cfmvdlr%c\tmvd%16-19d, %12-15r"}, + {ARM_CEXT_MAVERICK, 0x0e100410, 0x0ff00ff0, "cfmvrdl%c\t%12-15r, mvd%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000430, 0x0ff00ff0, "cfmvdhr%c\tmvd%16-19d, %12-15r"}, + {ARM_CEXT_MAVERICK, 0x0e100430, 0x0ff00fff, "cfmvrdh%c\t%12-15r, mvd%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000510, 0x0ff00fff, "cfmv64lr%c\tmvdx%16-19d, %12-15r"}, + {ARM_CEXT_MAVERICK, 0x0e100510, 0x0ff00fff, "cfmvr64l%c\t%12-15r, mvdx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000530, 0x0ff00fff, "cfmv64hr%c\tmvdx%16-19d, %12-15r"}, + {ARM_CEXT_MAVERICK, 0x0e100530, 0x0ff00fff, "cfmvr64h%c\t%12-15r, mvdx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e200440, 0x0ff00fff, "cfmval32%c\tmvax%12-15d, mvfx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e100440, 0x0ff00fff, "cfmv32al%c\tmvfx%12-15d, mvax%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e200460, 0x0ff00fff, "cfmvam32%c\tmvax%12-15d, mvfx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e100460, 0x0ff00fff, "cfmv32am%c\tmvfx%12-15d, mvax%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e200480, 0x0ff00fff, "cfmvah32%c\tmvax%12-15d, mvfx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e100480, 0x0ff00fff, "cfmv32ah%c\tmvfx%12-15d, mvax%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e2004a0, 0x0ff00fff, "cfmva32%c\tmvax%12-15d, mvfx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e1004a0, 0x0ff00fff, "cfmv32a%c\tmvfx%12-15d, mvax%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e2004c0, 0x0ff00fff, "cfmva64%c\tmvax%12-15d, mvdx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e1004c0, 0x0ff00fff, "cfmv64a%c\tmvdx%12-15d, mvax%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e2004e0, 0x0fff0fff, "cfmvsc32%c\tdspsc, mvdx%12-15d"}, + {ARM_CEXT_MAVERICK, 0x0e1004e0, 0x0fff0fff, "cfmv32sc%c\tmvdx%12-15d, dspsc"}, + {ARM_CEXT_MAVERICK, 0x0e000400, 0x0ff00fff, "cfcpys%c\tmvf%12-15d, mvf%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000420, 0x0ff00fff, "cfcpyd%c\tmvd%12-15d, mvd%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000460, 0x0ff00fff, "cfcvtsd%c\tmvd%12-15d, mvf%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000440, 0x0ff00fff, "cfcvtds%c\tmvf%12-15d, mvd%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000480, 0x0ff00fff, "cfcvt32s%c\tmvf%12-15d, mvfx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e0004a0, 0x0ff00fff, "cfcvt32d%c\tmvd%12-15d, mvfx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e0004c0, 0x0ff00fff, "cfcvt64s%c\tmvf%12-15d, mvdx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e0004e0, 0x0ff00fff, "cfcvt64d%c\tmvd%12-15d, mvdx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e100580, 0x0ff00fff, "cfcvts32%c\tmvfx%12-15d, mvf%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e1005a0, 0x0ff00fff, "cfcvtd32%c\tmvfx%12-15d, mvd%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e1005c0, 0x0ff00fff, "cftruncs32%c\tmvfx%12-15d, mvf%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e1005e0, 0x0ff00fff, "cftruncd32%c\tmvfx%12-15d, mvd%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e000550, 0x0ff00ff0, "cfrshl32%c\tmvfx%16-19d, mvfx%0-3d, %12-15r"}, + {ARM_CEXT_MAVERICK, 0x0e000570, 0x0ff00ff0, "cfrshl64%c\tmvdx%16-19d, mvdx%0-3d, %12-15r"}, + {ARM_CEXT_MAVERICK, 0x0e000500, 0x0ff00f10, "cfsh32%c\tmvfx%12-15d, mvfx%16-19d, #%I"}, + {ARM_CEXT_MAVERICK, 0x0e200500, 0x0ff00f10, "cfsh64%c\tmvdx%12-15d, mvdx%16-19d, #%I"}, + {ARM_CEXT_MAVERICK, 0x0e100490, 0x0ff00ff0, "cfcmps%c\t%12-15r, mvf%16-19d, mvf%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e1004b0, 0x0ff00ff0, "cfcmpd%c\t%12-15r, mvd%16-19d, mvd%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e100590, 0x0ff00ff0, "cfcmp32%c\t%12-15r, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e1005b0, 0x0ff00ff0, "cfcmp64%c\t%12-15r, mvdx%16-19d, mvdx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e300400, 0x0ff00fff, "cfabss%c\tmvf%12-15d, mvf%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e300420, 0x0ff00fff, "cfabsd%c\tmvd%12-15d, mvd%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e300440, 0x0ff00fff, "cfnegs%c\tmvf%12-15d, mvf%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e300460, 0x0ff00fff, "cfnegd%c\tmvd%12-15d, mvd%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e300480, 0x0ff00ff0, "cfadds%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e3004a0, 0x0ff00ff0, "cfaddd%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e3004c0, 0x0ff00ff0, "cfsubs%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e3004e0, 0x0ff00ff0, "cfsubd%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e100400, 0x0ff00ff0, "cfmuls%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e100420, 0x0ff00ff0, "cfmuld%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e300500, 0x0ff00fff, "cfabs32%c\tmvfx%12-15d, mvfx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e300520, 0x0ff00fff, "cfabs64%c\tmvdx%12-15d, mvdx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e300540, 0x0ff00fff, "cfneg32%c\tmvfx%12-15d, mvfx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e300560, 0x0ff00fff, "cfneg64%c\tmvdx%12-15d, mvdx%16-19d"}, + {ARM_CEXT_MAVERICK, 0x0e300580, 0x0ff00ff0, "cfadd32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e3005a0, 0x0ff00ff0, "cfadd64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e3005c0, 0x0ff00ff0, "cfsub32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e3005e0, 0x0ff00ff0, "cfsub64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e100500, 0x0ff00ff0, "cfmul32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e100520, 0x0ff00ff0, "cfmul64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e100540, 0x0ff00ff0, "cfmac32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e100560, 0x0ff00ff0, "cfmsc32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e000600, 0x0ff00f10, "cfmadd32%c\tmvax%5-7d, mvfx%12-15d, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e100600, 0x0ff00f10, "cfmsub32%c\tmvax%5-7d, mvfx%12-15d, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e200600, 0x0ff00f10, "cfmadda32%c\tmvax%5-7d, mvax%12-15d, mvfx%16-19d, mvfx%0-3d"}, + {ARM_CEXT_MAVERICK, 0x0e300600, 0x0ff00f10, "cfmsuba32%c\tmvax%5-7d, mvax%12-15d, mvfx%16-19d, mvfx%0-3d"}, + + /* Generic coprocessor instructions */ + {ARM_EXT_V2, 0x0c400000, 0x0ff00000, "mcrr%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"}, + {ARM_EXT_V2, 0x0c500000, 0x0ff00000, "mrrc%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"}, + {ARM_EXT_V2, 0x0e000000, 0x0f000010, "cdp%c\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"}, + {ARM_EXT_V2, 0x0e100010, 0x0f100010, "mrc%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"}, + {ARM_EXT_V2, 0x0e000010, 0x0f100010, "mcr%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"}, + {ARM_EXT_V2, 0x0c000000, 0x0e100000, "stc%22'l%c\t%8-11d, cr%12-15d, %A"}, + {ARM_EXT_V2, 0x0c100000, 0x0e100000, "ldc%22'l%c\t%8-11d, cr%12-15d, %A"}, + + /* V6 coprocessor instructions */ + {ARM_EXT_V6, 0xfc500000, 0xfff00000, "mrrc2%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"}, + {ARM_EXT_V6, 0xfc400000, 0xfff00000, "mcrr2%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"}, + + /* V5 coprocessor instructions */ + {ARM_EXT_V5, 0xfc100000, 0xfe100000, "ldc2%22'l%c\t%8-11d, cr%12-15d, %A"}, + {ARM_EXT_V5, 0xfc000000, 0xfe100000, "stc2%22'l%c\t%8-11d, cr%12-15d, %A"}, + {ARM_EXT_V5, 0xfe000000, 0xff000010, "cdp2%c\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"}, + {ARM_EXT_V5, 0xfe000010, 0xff100010, "mcr2%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"}, + {ARM_EXT_V5, 0xfe100010, 0xff100010, "mrc2%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"}, + + {0, 0, 0, 0} +}; + +/* Neon opcode table: This does not encode the top byte -- that is + checked by the print_insn_neon routine, as it depends on whether we are + doing thumb32 or arm32 disassembly. */ + +/* print_insn_neon recognizes the following format control codes: + + %% % + + %c print condition code + %A print v{st,ld}[1234] operands + %B print v{st,ld}[1234] any one operands + %C print v{st,ld}[1234] single->all operands + %D print scalar + %E print vmov, vmvn, vorr, vbic encoded constant + %F print vtbl,vtbx register list + + %r print as an ARM register + %d print the bitfield in decimal + %e print the 2^N - bitfield in decimal + %D print as a NEON D register + %Q print as a NEON Q register + %R print as a NEON D or Q register + %Sn print byte scaled width limited by n + %Tn print short scaled width limited by n + %Un print long scaled width limited by n + + %'c print specified char iff bitfield is all ones + %`c print specified char iff bitfield is all zeroes + %?ab... select from array of values in big endian order */ + +static const struct opcode32 neon_opcodes[] = +{ + /* Extract */ + {FPU_NEON_EXT_V1, 0xf2b00840, 0xffb00850, "vext%c.8\t%12-15,22R, %16-19,7R, %0-3,5R, #%8-11d"}, + {FPU_NEON_EXT_V1, 0xf2b00000, 0xffb00810, "vext%c.8\t%12-15,22R, %16-19,7R, %0-3,5R, #%8-11d"}, + + /* Move data element to all lanes */ + {FPU_NEON_EXT_V1, 0xf3b40c00, 0xffb70f90, "vdup%c.32\t%12-15,22R, %0-3,5D[%19d]"}, + {FPU_NEON_EXT_V1, 0xf3b20c00, 0xffb30f90, "vdup%c.16\t%12-15,22R, %0-3,5D[%18-19d]"}, + {FPU_NEON_EXT_V1, 0xf3b10c00, 0xffb10f90, "vdup%c.8\t%12-15,22R, %0-3,5D[%17-19d]"}, + + /* Table lookup */ + {FPU_NEON_EXT_V1, 0xf3b00800, 0xffb00c50, "vtbl%c.8\t%12-15,22D, %F, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf3b00840, 0xffb00c50, "vtbx%c.8\t%12-15,22D, %F, %0-3,5D"}, + + /* Two registers, miscellaneous */ + {FPU_NEON_EXT_V1, 0xf2880a10, 0xfebf0fd0, "vmovl%c.%24?us8\t%12-15,22Q, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2900a10, 0xfebf0fd0, "vmovl%c.%24?us16\t%12-15,22Q, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2a00a10, 0xfebf0fd0, "vmovl%c.%24?us32\t%12-15,22Q, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf3b00500, 0xffbf0f90, "vcnt%c.8\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00580, 0xffbf0f90, "vmvn%c\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b20000, 0xffbf0f90, "vswp%c\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b20200, 0xffb30fd0, "vmovn%c.i%18-19T2\t%12-15,22D, %0-3,5Q"}, + {FPU_NEON_EXT_V1, 0xf3b20240, 0xffb30fd0, "vqmovun%c.s%18-19T2\t%12-15,22D, %0-3,5Q"}, + {FPU_NEON_EXT_V1, 0xf3b20280, 0xffb30fd0, "vqmovn%c.s%18-19T2\t%12-15,22D, %0-3,5Q"}, + {FPU_NEON_EXT_V1, 0xf3b202c0, 0xffb30fd0, "vqmovn%c.u%18-19T2\t%12-15,22D, %0-3,5Q"}, + {FPU_NEON_EXT_V1, 0xf3b20300, 0xffb30fd0, "vshll%c.i%18-19S2\t%12-15,22Q, %0-3,5D, #%18-19S2"}, + {FPU_NEON_EXT_V1, 0xf3bb0400, 0xffbf0e90, "vrecpe%c.%8?fu%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3bb0480, 0xffbf0e90, "vrsqrte%c.%8?fu%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00000, 0xffb30f90, "vrev64%c.%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00080, 0xffb30f90, "vrev32%c.%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00100, 0xffb30f90, "vrev16%c.%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00400, 0xffb30f90, "vcls%c.s%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00480, 0xffb30f90, "vclz%c.i%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00700, 0xffb30f90, "vqabs%c.s%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00780, 0xffb30f90, "vqneg%c.s%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b20080, 0xffb30f90, "vtrn%c.%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b20100, 0xffb30f90, "vuzp%c.%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b20180, 0xffb30f90, "vzip%c.%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b10000, 0xffb30b90, "vcgt%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"}, + {FPU_NEON_EXT_V1, 0xf3b10080, 0xffb30b90, "vcge%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"}, + {FPU_NEON_EXT_V1, 0xf3b10100, 0xffb30b90, "vceq%c.%10?fi%18-19S2\t%12-15,22R, %0-3,5R, #0"}, + {FPU_NEON_EXT_V1, 0xf3b10180, 0xffb30b90, "vcle%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"}, + {FPU_NEON_EXT_V1, 0xf3b10200, 0xffb30b90, "vclt%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"}, + {FPU_NEON_EXT_V1, 0xf3b10300, 0xffb30b90, "vabs%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b10380, 0xffb30b90, "vneg%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00200, 0xffb30f10, "vpaddl%c.%7?us%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b00600, 0xffb30f10, "vpadal%c.%7?us%18-19S2\t%12-15,22R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3b30600, 0xffb30e10, "vcvt%c.%7-8?usff%18-19Sa.%7-8?ffus%18-19Sa\t%12-15,22R, %0-3,5R"}, + + /* Three registers of the same length */ + {FPU_NEON_EXT_V1, 0xf2000110, 0xffb00f10, "vand%c\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2100110, 0xffb00f10, "vbic%c\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2200110, 0xffb00f10, "vorr%c\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2300110, 0xffb00f10, "vorn%c\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000110, 0xffb00f10, "veor%c\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3100110, 0xffb00f10, "vbsl%c\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3200110, 0xffb00f10, "vbit%c\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3300110, 0xffb00f10, "vbif%c\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000d00, 0xffa00f10, "vadd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000d10, 0xffa00f10, "vmla%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000e00, 0xffa00f10, "vceq%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000f00, 0xffa00f10, "vmax%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000f10, 0xffa00f10, "vrecps%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2200d00, 0xffa00f10, "vsub%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2200d10, 0xffa00f10, "vmls%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2200f00, 0xffa00f10, "vmin%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2200f10, 0xffa00f10, "vrsqrts%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000d00, 0xffa00f10, "vpadd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000d10, 0xffa00f10, "vmul%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000e00, 0xffa00f10, "vcge%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000e10, 0xffa00f10, "vacge%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000f00, 0xffa00f10, "vpmax%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3200d00, 0xffa00f10, "vabd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3200e00, 0xffa00f10, "vcgt%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3200e10, 0xffa00f10, "vacgt%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3200f00, 0xffa00f10, "vpmin%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000800, 0xff800f10, "vadd%c.i%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000810, 0xff800f10, "vtst%c.%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000900, 0xff800f10, "vmla%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000b00, 0xff800f10, "vqdmulh%c.s%20-21S6\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000b10, 0xff800f10, "vpadd%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000800, 0xff800f10, "vsub%c.i%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000810, 0xff800f10, "vceq%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000900, 0xff800f10, "vmls%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf3000b00, 0xff800f10, "vqrdmulh%c.s%20-21S6\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000000, 0xfe800f10, "vhadd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000010, 0xfe800f10, "vqadd%c.%24?us%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000100, 0xfe800f10, "vrhadd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000200, 0xfe800f10, "vhsub%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000210, 0xfe800f10, "vqsub%c.%24?us%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000300, 0xfe800f10, "vcgt%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000310, 0xfe800f10, "vcge%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000400, 0xfe800f10, "vshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"}, + {FPU_NEON_EXT_V1, 0xf2000410, 0xfe800f10, "vqshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"}, + {FPU_NEON_EXT_V1, 0xf2000500, 0xfe800f10, "vrshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"}, + {FPU_NEON_EXT_V1, 0xf2000510, 0xfe800f10, "vqrshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"}, + {FPU_NEON_EXT_V1, 0xf2000600, 0xfe800f10, "vmax%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000610, 0xfe800f10, "vmin%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000700, 0xfe800f10, "vabd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000710, 0xfe800f10, "vaba%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000910, 0xfe800f10, "vmul%c.%24?pi%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000a00, 0xfe800f10, "vpmax%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + {FPU_NEON_EXT_V1, 0xf2000a10, 0xfe800f10, "vpmin%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"}, + + /* One register and an immediate value */ + {FPU_NEON_EXT_V1, 0xf2800e10, 0xfeb80fb0, "vmov%c.i8\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800e30, 0xfeb80fb0, "vmov%c.i64\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800f10, 0xfeb80fb0, "vmov%c.f32\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800810, 0xfeb80db0, "vmov%c.i16\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800830, 0xfeb80db0, "vmvn%c.i16\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800910, 0xfeb80db0, "vorr%c.i16\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800930, 0xfeb80db0, "vbic%c.i16\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800c10, 0xfeb80eb0, "vmov%c.i32\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800c30, 0xfeb80eb0, "vmvn%c.i32\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800110, 0xfeb809b0, "vorr%c.i32\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800130, 0xfeb809b0, "vbic%c.i32\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800010, 0xfeb808b0, "vmov%c.i32\t%12-15,22R, %E"}, + {FPU_NEON_EXT_V1, 0xf2800030, 0xfeb808b0, "vmvn%c.i32\t%12-15,22R, %E"}, + + /* Two registers and a shift amount */ + {FPU_NEON_EXT_V1, 0xf2880810, 0xffb80fd0, "vshrn%c.i16\t%12-15,22D, %0-3,5Q, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880850, 0xffb80fd0, "vrshrn%c.i16\t%12-15,22D, %0-3,5Q, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880810, 0xfeb80fd0, "vqshrun%c.s16\t%12-15,22D, %0-3,5Q, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880850, 0xfeb80fd0, "vqrshrun%c.s16\t%12-15,22D, %0-3,5Q, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880910, 0xfeb80fd0, "vqshrn%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880950, 0xfeb80fd0, "vqrshrn%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880a10, 0xfeb80fd0, "vshll%c.%24?us8\t%12-15,22D, %0-3,5Q, #%16-18d"}, + {FPU_NEON_EXT_V1, 0xf2900810, 0xffb00fd0, "vshrn%c.i32\t%12-15,22D, %0-3,5Q, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900850, 0xffb00fd0, "vrshrn%c.i32\t%12-15,22D, %0-3,5Q, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2880510, 0xffb80f90, "vshl%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18d"}, + {FPU_NEON_EXT_V1, 0xf3880410, 0xffb80f90, "vsri%c.8\t%12-15,22R, %0-3,5R, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf3880510, 0xffb80f90, "vsli%c.8\t%12-15,22R, %0-3,5R, #%16-18d"}, + {FPU_NEON_EXT_V1, 0xf3880610, 0xffb80f90, "vqshlu%c.s8\t%12-15,22R, %0-3,5R, #%16-18d"}, + {FPU_NEON_EXT_V1, 0xf2900810, 0xfeb00fd0, "vqshrun%c.s32\t%12-15,22D, %0-3,5Q, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900850, 0xfeb00fd0, "vqrshrun%c.s32\t%12-15,22D, %0-3,5Q, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900910, 0xfeb00fd0, "vqshrn%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900950, 0xfeb00fd0, "vqrshrn%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900a10, 0xfeb00fd0, "vshll%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-19d"}, + {FPU_NEON_EXT_V1, 0xf2880010, 0xfeb80f90, "vshr%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880110, 0xfeb80f90, "vsra%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880210, 0xfeb80f90, "vrshr%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880310, 0xfeb80f90, "vrsra%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"}, + {FPU_NEON_EXT_V1, 0xf2880710, 0xfeb80f90, "vqshl%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18d"}, + {FPU_NEON_EXT_V1, 0xf2a00810, 0xffa00fd0, "vshrn%c.i64\t%12-15,22D, %0-3,5Q, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2a00850, 0xffa00fd0, "vrshrn%c.i64\t%12-15,22D, %0-3,5Q, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2900510, 0xffb00f90, "vshl%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19d"}, + {FPU_NEON_EXT_V1, 0xf3900410, 0xffb00f90, "vsri%c.16\t%12-15,22R, %0-3,5R, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf3900510, 0xffb00f90, "vsli%c.16\t%12-15,22R, %0-3,5R, #%16-19d"}, + {FPU_NEON_EXT_V1, 0xf3900610, 0xffb00f90, "vqshlu%c.s16\t%12-15,22R, %0-3,5R, #%16-19d"}, + {FPU_NEON_EXT_V1, 0xf2a00a10, 0xfea00fd0, "vshll%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-20d"}, + {FPU_NEON_EXT_V1, 0xf2900010, 0xfeb00f90, "vshr%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900110, 0xfeb00f90, "vsra%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900210, 0xfeb00f90, "vrshr%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900310, 0xfeb00f90, "vrsra%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"}, + {FPU_NEON_EXT_V1, 0xf2900710, 0xfeb00f90, "vqshl%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19d"}, + {FPU_NEON_EXT_V1, 0xf2800810, 0xfec00fd0, "vqshrun%c.s64\t%12-15,22D, %0-3,5Q, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2800850, 0xfec00fd0, "vqrshrun%c.s64\t%12-15,22D, %0-3,5Q, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2800910, 0xfec00fd0, "vqshrn%c.%24?us64\t%12-15,22D, %0-3,5Q, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2800950, 0xfec00fd0, "vqrshrn%c.%24?us64\t%12-15,22D, %0-3,5Q, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2a00510, 0xffa00f90, "vshl%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20d"}, + {FPU_NEON_EXT_V1, 0xf3a00410, 0xffa00f90, "vsri%c.32\t%12-15,22R, %0-3,5R, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf3a00510, 0xffa00f90, "vsli%c.32\t%12-15,22R, %0-3,5R, #%16-20d"}, + {FPU_NEON_EXT_V1, 0xf3a00610, 0xffa00f90, "vqshlu%c.s32\t%12-15,22R, %0-3,5R, #%16-20d"}, + {FPU_NEON_EXT_V1, 0xf2a00010, 0xfea00f90, "vshr%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2a00110, 0xfea00f90, "vsra%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2a00210, 0xfea00f90, "vrshr%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2a00310, 0xfea00f90, "vrsra%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"}, + {FPU_NEON_EXT_V1, 0xf2a00710, 0xfea00f90, "vqshl%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20d"}, + {FPU_NEON_EXT_V1, 0xf2800590, 0xff800f90, "vshl%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21d"}, + {FPU_NEON_EXT_V1, 0xf3800490, 0xff800f90, "vsri%c.64\t%12-15,22R, %0-3,5R, #%16-21e"}, + {FPU_NEON_EXT_V1, 0xf3800590, 0xff800f90, "vsli%c.64\t%12-15,22R, %0-3,5R, #%16-21d"}, + {FPU_NEON_EXT_V1, 0xf3800690, 0xff800f90, "vqshlu%c.s64\t%12-15,22R, %0-3,5R, #%16-21d"}, + {FPU_NEON_EXT_V1, 0xf2800090, 0xfe800f90, "vshr%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"}, + {FPU_NEON_EXT_V1, 0xf2800190, 0xfe800f90, "vsra%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"}, + {FPU_NEON_EXT_V1, 0xf2800290, 0xfe800f90, "vrshr%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"}, + {FPU_NEON_EXT_V1, 0xf2800390, 0xfe800f90, "vrsra%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"}, + {FPU_NEON_EXT_V1, 0xf2800790, 0xfe800f90, "vqshl%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21d"}, + {FPU_NEON_EXT_V1, 0xf2a00e10, 0xfea00e90, "vcvt%c.%24,8?usff32.%24,8?ffus32\t%12-15,22R, %0-3,5R, #%16-20e"}, + + /* Three registers of different lengths */ + {FPU_NEON_EXT_V1, 0xf2800e00, 0xfea00f50, "vmull%c.p%20S0\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800400, 0xff800f50, "vaddhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"}, + {FPU_NEON_EXT_V1, 0xf2800600, 0xff800f50, "vsubhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"}, + {FPU_NEON_EXT_V1, 0xf2800900, 0xff800f50, "vqdmlal%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800b00, 0xff800f50, "vqdmlsl%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800d00, 0xff800f50, "vqdmull%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf3800400, 0xff800f50, "vraddhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"}, + {FPU_NEON_EXT_V1, 0xf3800600, 0xff800f50, "vrsubhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"}, + {FPU_NEON_EXT_V1, 0xf2800000, 0xfe800f50, "vaddl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800100, 0xfe800f50, "vaddw%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7Q, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800200, 0xfe800f50, "vsubl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800300, 0xfe800f50, "vsubw%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7Q, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800500, 0xfe800f50, "vabal%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800700, 0xfe800f50, "vabdl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800800, 0xfe800f50, "vmlal%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800a00, 0xfe800f50, "vmlsl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + {FPU_NEON_EXT_V1, 0xf2800c00, 0xfe800f50, "vmull%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"}, + + /* Two registers and a scalar */ + {FPU_NEON_EXT_V1, 0xf2800040, 0xff800f50, "vmla%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800140, 0xff800f50, "vmla%c.f%20-21Sa\t%12-15,22D, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800340, 0xff800f50, "vqdmlal%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800440, 0xff800f50, "vmls%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800540, 0xff800f50, "vmls%c.f%20-21S6\t%12-15,22D, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800740, 0xff800f50, "vqdmlsl%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800840, 0xff800f50, "vmul%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800940, 0xff800f50, "vmul%c.f%20-21Sa\t%12-15,22D, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800b40, 0xff800f50, "vqdmull%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800c40, 0xff800f50, "vqdmulh%c.s%20-21S6\t%12-15,22D, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800d40, 0xff800f50, "vqrdmulh%c.s%20-21S6\t%12-15,22D, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf3800040, 0xff800f50, "vmla%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"}, + {FPU_NEON_EXT_V1, 0xf3800140, 0xff800f50, "vmla%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"}, + {FPU_NEON_EXT_V1, 0xf3800440, 0xff800f50, "vmls%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"}, + {FPU_NEON_EXT_V1, 0xf3800540, 0xff800f50, "vmls%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"}, + {FPU_NEON_EXT_V1, 0xf3800840, 0xff800f50, "vmul%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"}, + {FPU_NEON_EXT_V1, 0xf3800940, 0xff800f50, "vmul%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"}, + {FPU_NEON_EXT_V1, 0xf3800c40, 0xff800f50, "vqdmulh%c.s%20-21S6\t%12-15,22Q, %16-19,7Q, %D"}, + {FPU_NEON_EXT_V1, 0xf3800d40, 0xff800f50, "vqrdmulh%c.s%20-21S6\t%12-15,22Q, %16-19,7Q, %D"}, + {FPU_NEON_EXT_V1, 0xf2800240, 0xfe800f50, "vmlal%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800640, 0xfe800f50, "vmlsl%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"}, + {FPU_NEON_EXT_V1, 0xf2800a40, 0xfe800f50, "vmull%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"}, + + /* Element and structure load/store */ + {FPU_NEON_EXT_V1, 0xf4a00fc0, 0xffb00fc0, "vld4%c.32\t%C"}, + {FPU_NEON_EXT_V1, 0xf4a00c00, 0xffb00f00, "vld1%c.%6-7S2\t%C"}, + {FPU_NEON_EXT_V1, 0xf4a00d00, 0xffb00f00, "vld2%c.%6-7S2\t%C"}, + {FPU_NEON_EXT_V1, 0xf4a00e00, 0xffb00f00, "vld3%c.%6-7S2\t%C"}, + {FPU_NEON_EXT_V1, 0xf4a00f00, 0xffb00f00, "vld4%c.%6-7S2\t%C"}, + {FPU_NEON_EXT_V1, 0xf4000200, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000300, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000400, 0xff900f00, "v%21?ls%21?dt3%c.%6-7S2\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000500, 0xff900f00, "v%21?ls%21?dt3%c.%6-7S2\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000600, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000700, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000800, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000900, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000a00, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"}, + {FPU_NEON_EXT_V1, 0xf4000000, 0xff900e00, "v%21?ls%21?dt4%c.%6-7S2\t%A"}, + {FPU_NEON_EXT_V1, 0xf4800000, 0xff900300, "v%21?ls%21?dt1%c.%10-11S2\t%B"}, + {FPU_NEON_EXT_V1, 0xf4800100, 0xff900300, "v%21?ls%21?dt2%c.%10-11S2\t%B"}, + {FPU_NEON_EXT_V1, 0xf4800200, 0xff900300, "v%21?ls%21?dt3%c.%10-11S2\t%B"}, + {FPU_NEON_EXT_V1, 0xf4800300, 0xff900300, "v%21?ls%21?dt4%c.%10-11S2\t%B"}, + + {0,0 ,0, 0} +}; + +/* Opcode tables: ARM, 16-bit Thumb, 32-bit Thumb. All three are partially + ordered: they must be searched linearly from the top to obtain a correct + match. */ + +/* print_insn_arm recognizes the following format control codes: + + %% % + + %a print address for ldr/str instruction + %s print address for ldr/str halfword/signextend instruction + %b print branch destination + %c print condition code (always bits 28-31) + %m print register mask for ldm/stm instruction + %o print operand2 (immediate or register + shift) + %p print 'p' iff bits 12-15 are 15 + %t print 't' iff bit 21 set and bit 24 clear + %B print arm BLX(1) destination + %C print the PSR sub type. + %U print barrier type. + %P print address for pli instruction. + + %r print as an ARM register + %d print the bitfield in decimal + %W print the bitfield plus one in decimal + %x print the bitfield in hex + %X print the bitfield as 1 hex digit without leading "0x" + + %'c print specified char iff bitfield is all ones + %`c print specified char iff bitfield is all zeroes + %?ab... select from array of values in big endian order + + %e print arm SMI operand (bits 0..7,8..19). + %E print the LSB and WIDTH fields of a BFI or BFC instruction. + %V print the 16-bit immediate field of a MOVT or MOVW instruction. */ + +static const struct opcode32 arm_opcodes[] = +{ + /* ARM instructions. */ + {ARM_EXT_V1, 0xe1a00000, 0xffffffff, "nop\t\t\t(mov r0,r0)"}, + {ARM_EXT_V4T | ARM_EXT_V5, 0x012FFF10, 0x0ffffff0, "bx%c\t%0-3r"}, + {ARM_EXT_V2, 0x00000090, 0x0fe000f0, "mul%20's%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V2, 0x00200090, 0x0fe000f0, "mla%20's%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V2S, 0x01000090, 0x0fb00ff0, "swp%22'b%c\t%12-15r, %0-3r, [%16-19r]"}, + {ARM_EXT_V3M, 0x00800090, 0x0fa000f0, "%22?sumull%20's%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V3M, 0x00a00090, 0x0fa000f0, "%22?sumlal%20's%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + + /* IDIV instructions. */ + {ARM_EXT_DIV, 0x0710f010, 0x0ff0f0f0, "sdiv%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_DIV, 0x0730f010, 0x0ff0f0f0, "udiv%c\t%16-19r, %0-3r, %8-11r"}, + + /* V7 instructions. */ + {ARM_EXT_V7, 0xf450f000, 0xfd70f000, "pli\t%P"}, + {ARM_EXT_V7, 0x0320f0f0, 0x0ffffff0, "dbg%c\t#%0-3d"}, + {ARM_EXT_V7, 0xf57ff050, 0xfffffff0, "dmb\t%U"}, + {ARM_EXT_V7, 0xf57ff040, 0xfffffff0, "dsb\t%U"}, + {ARM_EXT_V7, 0xf57ff060, 0xfffffff0, "isb\t%U"}, + + /* ARM V6T2 instructions. */ + {ARM_EXT_V6T2, 0x07c0001f, 0x0fe0007f, "bfc%c\t%12-15r, %E"}, + {ARM_EXT_V6T2, 0x07c00010, 0x0fe00070, "bfi%c\t%12-15r, %0-3r, %E"}, + {ARM_EXT_V6T2, 0x00600090, 0x0ff000f0, "mls%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V6T2, 0x006000b0, 0x0f7000f0, "strht%c\t%12-15r, %s"}, + {ARM_EXT_V6T2, 0x00300090, 0x0f300090, "ldr%6's%5?hbt%c\t%12-15r, %s"}, + {ARM_EXT_V6T2, 0x03000000, 0x0ff00000, "movw%c\t%12-15r, %V"}, + {ARM_EXT_V6T2, 0x03400000, 0x0ff00000, "movt%c\t%12-15r, %V"}, + {ARM_EXT_V6T2, 0x06ff0f30, 0x0fff0ff0, "rbit%c\t%12-15r, %0-3r"}, + {ARM_EXT_V6T2, 0x07a00050, 0x0fa00070, "%22?usbfx%c\t%12-15r, %0-3r, #%7-11d, #%16-20W"}, + + /* ARM V6Z instructions. */ + {ARM_EXT_V6Z, 0x01600070, 0x0ff000f0, "smc%c\t%e"}, + + /* ARM V6K instructions. */ + {ARM_EXT_V6K, 0xf57ff01f, 0xffffffff, "clrex"}, + {ARM_EXT_V6K, 0x01d00f9f, 0x0ff00fff, "ldrexb%c\t%12-15r, [%16-19r]"}, + {ARM_EXT_V6K, 0x01b00f9f, 0x0ff00fff, "ldrexd%c\t%12-15r, [%16-19r]"}, + {ARM_EXT_V6K, 0x01f00f9f, 0x0ff00fff, "ldrexh%c\t%12-15r, [%16-19r]"}, + {ARM_EXT_V6K, 0x01c00f90, 0x0ff00ff0, "strexb%c\t%12-15r, %0-3r, [%16-19r]"}, + {ARM_EXT_V6K, 0x01a00f90, 0x0ff00ff0, "strexd%c\t%12-15r, %0-3r, [%16-19r]"}, + {ARM_EXT_V6K, 0x01e00f90, 0x0ff00ff0, "strexh%c\t%12-15r, %0-3r, [%16-19r]"}, + + /* ARM V6K NOP hints. */ + {ARM_EXT_V6K, 0x0320f001, 0x0fffffff, "yield%c"}, + {ARM_EXT_V6K, 0x0320f002, 0x0fffffff, "wfe%c"}, + {ARM_EXT_V6K, 0x0320f003, 0x0fffffff, "wfi%c"}, + {ARM_EXT_V6K, 0x0320f004, 0x0fffffff, "sev%c"}, + {ARM_EXT_V6K, 0x0320f000, 0x0fffff00, "nop%c\t{%0-7d}"}, + + /* ARM V6 instructions. */ + {ARM_EXT_V6, 0xf1080000, 0xfffffe3f, "cpsie\t%8'a%7'i%6'f"}, + {ARM_EXT_V6, 0xf10a0000, 0xfffffe20, "cpsie\t%8'a%7'i%6'f,#%0-4d"}, + {ARM_EXT_V6, 0xf10C0000, 0xfffffe3f, "cpsid\t%8'a%7'i%6'f"}, + {ARM_EXT_V6, 0xf10e0000, 0xfffffe20, "cpsid\t%8'a%7'i%6'f,#%0-4d"}, + {ARM_EXT_V6, 0xf1000000, 0xfff1fe20, "cps\t#%0-4d"}, + {ARM_EXT_V6, 0x06800010, 0x0ff00ff0, "pkhbt%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06800010, 0x0ff00070, "pkhbt%c\t%12-15r, %16-19r, %0-3r, lsl #%7-11d"}, + {ARM_EXT_V6, 0x06800050, 0x0ff00ff0, "pkhtb%c\t%12-15r, %16-19r, %0-3r, asr #32"}, + {ARM_EXT_V6, 0x06800050, 0x0ff00070, "pkhtb%c\t%12-15r, %16-19r, %0-3r, asr #%7-11d"}, + {ARM_EXT_V6, 0x01900f9f, 0x0ff00fff, "ldrex%c\tr%12-15d, [%16-19r]"}, + {ARM_EXT_V6, 0x06200f10, 0x0ff00ff0, "qadd16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06200f90, 0x0ff00ff0, "qadd8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06200f30, 0x0ff00ff0, "qaddsubx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06200f70, 0x0ff00ff0, "qsub16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06200ff0, 0x0ff00ff0, "qsub8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06200f50, 0x0ff00ff0, "qsubaddx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06100f10, 0x0ff00ff0, "sadd16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06100f90, 0x0ff00ff0, "sadd8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06100f30, 0x0ff00ff0, "saddaddx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06300f10, 0x0ff00ff0, "shadd16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06300f90, 0x0ff00ff0, "shadd8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06300f30, 0x0ff00ff0, "shaddsubx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06300f70, 0x0ff00ff0, "shsub16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06300ff0, 0x0ff00ff0, "shsub8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06300f50, 0x0ff00ff0, "shsubaddx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06100f70, 0x0ff00ff0, "ssub16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06100ff0, 0x0ff00ff0, "ssub8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06100f50, 0x0ff00ff0, "ssubaddx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06500f10, 0x0ff00ff0, "uadd16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06500f90, 0x0ff00ff0, "uadd8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06500f30, 0x0ff00ff0, "uaddsubx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06700f10, 0x0ff00ff0, "uhadd16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06700f90, 0x0ff00ff0, "uhadd8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06700f30, 0x0ff00ff0, "uhaddsubx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06700f70, 0x0ff00ff0, "uhsub16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06700ff0, 0x0ff00ff0, "uhsub8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06700f50, 0x0ff00ff0, "uhsubaddx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06600f10, 0x0ff00ff0, "uqadd16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06600f90, 0x0ff00ff0, "uqadd8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06600f30, 0x0ff00ff0, "uqaddsubx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06600f70, 0x0ff00ff0, "uqsub16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06600ff0, 0x0ff00ff0, "uqsub8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06600f50, 0x0ff00ff0, "uqsubaddx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06500f70, 0x0ff00ff0, "usub16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06500ff0, 0x0ff00ff0, "usub8%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06500f50, 0x0ff00ff0, "usubaddx%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06bf0f30, 0x0fff0ff0, "rev%c\t\%12-15r, %0-3r"}, + {ARM_EXT_V6, 0x06bf0fb0, 0x0fff0ff0, "rev16%c\t\%12-15r, %0-3r"}, + {ARM_EXT_V6, 0x06ff0fb0, 0x0fff0ff0, "revsh%c\t\%12-15r, %0-3r"}, + {ARM_EXT_V6, 0xf8100a00, 0xfe50ffff, "rfe%23?id%24?ba\t\%16-19r%21'!"}, + {ARM_EXT_V6, 0x06bf0070, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r"}, + {ARM_EXT_V6, 0x06bf0470, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06bf0870, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06bf0c70, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x068f0070, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r"}, + {ARM_EXT_V6, 0x068f0470, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x068f0870, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x068f0c70, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06af0070, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r"}, + {ARM_EXT_V6, 0x06af0470, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06af0870, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06af0c70, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06ff0070, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r"}, + {ARM_EXT_V6, 0x06ff0470, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06ff0870, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06ff0c70, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06cf0070, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r"}, + {ARM_EXT_V6, 0x06cf0470, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06cf0870, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06cf0c70, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06ef0070, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r"}, + {ARM_EXT_V6, 0x06ef0470, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06ef0870, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06ef0c70, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06b00070, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06b00470, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06b00870, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06b00c70, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06800070, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06800470, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06800870, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06800c70, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06a00070, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06a00470, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06a00870, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06a00c70, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06f00070, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06f00470, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06f00870, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06f00c70, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06c00070, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06c00470, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06c00870, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06c00c70, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ROR #24"}, + {ARM_EXT_V6, 0x06e00070, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0x06e00470, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #8"}, + {ARM_EXT_V6, 0x06e00870, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #16"}, + {ARM_EXT_V6, 0x06e00c70, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #24"}, + {ARM_EXT_V6, 0x06800fb0, 0x0ff00ff0, "sel%c\t%12-15r, %16-19r, %0-3r"}, + {ARM_EXT_V6, 0xf1010000, 0xfffffc00, "setend\t%9?ble"}, + {ARM_EXT_V6, 0x0700f010, 0x0ff0f0d0, "smuad%5'x%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V6, 0x0700f050, 0x0ff0f0d0, "smusd%5'x%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V6, 0x07000010, 0x0ff000d0, "smlad%5'x%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V6, 0x07400010, 0x0ff000d0, "smlald%5'x%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V6, 0x07000050, 0x0ff000d0, "smlsd%5'x%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V6, 0x07400050, 0x0ff000d0, "smlsld%5'x%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V6, 0x0750f010, 0x0ff0f0d0, "smmul%5'r%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V6, 0x07500010, 0x0ff000d0, "smmla%5'r%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V6, 0x075000d0, 0x0ff000d0, "smmls%5'r%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V6, 0xf84d0500, 0xfe5fffe0, "srs%23?id%24?ba\t%16-19r%21'!, #%0-4d"}, + {ARM_EXT_V6, 0x06a00010, 0x0fe00ff0, "ssat%c\t%12-15r, #%16-20W, %0-3r"}, + {ARM_EXT_V6, 0x06a00010, 0x0fe00070, "ssat%c\t%12-15r, #%16-20W, %0-3r, lsl #%7-11d"}, + {ARM_EXT_V6, 0x06a00050, 0x0fe00070, "ssat%c\t%12-15r, #%16-20W, %0-3r, asr #%7-11d"}, + {ARM_EXT_V6, 0x06a00f30, 0x0ff00ff0, "ssat16%c\t%12-15r, #%16-19W, %0-3r"}, + {ARM_EXT_V6, 0x01800f90, 0x0ff00ff0, "strex%c\t%12-15r, %0-3r, [%16-19r]"}, + {ARM_EXT_V6, 0x00400090, 0x0ff000f0, "umaal%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V6, 0x0780f010, 0x0ff0f0f0, "usad8%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V6, 0x07800010, 0x0ff000f0, "usada8%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V6, 0x06e00010, 0x0fe00ff0, "usat%c\t%12-15r, #%16-20d, %0-3r"}, + {ARM_EXT_V6, 0x06e00010, 0x0fe00070, "usat%c\t%12-15r, #%16-20d, %0-3r, lsl #%7-11d"}, + {ARM_EXT_V6, 0x06e00050, 0x0fe00070, "usat%c\t%12-15r, #%16-20d, %0-3r, asr #%7-11d"}, + {ARM_EXT_V6, 0x06e00f30, 0x0ff00ff0, "usat16%c\t%12-15r, #%16-19d, %0-3r"}, + + /* V5J instruction. */ + {ARM_EXT_V5J, 0x012fff20, 0x0ffffff0, "bxj%c\t%0-3r"}, + + /* V5 Instructions. */ + {ARM_EXT_V5, 0xe1200070, 0xfff000f0, "bkpt\t0x%16-19X%12-15X%8-11X%0-3X"}, + {ARM_EXT_V5, 0xfa000000, 0xfe000000, "blx\t%B"}, + {ARM_EXT_V5, 0x012fff30, 0x0ffffff0, "blx%c\t%0-3r"}, + {ARM_EXT_V5, 0x016f0f10, 0x0fff0ff0, "clz%c\t%12-15r, %0-3r"}, + + /* V5E "El Segundo" Instructions. */ + {ARM_EXT_V5E, 0x000000d0, 0x0e1000f0, "ldrd%c\t%12-15r, %s"}, + {ARM_EXT_V5E, 0x000000f0, 0x0e1000f0, "strd%c\t%12-15r, %s"}, + {ARM_EXT_V5E, 0xf450f000, 0xfc70f000, "pld\t%a"}, + {ARM_EXT_V5ExP, 0x01000080, 0x0ff000f0, "smlabb%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V5ExP, 0x010000a0, 0x0ff000f0, "smlatb%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V5ExP, 0x010000c0, 0x0ff000f0, "smlabt%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V5ExP, 0x010000e0, 0x0ff000f0, "smlatt%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + + {ARM_EXT_V5ExP, 0x01200080, 0x0ff000f0, "smlawb%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + {ARM_EXT_V5ExP, 0x012000c0, 0x0ff000f0, "smlawt%c\t%16-19r, %0-3r, %8-11r, %12-15r"}, + + {ARM_EXT_V5ExP, 0x01400080, 0x0ff000f0, "smlalbb%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V5ExP, 0x014000a0, 0x0ff000f0, "smlaltb%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V5ExP, 0x014000c0, 0x0ff000f0, "smlalbt%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V5ExP, 0x014000e0, 0x0ff000f0, "smlaltt%c\t%12-15r, %16-19r, %0-3r, %8-11r"}, + + {ARM_EXT_V5ExP, 0x01600080, 0x0ff0f0f0, "smulbb%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V5ExP, 0x016000a0, 0x0ff0f0f0, "smultb%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V5ExP, 0x016000c0, 0x0ff0f0f0, "smulbt%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V5ExP, 0x016000e0, 0x0ff0f0f0, "smultt%c\t%16-19r, %0-3r, %8-11r"}, + + {ARM_EXT_V5ExP, 0x012000a0, 0x0ff0f0f0, "smulwb%c\t%16-19r, %0-3r, %8-11r"}, + {ARM_EXT_V5ExP, 0x012000e0, 0x0ff0f0f0, "smulwt%c\t%16-19r, %0-3r, %8-11r"}, + + {ARM_EXT_V5ExP, 0x01000050, 0x0ff00ff0, "qadd%c\t%12-15r, %0-3r, %16-19r"}, + {ARM_EXT_V5ExP, 0x01400050, 0x0ff00ff0, "qdadd%c\t%12-15r, %0-3r, %16-19r"}, + {ARM_EXT_V5ExP, 0x01200050, 0x0ff00ff0, "qsub%c\t%12-15r, %0-3r, %16-19r"}, + {ARM_EXT_V5ExP, 0x01600050, 0x0ff00ff0, "qdsub%c\t%12-15r, %0-3r, %16-19r"}, + + /* ARM Instructions. */ + {ARM_EXT_V1, 0x00000090, 0x0e100090, "str%6's%5?hb%c\t%12-15r, %s"}, + {ARM_EXT_V1, 0x00100090, 0x0e100090, "ldr%6's%5?hb%c\t%12-15r, %s"}, + {ARM_EXT_V1, 0x00000000, 0x0de00000, "and%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x00200000, 0x0de00000, "eor%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x00400000, 0x0de00000, "sub%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x00600000, 0x0de00000, "rsb%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x00800000, 0x0de00000, "add%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x00a00000, 0x0de00000, "adc%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x00c00000, 0x0de00000, "sbc%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x00e00000, 0x0de00000, "rsc%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V3, 0x0120f000, 0x0db0f000, "msr%c\t%22?SCPSR%C, %o"}, + {ARM_EXT_V3, 0x010f0000, 0x0fbf0fff, "mrs%c\t%12-15r, %22?SCPSR"}, + {ARM_EXT_V1, 0x01000000, 0x0de00000, "tst%p%c\t%16-19r, %o"}, + {ARM_EXT_V1, 0x01200000, 0x0de00000, "teq%p%c\t%16-19r, %o"}, + {ARM_EXT_V1, 0x01400000, 0x0de00000, "cmp%p%c\t%16-19r, %o"}, + {ARM_EXT_V1, 0x01600000, 0x0de00000, "cmn%p%c\t%16-19r, %o"}, + {ARM_EXT_V1, 0x01800000, 0x0de00000, "orr%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x03a00000, 0x0fef0000, "mov%20's%c\t%12-15r, %o"}, + {ARM_EXT_V1, 0x01a00000, 0x0def0ff0, "mov%20's%c\t%12-15r, %0-3r"}, + {ARM_EXT_V1, 0x01a00000, 0x0def0060, "lsl%20's%c\t%12-15r, %q"}, + {ARM_EXT_V1, 0x01a00020, 0x0def0060, "lsr%20's%c\t%12-15r, %q"}, + {ARM_EXT_V1, 0x01a00040, 0x0def0060, "asr%20's%c\t%12-15r, %q"}, + {ARM_EXT_V1, 0x01a00060, 0x0def0ff0, "rrx%20's%c\t%12-15r, %0-3r"}, + {ARM_EXT_V1, 0x01a00060, 0x0def0060, "ror%20's%c\t%12-15r, %q"}, + {ARM_EXT_V1, 0x01c00000, 0x0de00000, "bic%20's%c\t%12-15r, %16-19r, %o"}, + {ARM_EXT_V1, 0x01e00000, 0x0de00000, "mvn%20's%c\t%12-15r, %o"}, + {ARM_EXT_V1, 0x052d0004, 0x0fff0fff, "push%c\t{%12-15r}\t\t; (str%c %12-15r, %a)"}, + {ARM_EXT_V1, 0x04000000, 0x0e100000, "str%22'b%t%c\t%12-15r, %a"}, + {ARM_EXT_V1, 0x06000000, 0x0e100ff0, "str%22'b%t%c\t%12-15r, %a"}, + {ARM_EXT_V1, 0x04000000, 0x0c100010, "str%22'b%t%c\t%12-15r, %a"}, + {ARM_EXT_V1, 0x06000010, 0x0e000010, "undefined"}, + {ARM_EXT_V1, 0x049d0004, 0x0fff0fff, "pop%c\t{%12-15r}\t\t; (ldr%c %12-15r, %a)"}, + {ARM_EXT_V1, 0x04100000, 0x0c100000, "ldr%22'b%t%c\t%12-15r, %a"}, + {ARM_EXT_V1, 0x092d0000, 0x0fff0000, "push%c\t%m"}, + {ARM_EXT_V1, 0x08800000, 0x0ff00000, "stm%c\t%16-19r%21'!, %m%22'^"}, + {ARM_EXT_V1, 0x08000000, 0x0e100000, "stm%23?id%24?ba%c\t%16-19r%21'!, %m%22'^"}, + {ARM_EXT_V1, 0x08bd0000, 0x0fff0000, "pop%c\t%m"}, + {ARM_EXT_V1, 0x08900000, 0x0f900000, "ldm%c\t%16-19r%21'!, %m%22'^"}, + {ARM_EXT_V1, 0x08100000, 0x0e100000, "ldm%23?id%24?ba%c\t%16-19r%21'!, %m%22'^"}, + {ARM_EXT_V1, 0x0a000000, 0x0e000000, "b%24'l%c\t%b"}, + {ARM_EXT_V1, 0x0f000000, 0x0f000000, "svc%c\t%0-23x"}, + + /* The rest. */ + {ARM_EXT_V1, 0x00000000, 0x00000000, "undefined instruction %0-31x"}, + {0, 0x00000000, 0x00000000, 0} +}; + +/* print_insn_thumb16 recognizes the following format control codes: + + %S print Thumb register (bits 3..5 as high number if bit 6 set) + %D print Thumb register (bits 0..2 as high number if bit 7 set) + %I print bitfield as a signed decimal + (top bit of range being the sign bit) + %N print Thumb register mask (with LR) + %O print Thumb register mask (with PC) + %M print Thumb register mask + %b print CZB's 6-bit unsigned branch destination + %s print Thumb right-shift immediate (6..10; 0 == 32). + %c print the condition code + %C print the condition code, or "s" if not conditional + %x print warning if conditional an not at end of IT block" + %X print "\t; unpredictable " if conditional + %I print IT instruction suffix and operands + %r print bitfield as an ARM register + %d print bitfield as a decimal + %H print (bitfield * 2) as a decimal + %W print (bitfield * 4) as a decimal + %a print (bitfield * 4) as a pc-rel offset + decoded symbol + %B print Thumb branch destination (signed displacement) + %c print bitfield as a condition code + %'c print specified char iff bit is one + %?ab print a if bit is one else print b. */ + +static const struct opcode16 thumb_opcodes[] = +{ + /* Thumb instructions. */ + + /* ARM V6K no-argument instructions. */ + {ARM_EXT_V6K, 0xbf00, 0xffff, "nop%c"}, + {ARM_EXT_V6K, 0xbf10, 0xffff, "yield%c"}, + {ARM_EXT_V6K, 0xbf20, 0xffff, "wfe%c"}, + {ARM_EXT_V6K, 0xbf30, 0xffff, "wfi%c"}, + {ARM_EXT_V6K, 0xbf40, 0xffff, "sev%c"}, + {ARM_EXT_V6K, 0xbf00, 0xff0f, "nop%c\t{%4-7d}"}, + + /* ARM V6T2 instructions. */ + {ARM_EXT_V6T2, 0xb900, 0xfd00, "cbnz\t%0-2r, %b%X"}, + {ARM_EXT_V6T2, 0xb100, 0xfd00, "cbz\t%0-2r, %b%X"}, + {ARM_EXT_V6T2, 0xbf00, 0xff00, "it%I%X"}, + + /* ARM V6. */ + {ARM_EXT_V6, 0xb660, 0xfff8, "cpsie\t%2'a%1'i%0'f%X"}, + {ARM_EXT_V6, 0xb670, 0xfff8, "cpsid\t%2'a%1'i%0'f%X"}, + {ARM_EXT_V6, 0x4600, 0xffc0, "mov%c\t%0-2r, %3-5r"}, + {ARM_EXT_V6, 0xba00, 0xffc0, "rev%c\t%0-2r, %3-5r"}, + {ARM_EXT_V6, 0xba40, 0xffc0, "rev16%c\t%0-2r, %3-5r"}, + {ARM_EXT_V6, 0xbac0, 0xffc0, "revsh%c\t%0-2r, %3-5r"}, + {ARM_EXT_V6, 0xb650, 0xfff7, "setend\t%3?ble%X"}, + {ARM_EXT_V6, 0xb200, 0xffc0, "sxth%c\t%0-2r, %3-5r"}, + {ARM_EXT_V6, 0xb240, 0xffc0, "sxtb%c\t%0-2r, %3-5r"}, + {ARM_EXT_V6, 0xb280, 0xffc0, "uxth%c\t%0-2r, %3-5r"}, + {ARM_EXT_V6, 0xb2c0, 0xffc0, "uxtb%c\t%0-2r, %3-5r"}, + + /* ARM V5 ISA extends Thumb. */ + {ARM_EXT_V5T, 0xbe00, 0xff00, "bkpt\t%0-7x"}, /* Is always unconditional. */ + /* This is BLX(2). BLX(1) is a 32-bit instruction. */ + {ARM_EXT_V5T, 0x4780, 0xff87, "blx%c\t%3-6r%x"}, /* note: 4 bit register number. */ + /* ARM V4T ISA (Thumb v1). */ + {ARM_EXT_V4T, 0x46C0, 0xFFFF, "nop%c\t\t\t(mov r8, r8)"}, + /* Format 4. */ + {ARM_EXT_V4T, 0x4000, 0xFFC0, "and%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4040, 0xFFC0, "eor%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4080, 0xFFC0, "lsl%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x40C0, 0xFFC0, "lsr%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4100, 0xFFC0, "asr%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4140, 0xFFC0, "adc%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4180, 0xFFC0, "sbc%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x41C0, 0xFFC0, "ror%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4200, 0xFFC0, "tst%c\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4240, 0xFFC0, "neg%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4280, 0xFFC0, "cmp%c\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x42C0, 0xFFC0, "cmn%c\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4300, 0xFFC0, "orr%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4340, 0xFFC0, "mul%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x4380, 0xFFC0, "bic%C\t%0-2r, %3-5r"}, + {ARM_EXT_V4T, 0x43C0, 0xFFC0, "mvn%C\t%0-2r, %3-5r"}, + /* format 13 */ + {ARM_EXT_V4T, 0xB000, 0xFF80, "add%c\tsp, #%0-6W"}, + {ARM_EXT_V4T, 0xB080, 0xFF80, "sub%c\tsp, #%0-6W"}, + /* format 5 */ + {ARM_EXT_V4T, 0x4700, 0xFF80, "bx%c\t%S%x"}, + {ARM_EXT_V4T, 0x4400, 0xFF00, "add%c\t%D, %S"}, + {ARM_EXT_V4T, 0x4500, 0xFF00, "cmp%c\t%D, %S"}, + {ARM_EXT_V4T, 0x4600, 0xFF00, "mov%c\t%D, %S"}, + /* format 14 */ + {ARM_EXT_V4T, 0xB400, 0xFE00, "push%c\t%N"}, + {ARM_EXT_V4T, 0xBC00, 0xFE00, "pop%c\t%O"}, + /* format 2 */ + {ARM_EXT_V4T, 0x1800, 0xFE00, "add%C\t%0-2r, %3-5r, %6-8r"}, + {ARM_EXT_V4T, 0x1A00, 0xFE00, "sub%C\t%0-2r, %3-5r, %6-8r"}, + {ARM_EXT_V4T, 0x1C00, 0xFE00, "add%C\t%0-2r, %3-5r, #%6-8d"}, + {ARM_EXT_V4T, 0x1E00, 0xFE00, "sub%C\t%0-2r, %3-5r, #%6-8d"}, + /* format 8 */ + {ARM_EXT_V4T, 0x5200, 0xFE00, "strh%c\t%0-2r, [%3-5r, %6-8r]"}, + {ARM_EXT_V4T, 0x5A00, 0xFE00, "ldrh%c\t%0-2r, [%3-5r, %6-8r]"}, + {ARM_EXT_V4T, 0x5600, 0xF600, "ldrs%11?hb%c\t%0-2r, [%3-5r, %6-8r]"}, + /* format 7 */ + {ARM_EXT_V4T, 0x5000, 0xFA00, "str%10'b%c\t%0-2r, [%3-5r, %6-8r]"}, + {ARM_EXT_V4T, 0x5800, 0xFA00, "ldr%10'b%c\t%0-2r, [%3-5r, %6-8r]"}, + /* format 1 */ + {ARM_EXT_V4T, 0x0000, 0xF800, "lsl%C\t%0-2r, %3-5r, #%6-10d"}, + {ARM_EXT_V4T, 0x0800, 0xF800, "lsr%C\t%0-2r, %3-5r, %s"}, + {ARM_EXT_V4T, 0x1000, 0xF800, "asr%C\t%0-2r, %3-5r, %s"}, + /* format 3 */ + {ARM_EXT_V4T, 0x2000, 0xF800, "mov%C\t%8-10r, #%0-7d"}, + {ARM_EXT_V4T, 0x2800, 0xF800, "cmp%c\t%8-10r, #%0-7d"}, + {ARM_EXT_V4T, 0x3000, 0xF800, "add%C\t%8-10r, #%0-7d"}, + {ARM_EXT_V4T, 0x3800, 0xF800, "sub%C\t%8-10r, #%0-7d"}, + /* format 6 */ + {ARM_EXT_V4T, 0x4800, 0xF800, "ldr%c\t%8-10r, [pc, #%0-7W]\t(%0-7a)"}, /* TODO: Disassemble PC relative "LDR rD,=" */ + /* format 9 */ + {ARM_EXT_V4T, 0x6000, 0xF800, "str%c\t%0-2r, [%3-5r, #%6-10W]"}, + {ARM_EXT_V4T, 0x6800, 0xF800, "ldr%c\t%0-2r, [%3-5r, #%6-10W]"}, + {ARM_EXT_V4T, 0x7000, 0xF800, "strb%c\t%0-2r, [%3-5r, #%6-10d]"}, + {ARM_EXT_V4T, 0x7800, 0xF800, "ldrb%c\t%0-2r, [%3-5r, #%6-10d]"}, + /* format 10 */ + {ARM_EXT_V4T, 0x8000, 0xF800, "strh%c\t%0-2r, [%3-5r, #%6-10H]"}, + {ARM_EXT_V4T, 0x8800, 0xF800, "ldrh%c\t%0-2r, [%3-5r, #%6-10H]"}, + /* format 11 */ + {ARM_EXT_V4T, 0x9000, 0xF800, "str%c\t%8-10r, [sp, #%0-7W]"}, + {ARM_EXT_V4T, 0x9800, 0xF800, "ldr%c\t%8-10r, [sp, #%0-7W]"}, + /* format 12 */ + {ARM_EXT_V4T, 0xA000, 0xF800, "add%c\t%8-10r, pc, #%0-7W\t(adr %8-10r, %0-7a)"}, + {ARM_EXT_V4T, 0xA800, 0xF800, "add%c\t%8-10r, sp, #%0-7W"}, + /* format 15 */ + {ARM_EXT_V4T, 0xC000, 0xF800, "stmia%c\t%8-10r!, %M"}, + {ARM_EXT_V4T, 0xC800, 0xF800, "ldmia%c\t%8-10r!, %M"}, + /* format 17 */ + {ARM_EXT_V4T, 0xDF00, 0xFF00, "svc%c\t%0-7d"}, + /* format 16 */ + {ARM_EXT_V4T, 0xDE00, 0xFE00, "undefined"}, + {ARM_EXT_V4T, 0xD000, 0xF000, "b%8-11c.n\t%0-7B%X"}, + /* format 18 */ + {ARM_EXT_V4T, 0xE000, 0xF800, "b%c.n\t%0-10B%x"}, + + /* The E800 .. FFFF range is unconditionally redirected to the + 32-bit table, because even in pre-V6T2 ISAs, BL and BLX(1) pairs + are processed via that table. Thus, we can never encounter a + bare "second half of BL/BLX(1)" instruction here. */ + {ARM_EXT_V1, 0x0000, 0x0000, "undefined"}, + {0, 0, 0, 0} +}; + +/* Thumb32 opcodes use the same table structure as the ARM opcodes. + We adopt the convention that hw1 is the high 16 bits of .value and + .mask, hw2 the low 16 bits. + + print_insn_thumb32 recognizes the following format control codes: + + %% % + + %I print a 12-bit immediate from hw1[10],hw2[14:12,7:0] + %M print a modified 12-bit immediate (same location) + %J print a 16-bit immediate from hw1[3:0,10],hw2[14:12,7:0] + %K print a 16-bit immediate from hw2[3:0],hw1[3:0],hw2[11:4] + %S print a possibly-shifted Rm + + %a print the address of a plain load/store + %w print the width and signedness of a core load/store + %m print register mask for ldm/stm + + %E print the lsb and width fields of a bfc/bfi instruction + %F print the lsb and width fields of a sbfx/ubfx instruction + %b print a conditional branch offset + %B print an unconditional branch offset + %s print the shift field of an SSAT instruction + %R print the rotation field of an SXT instruction + %U print barrier type. + %P print address for pli instruction. + %c print the condition code + %x print warning if conditional an not at end of IT block" + %X print "\t; unpredictable " if conditional + + %d print bitfield in decimal + %W print bitfield*4 in decimal + %r print bitfield as an ARM register + %c print bitfield as a condition code + + %'c print specified char iff bitfield is all ones + %`c print specified char iff bitfield is all zeroes + %?ab... select from array of values in big endian order + + With one exception at the bottom (done because BL and BLX(1) need + to come dead last), this table was machine-sorted first in + decreasing order of number of bits set in the mask, then in + increasing numeric order of mask, then in increasing numeric order + of opcode. This order is not the clearest for a human reader, but + is guaranteed never to catch a special-case bit pattern with a more + general mask, which is important, because this instruction encoding + makes heavy use of special-case bit patterns. */ +static const struct opcode32 thumb32_opcodes[] = +{ + /* V7 instructions. */ + {ARM_EXT_V7, 0xf910f000, 0xff70f000, "pli%c\t%a"}, + {ARM_EXT_V7, 0xf3af80f0, 0xfffffff0, "dbg%c\t#%0-3d"}, + {ARM_EXT_V7, 0xf3bf8f50, 0xfffffff0, "dmb%c\t%U"}, + {ARM_EXT_V7, 0xf3bf8f40, 0xfffffff0, "dsb%c\t%U"}, + {ARM_EXT_V7, 0xf3bf8f60, 0xfffffff0, "isb%c\t%U"}, + {ARM_EXT_DIV, 0xfb90f0f0, 0xfff0f0f0, "sdiv%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_DIV, 0xfbb0f0f0, 0xfff0f0f0, "udiv%c\t%8-11r, %16-19r, %0-3r"}, + + /* Instructions defined in the basic V6T2 set. */ + {ARM_EXT_V6T2, 0xf3af8000, 0xffffffff, "nop%c.w"}, + {ARM_EXT_V6T2, 0xf3af8001, 0xffffffff, "yield%c.w"}, + {ARM_EXT_V6T2, 0xf3af8002, 0xffffffff, "wfe%c.w"}, + {ARM_EXT_V6T2, 0xf3af8003, 0xffffffff, "wfi%c.w"}, + {ARM_EXT_V6T2, 0xf3af9004, 0xffffffff, "sev%c.w"}, + {ARM_EXT_V6T2, 0xf3af8000, 0xffffff00, "nop%c.w\t{%0-7d}"}, + + {ARM_EXT_V6T2, 0xf3bf8f2f, 0xffffffff, "clrex%c"}, + {ARM_EXT_V6T2, 0xf3af8400, 0xffffff1f, "cpsie.w\t%7'a%6'i%5'f%X"}, + {ARM_EXT_V6T2, 0xf3af8600, 0xffffff1f, "cpsid.w\t%7'a%6'i%5'f%X"}, + {ARM_EXT_V6T2, 0xf3c08f00, 0xfff0ffff, "bxj%c\t%16-19r%x"}, + {ARM_EXT_V6T2, 0xe810c000, 0xffd0ffff, "rfedb%c\t%16-19r%21'!"}, + {ARM_EXT_V6T2, 0xe990c000, 0xffd0ffff, "rfeia%c\t%16-19r%21'!"}, + {ARM_EXT_V6T2, 0xf3ef8000, 0xffeff000, "mrs%c\t%8-11r, %D"}, + {ARM_EXT_V6T2, 0xf3af8100, 0xffffffe0, "cps\t#%0-4d%X"}, + {ARM_EXT_V6T2, 0xe8d0f000, 0xfff0fff0, "tbb%c\t[%16-19r, %0-3r]%x"}, + {ARM_EXT_V6T2, 0xe8d0f010, 0xfff0fff0, "tbh%c\t[%16-19r, %0-3r, lsl #1]%x"}, + {ARM_EXT_V6T2, 0xf3af8500, 0xffffff00, "cpsie\t%7'a%6'i%5'f, #%0-4d%X"}, + {ARM_EXT_V6T2, 0xf3af8700, 0xffffff00, "cpsid\t%7'a%6'i%5'f, #%0-4d%X"}, + {ARM_EXT_V6T2, 0xf3de8f00, 0xffffff00, "subs%c\tpc, lr, #%0-7d"}, + {ARM_EXT_V6T2, 0xf3808000, 0xffe0f000, "msr%c\t%C, %16-19r"}, + {ARM_EXT_V6T2, 0xe8500f00, 0xfff00fff, "ldrex%c\t%12-15r, [%16-19r]"}, + {ARM_EXT_V6T2, 0xe8d00f4f, 0xfff00fef, "ldrex%4?hb%c\t%12-15r, [%16-19r]"}, + {ARM_EXT_V6T2, 0xe800c000, 0xffd0ffe0, "srsdb%c\t%16-19r%21'!, #%0-4d"}, + {ARM_EXT_V6T2, 0xe980c000, 0xffd0ffe0, "srsia%c\t%16-19r%21'!, #%0-4d"}, + {ARM_EXT_V6T2, 0xfa0ff080, 0xfffff0c0, "sxth%c.w\t%8-11r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa1ff080, 0xfffff0c0, "uxth%c.w\t%8-11r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa2ff080, 0xfffff0c0, "sxtb16%c\t%8-11r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa3ff080, 0xfffff0c0, "uxtb16%c\t%8-11r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa4ff080, 0xfffff0c0, "sxtb%c.w\t%8-11r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa5ff080, 0xfffff0c0, "uxtb%c.w\t%8-11r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xe8400000, 0xfff000ff, "strex%c\t%8-11r, %12-15r, [%16-19r]"}, + {ARM_EXT_V6T2, 0xe8d0007f, 0xfff000ff, "ldrexd%c\t%12-15r, %8-11r, [%16-19r]"}, + {ARM_EXT_V6T2, 0xfa80f000, 0xfff0f0f0, "sadd8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa80f010, 0xfff0f0f0, "qadd8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa80f020, 0xfff0f0f0, "shadd8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa80f040, 0xfff0f0f0, "uadd8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa80f050, 0xfff0f0f0, "uqadd8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa80f060, 0xfff0f0f0, "uhadd8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa80f080, 0xfff0f0f0, "qadd%c\t%8-11r, %0-3r, %16-19r"}, + {ARM_EXT_V6T2, 0xfa80f090, 0xfff0f0f0, "qdadd%c\t%8-11r, %0-3r, %16-19r"}, + {ARM_EXT_V6T2, 0xfa80f0a0, 0xfff0f0f0, "qsub%c\t%8-11r, %0-3r, %16-19r"}, + {ARM_EXT_V6T2, 0xfa80f0b0, 0xfff0f0f0, "qdsub%c\t%8-11r, %0-3r, %16-19r"}, + {ARM_EXT_V6T2, 0xfa90f000, 0xfff0f0f0, "sadd16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa90f010, 0xfff0f0f0, "qadd16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa90f020, 0xfff0f0f0, "shadd16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa90f040, 0xfff0f0f0, "uadd16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa90f050, 0xfff0f0f0, "uqadd16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa90f060, 0xfff0f0f0, "uhadd16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa90f080, 0xfff0f0f0, "rev%c.w\t%8-11r, %16-19r"}, + {ARM_EXT_V6T2, 0xfa90f090, 0xfff0f0f0, "rev16%c.w\t%8-11r, %16-19r"}, + {ARM_EXT_V6T2, 0xfa90f0a0, 0xfff0f0f0, "rbit%c\t%8-11r, %16-19r"}, + {ARM_EXT_V6T2, 0xfa90f0b0, 0xfff0f0f0, "revsh%c.w\t%8-11r, %16-19r"}, + {ARM_EXT_V6T2, 0xfaa0f000, 0xfff0f0f0, "saddsubx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfaa0f010, 0xfff0f0f0, "qaddsubx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfaa0f020, 0xfff0f0f0, "shaddsubx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfaa0f040, 0xfff0f0f0, "uaddsubx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfaa0f050, 0xfff0f0f0, "uqaddsubx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfaa0f060, 0xfff0f0f0, "uhaddsubx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfaa0f080, 0xfff0f0f0, "sel%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfab0f080, 0xfff0f0f0, "clz%c\t%8-11r, %16-19r"}, + {ARM_EXT_V6T2, 0xfac0f000, 0xfff0f0f0, "ssub8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfac0f010, 0xfff0f0f0, "qsub8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfac0f020, 0xfff0f0f0, "shsub8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfac0f040, 0xfff0f0f0, "usub8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfac0f050, 0xfff0f0f0, "uqsub8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfac0f060, 0xfff0f0f0, "uhsub8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfad0f000, 0xfff0f0f0, "ssub16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfad0f010, 0xfff0f0f0, "qsub16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfad0f020, 0xfff0f0f0, "shsub16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfad0f040, 0xfff0f0f0, "usub16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfad0f050, 0xfff0f0f0, "uqsub16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfad0f060, 0xfff0f0f0, "uhsub16%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfae0f000, 0xfff0f0f0, "ssubaddx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfae0f010, 0xfff0f0f0, "qsubaddx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfae0f020, 0xfff0f0f0, "shsubaddx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfae0f040, 0xfff0f0f0, "usubaddx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfae0f050, 0xfff0f0f0, "uqsubaddx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfae0f060, 0xfff0f0f0, "uhsubaddx%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfb00f000, 0xfff0f0f0, "mul%c.w\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfb70f000, 0xfff0f0f0, "usad8%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa00f000, 0xffe0f0f0, "lsl%20's%c.w\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa20f000, 0xffe0f0f0, "lsr%20's%c.w\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa40f000, 0xffe0f0f0, "asr%20's%c.w\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa60f000, 0xffe0f0f0, "ror%20's%c.w\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xe8c00f40, 0xfff00fe0, "strex%4?hb%c\t%0-3r, %12-15r, [%16-19r]"}, + {ARM_EXT_V6T2, 0xf3200000, 0xfff0f0e0, "ssat16%c\t%8-11r, #%0-4d, %16-19r"}, + {ARM_EXT_V6T2, 0xf3a00000, 0xfff0f0e0, "usat16%c\t%8-11r, #%0-4d, %16-19r"}, + {ARM_EXT_V6T2, 0xfb20f000, 0xfff0f0e0, "smuad%4'x%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfb30f000, 0xfff0f0e0, "smulw%4?tb%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfb40f000, 0xfff0f0e0, "smusd%4'x%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfb50f000, 0xfff0f0e0, "smmul%4'r%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfa00f080, 0xfff0f0c0, "sxtah%c\t%8-11r, %16-19r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa10f080, 0xfff0f0c0, "uxtah%c\t%8-11r, %16-19r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa20f080, 0xfff0f0c0, "sxtab16%c\t%8-11r, %16-19r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa30f080, 0xfff0f0c0, "uxtab16%c\t%8-11r, %16-19r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa40f080, 0xfff0f0c0, "sxtab%c\t%8-11r, %16-19r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfa50f080, 0xfff0f0c0, "uxtab%c\t%8-11r, %16-19r, %0-3r%R"}, + {ARM_EXT_V6T2, 0xfb10f000, 0xfff0f0c0, "smul%5?tb%4?tb%c\t%8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xf36f0000, 0xffff8020, "bfc%c\t%8-11r, %E"}, + {ARM_EXT_V6T2, 0xea100f00, 0xfff08f00, "tst%c.w\t%16-19r, %S"}, + {ARM_EXT_V6T2, 0xea900f00, 0xfff08f00, "teq%c\t%16-19r, %S"}, + {ARM_EXT_V6T2, 0xeb100f00, 0xfff08f00, "cmn%c.w\t%16-19r, %S"}, + {ARM_EXT_V6T2, 0xebb00f00, 0xfff08f00, "cmp%c.w\t%16-19r, %S"}, + {ARM_EXT_V6T2, 0xf0100f00, 0xfbf08f00, "tst%c.w\t%16-19r, %M"}, + {ARM_EXT_V6T2, 0xf0900f00, 0xfbf08f00, "teq%c\t%16-19r, %M"}, + {ARM_EXT_V6T2, 0xf1100f00, 0xfbf08f00, "cmn%c.w\t%16-19r, %M"}, + {ARM_EXT_V6T2, 0xf1b00f00, 0xfbf08f00, "cmp%c.w\t%16-19r, %M"}, + {ARM_EXT_V6T2, 0xea4f0000, 0xffef8000, "mov%20's%c.w\t%8-11r, %S"}, + {ARM_EXT_V6T2, 0xea6f0000, 0xffef8000, "mvn%20's%c.w\t%8-11r, %S"}, + {ARM_EXT_V6T2, 0xe8c00070, 0xfff000f0, "strexd%c\t%0-3r, %12-15r, %8-11r, [%16-19r]"}, + {ARM_EXT_V6T2, 0xfb000000, 0xfff000f0, "mla%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfb000010, 0xfff000f0, "mls%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfb700000, 0xfff000f0, "usada8%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfb800000, 0xfff000f0, "smull%c\t%12-15r, %8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfba00000, 0xfff000f0, "umull%c\t%12-15r, %8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfbc00000, 0xfff000f0, "smlal%c\t%12-15r, %8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfbe00000, 0xfff000f0, "umlal%c\t%12-15r, %8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfbe00060, 0xfff000f0, "umaal%c\t%12-15r, %8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xe8500f00, 0xfff00f00, "ldrex%c\t%12-15r, [%16-19r, #%0-7W]"}, + {ARM_EXT_V6T2, 0xf7f08000, 0xfff0f000, "smc%c\t%K"}, + {ARM_EXT_V6T2, 0xf04f0000, 0xfbef8000, "mov%20's%c.w\t%8-11r, %M"}, + {ARM_EXT_V6T2, 0xf06f0000, 0xfbef8000, "mvn%20's%c.w\t%8-11r, %M"}, + {ARM_EXT_V6T2, 0xf810f000, 0xff70f000, "pld%c\t%a"}, + {ARM_EXT_V6T2, 0xfb200000, 0xfff000e0, "smlad%4'x%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfb300000, 0xfff000e0, "smlaw%4?tb%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfb400000, 0xfff000e0, "smlsd%4'x%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfb500000, 0xfff000e0, "smmla%4'r%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfb600000, 0xfff000e0, "smmls%4'r%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfbc000c0, 0xfff000e0, "smlald%4'x%c\t%12-15r, %8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xfbd000c0, 0xfff000e0, "smlsld%4'x%c\t%12-15r, %8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xeac00000, 0xfff08030, "pkhbt%c\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xeac00020, 0xfff08030, "pkhtb%c\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xf3400000, 0xfff08020, "sbfx%c\t%8-11r, %16-19r, %F"}, + {ARM_EXT_V6T2, 0xf3c00000, 0xfff08020, "ubfx%c\t%8-11r, %16-19r, %F"}, + {ARM_EXT_V6T2, 0xf8000e00, 0xff900f00, "str%wt%c\t%12-15r, %a"}, + {ARM_EXT_V6T2, 0xfb100000, 0xfff000c0, "smla%5?tb%4?tb%c\t%8-11r, %16-19r, %0-3r, %12-15r"}, + {ARM_EXT_V6T2, 0xfbc00080, 0xfff000c0, "smlal%5?tb%4?tb%c\t%12-15r, %8-11r, %16-19r, %0-3r"}, + {ARM_EXT_V6T2, 0xf3600000, 0xfff08020, "bfi%c\t%8-11r, %16-19r, %E"}, + {ARM_EXT_V6T2, 0xf8100e00, 0xfe900f00, "ldr%wt%c\t%12-15r, %a"}, + {ARM_EXT_V6T2, 0xf3000000, 0xffd08020, "ssat%c\t%8-11r, #%0-4d, %16-19r%s"}, + {ARM_EXT_V6T2, 0xf3800000, 0xffd08020, "usat%c\t%8-11r, #%0-4d, %16-19r%s"}, + {ARM_EXT_V6T2, 0xf2000000, 0xfbf08000, "addw%c\t%8-11r, %16-19r, %I"}, + {ARM_EXT_V6T2, 0xf2400000, 0xfbf08000, "movw%c\t%8-11r, %J"}, + {ARM_EXT_V6T2, 0xf2a00000, 0xfbf08000, "subw%c\t%8-11r, %16-19r, %I"}, + {ARM_EXT_V6T2, 0xf2c00000, 0xfbf08000, "movt%c\t%8-11r, %J"}, + {ARM_EXT_V6T2, 0xea000000, 0xffe08000, "and%20's%c.w\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xea200000, 0xffe08000, "bic%20's%c.w\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xea400000, 0xffe08000, "orr%20's%c.w\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xea600000, 0xffe08000, "orn%20's%c\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xea800000, 0xffe08000, "eor%20's%c.w\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xeb000000, 0xffe08000, "add%20's%c.w\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xeb400000, 0xffe08000, "adc%20's%c.w\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xeb600000, 0xffe08000, "sbc%20's%c.w\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xeba00000, 0xffe08000, "sub%20's%c.w\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xebc00000, 0xffe08000, "rsb%20's%c\t%8-11r, %16-19r, %S"}, + {ARM_EXT_V6T2, 0xe8400000, 0xfff00000, "strex%c\t%8-11r, %12-15r, [%16-19r, #%0-7W]"}, + {ARM_EXT_V6T2, 0xf0000000, 0xfbe08000, "and%20's%c.w\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf0200000, 0xfbe08000, "bic%20's%c.w\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf0400000, 0xfbe08000, "orr%20's%c.w\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf0600000, 0xfbe08000, "orn%20's%c\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf0800000, 0xfbe08000, "eor%20's%c.w\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf1000000, 0xfbe08000, "add%20's%c.w\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf1400000, 0xfbe08000, "adc%20's%c.w\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf1600000, 0xfbe08000, "sbc%20's%c.w\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf1a00000, 0xfbe08000, "sub%20's%c.w\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xf1c00000, 0xfbe08000, "rsb%20's%c\t%8-11r, %16-19r, %M"}, + {ARM_EXT_V6T2, 0xe8800000, 0xffd00000, "stmia%c.w\t%16-19r%21'!, %m"}, + {ARM_EXT_V6T2, 0xe8900000, 0xffd00000, "ldmia%c.w\t%16-19r%21'!, %m"}, + {ARM_EXT_V6T2, 0xe9000000, 0xffd00000, "stmdb%c\t%16-19r%21'!, %m"}, + {ARM_EXT_V6T2, 0xe9100000, 0xffd00000, "ldmdb%c\t%16-19r%21'!, %m"}, + {ARM_EXT_V6T2, 0xe9c00000, 0xffd000ff, "strd%c\t%12-15r, %8-11r, [%16-19r]"}, + {ARM_EXT_V6T2, 0xe9d00000, 0xffd000ff, "ldrd%c\t%12-15r, %8-11r, [%16-19r]"}, + {ARM_EXT_V6T2, 0xe9400000, 0xff500000, "strd%c\t%12-15r, %8-11r, [%16-19r, #%23`-%0-7W]%21'!"}, + {ARM_EXT_V6T2, 0xe9500000, 0xff500000, "ldrd%c\t%12-15r, %8-11r, [%16-19r, #%23`-%0-7W]%21'!"}, + {ARM_EXT_V6T2, 0xe8600000, 0xff700000, "strd%c\t%12-15r, %8-11r, [%16-19r], #%23`-%0-7W"}, + {ARM_EXT_V6T2, 0xe8700000, 0xff700000, "ldrd%c\t%12-15r, %8-11r, [%16-19r], #%23`-%0-7W"}, + {ARM_EXT_V6T2, 0xf8000000, 0xff100000, "str%w%c.w\t%12-15r, %a"}, + {ARM_EXT_V6T2, 0xf8100000, 0xfe100000, "ldr%w%c.w\t%12-15r, %a"}, + + /* Filter out Bcc with cond=E or F, which are used for other instructions. */ + {ARM_EXT_V6T2, 0xf3c08000, 0xfbc0d000, "undefined (bcc, cond=0xF)"}, + {ARM_EXT_V6T2, 0xf3808000, 0xfbc0d000, "undefined (bcc, cond=0xE)"}, + {ARM_EXT_V6T2, 0xf0008000, 0xf800d000, "b%22-25c.w\t%b%X"}, + {ARM_EXT_V6T2, 0xf0009000, 0xf800d000, "b%c.w\t%B%x"}, + + /* These have been 32-bit since the invention of Thumb. */ + {ARM_EXT_V4T, 0xf000c000, 0xf800d000, "blx%c\t%B%x"}, + {ARM_EXT_V4T, 0xf000d000, 0xf800d000, "bl%c\t%B%x"}, + + /* Fallback. */ + {ARM_EXT_V1, 0x00000000, 0x00000000, "undefined"}, + {0, 0, 0, 0} +}; + +static const char *const arm_conditional[] = +{"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le", "al", "", ""}; + +static const char *const arm_fp_const[] = +{"0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0"}; + +static const char *const arm_shift[] = +{"lsl", "lsr", "asr", "ror"}; + +typedef struct +{ + const char *name; + const char *description; + const char *reg_names[16]; +} +arm_regname; + +static const arm_regname regnames[] = +{ + { "raw" , "Select raw register names", + { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}}, + { "gcc", "Select register names used by GCC", + { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc" }}, + { "std", "Select register names used in ARM's ISA documentation", + { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc" }}, + { "apcs", "Select register names used in the APCS", + { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "sl", "fp", "ip", "sp", "lr", "pc" }}, + { "atpcs", "Select register names used in the ATPCS", + { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "IP", "SP", "LR", "PC" }}, + { "special-atpcs", "Select special register names used in the ATPCS", + { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL", "FP", "IP", "SP", "LR", "PC" }}, +}; + +static const char *const iwmmxt_wwnames[] = +{"b", "h", "w", "d"}; + +static const char *const iwmmxt_wwssnames[] = +{"b", "bus", "bc", "bss", + "h", "hus", "hc", "hss", + "w", "wus", "wc", "wss", + "d", "dus", "dc", "dss" +}; + +static const char *const iwmmxt_regnames[] = +{ "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7", + "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15" +}; + +static const char *const iwmmxt_cregnames[] = +{ "wcid", "wcon", "wcssf", "wcasf", "reserved", "reserved", "reserved", "reserved", + "wcgr0", "wcgr1", "wcgr2", "wcgr3", "reserved", "reserved", "reserved", "reserved" +}; + +/* Default to GCC register name set. */ +static unsigned int regname_selected = 1; + +#define NUM_ARM_REGNAMES NUM_ELEM (regnames) +#define arm_regnames regnames[regname_selected].reg_names + +static bfd_boolean force_thumb = false; + +/* Current IT instruction state. This contains the same state as the IT + bits in the CPSR. */ +static unsigned int ifthen_state; +/* IT state for the next instruction. */ +static unsigned int ifthen_next_state; +/* The address of the insn for which the IT state is valid. */ +static bfd_vma ifthen_address; +#define IFTHEN_COND ((ifthen_state >> 4) & 0xf) + +/* Cached mapping symbol state. */ +enum map_type { + MAP_ARM, + MAP_THUMB, + MAP_DATA +}; + +/* Decode a bitfield of the form matching regexp (N(-N)?,)*N(-N)?. + Returns pointer to following character of the format string and + fills in *VALUEP and *WIDTHP with the extracted value and number of + bits extracted. WIDTHP can be NULL. */ + +static const char * +arm_decode_bitfield (const char *ptr, unsigned long insn, + unsigned long *valuep, int *widthp) +{ + unsigned long value = 0; + int width = 0; + + do + { + int start, end; + int bits; + + for (start = 0; *ptr >= '0' && *ptr <= '9'; ptr++) + start = start * 10 + *ptr - '0'; + if (*ptr == '-') + for (end = 0, ptr++; *ptr >= '0' && *ptr <= '9'; ptr++) + end = end * 10 + *ptr - '0'; + else + end = start; + bits = end - start; + if (bits < 0) + abort (); + value |= ((insn >> start) & ((2ul << bits) - 1)) << width; + width += bits + 1; + } + while (*ptr++ == ','); + *valuep = value; + if (widthp) + *widthp = width; + return ptr - 1; +} + +static void +arm_decode_shift (long given, fprintf_function func, void *stream, + int print_shift) +{ + func (stream, "%s", arm_regnames[given & 0xf]); + + if ((given & 0xff0) != 0) + { + if ((given & 0x10) == 0) + { + int amount = (given & 0xf80) >> 7; + int shift = (given & 0x60) >> 5; + + if (amount == 0) + { + if (shift == 3) + { + func (stream, ", rrx"); + return; + } + + amount = 32; + } + + if (print_shift) + func (stream, ", %s #%d", arm_shift[shift], amount); + else + func (stream, ", #%d", amount); + } + else if (print_shift) + func (stream, ", %s %s", arm_shift[(given & 0x60) >> 5], + arm_regnames[(given & 0xf00) >> 8]); + else + func (stream, ", %s", arm_regnames[(given & 0xf00) >> 8]); + } +} + +/* Print one coprocessor instruction on INFO->STREAM. + Return true if the instruction matched, false if this is not a + recognised coprocessor instruction. */ + +static bfd_boolean +print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given, + bfd_boolean thumb) +{ + const struct opcode32 *insn; + void *stream = info->stream; + fprintf_function func = info->fprintf_func; + unsigned long mask; + unsigned long value; + int cond; + + for (insn = coprocessor_opcodes; insn->assembler; insn++) + { + if (insn->value == FIRST_IWMMXT_INSN + && info->mach != bfd_mach_arm_XScale + && info->mach != bfd_mach_arm_iWMMXt + && info->mach != bfd_mach_arm_iWMMXt2) + insn = insn + IWMMXT_INSN_COUNT; + + mask = insn->mask; + value = insn->value; + if (thumb) + { + /* The high 4 bits are 0xe for Arm conditional instructions, and + 0xe for arm unconditional instructions. The rest of the + encoding is the same. */ + mask |= 0xf0000000; + value |= 0xe0000000; + if (ifthen_state) + cond = IFTHEN_COND; + else + cond = 16; + } + else + { + /* Only match unconditional instuctions against unconditional + patterns. */ + if ((given & 0xf0000000) == 0xf0000000) + { + mask |= 0xf0000000; + cond = 16; + } + else + { + cond = (given >> 28) & 0xf; + if (cond == 0xe) + cond = 16; + } + } + if ((given & mask) == value) + { + const char *c; + + for (c = insn->assembler; *c; c++) + { + if (*c == '%') + { + switch (*++c) + { + case '%': + func (stream, "%%"); + break; + + case 'A': + func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]); + + if ((given & (1 << 24)) != 0) + { + int offset = given & 0xff; + + if (offset) + func (stream, ", #%s%d]%s", + ((given & 0x00800000) == 0 ? "-" : ""), + offset * 4, + ((given & 0x00200000) != 0 ? "!" : "")); + else + func (stream, "]"); + } + else + { + int offset = given & 0xff; + + func (stream, "]"); + + if (given & (1 << 21)) + { + if (offset) + func (stream, ", #%s%d", + ((given & 0x00800000) == 0 ? "-" : ""), + offset * 4); + } + else + func (stream, ", {%d}", offset); + } + break; + + case 'B': + { + int regno = ((given >> 12) & 0xf) | ((given >> (22 - 4)) & 0x10); + int offset = (given >> 1) & 0x3f; + + if (offset == 1) + func (stream, "{d%d}", regno); + else if (regno + offset > 32) + func (stream, "{d%d-}", regno, regno + offset - 1); + else + func (stream, "{d%d-d%d}", regno, regno + offset - 1); + } + break; + + case 'C': + { + int rn = (given >> 16) & 0xf; + int offset = (given & 0xff) * 4; + int add = (given >> 23) & 1; + + func (stream, "[%s", arm_regnames[rn]); + + if (offset) + { + if (!add) + offset = -offset; + func (stream, ", #%d", offset); + } + func (stream, "]"); + if (rn == 15) + { + func (stream, "\t; "); + /* FIXME: Unsure if info->bytes_per_chunk is the + right thing to use here. */ + info->print_address_func (offset + pc + + info->bytes_per_chunk * 2, info); + } + } + break; + + case 'c': + func (stream, "%s", arm_conditional[cond]); + break; + + case 'I': + /* Print a Cirrus/DSP shift immediate. */ + /* Immediates are 7bit signed ints with bits 0..3 in + bits 0..3 of opcode and bits 4..6 in bits 5..7 + of opcode. */ + { + int imm; + + imm = (given & 0xf) | ((given & 0xe0) >> 1); + + /* Is ``imm'' a negative number? */ + if (imm & 0x40) + imm |= (-1 << 7); + + func (stream, "%d", imm); + } + + break; + + case 'F': + switch (given & 0x00408000) + { + case 0: + func (stream, "4"); + break; + case 0x8000: + func (stream, "1"); + break; + case 0x00400000: + func (stream, "2"); + break; + default: + func (stream, "3"); + } + break; + + case 'P': + switch (given & 0x00080080) + { + case 0: + func (stream, "s"); + break; + case 0x80: + func (stream, "d"); + break; + case 0x00080000: + func (stream, "e"); + break; + default: + func (stream, _("")); + break; + } + break; + case 'Q': + switch (given & 0x00408000) + { + case 0: + func (stream, "s"); + break; + case 0x8000: + func (stream, "d"); + break; + case 0x00400000: + func (stream, "e"); + break; + default: + func (stream, "p"); + break; + } + break; + case 'R': + switch (given & 0x60) + { + case 0: + break; + case 0x20: + func (stream, "p"); + break; + case 0x40: + func (stream, "m"); + break; + default: + func (stream, "z"); + break; + } + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int width; + unsigned long value; + + c = arm_decode_bitfield (c, given, &value, &width); + + switch (*c) + { + case 'r': + func (stream, "%s", arm_regnames[value]); + break; + case 'D': + func (stream, "d%ld", value); + break; + case 'Q': + if (value & 1) + func (stream, "", value >> 1); + else + func (stream, "q%ld", value >> 1); + break; + case 'd': + func (stream, "%ld", value); + break; + case 'k': + { + int from = (given & (1 << 7)) ? 32 : 16; + func (stream, "%ld", from - value); + } + break; + + case 'f': + if (value > 7) + func (stream, "#%s", arm_fp_const[value & 7]); + else + func (stream, "f%ld", value); + break; + + case 'w': + if (width == 2) + func (stream, "%s", iwmmxt_wwnames[value]); + else + func (stream, "%s", iwmmxt_wwssnames[value]); + break; + + case 'g': + func (stream, "%s", iwmmxt_regnames[value]); + break; + case 'G': + func (stream, "%s", iwmmxt_cregnames[value]); + break; + + case 'x': + func (stream, "0x%lx", value); + break; + + case '`': + c++; + if (value == 0) + func (stream, "%c", *c); + break; + case '\'': + c++; + if (value == ((1ul << width) - 1)) + func (stream, "%c", *c); + break; + case '?': + func (stream, "%c", c[(1 << width) - (int)value]); + c += 1 << width; + break; + default: + abort (); + } + break; + + case 'y': + case 'z': + { + int single = *c++ == 'y'; + int regno; + + switch (*c) + { + case '4': /* Sm pair */ + func (stream, "{"); + /* Fall through. */ + case '0': /* Sm, Dm */ + regno = given & 0x0000000f; + if (single) + { + regno <<= 1; + regno += (given >> 5) & 1; + } + else + regno += ((given >> 5) & 1) << 4; + break; + + case '1': /* Sd, Dd */ + regno = (given >> 12) & 0x0000000f; + if (single) + { + regno <<= 1; + regno += (given >> 22) & 1; + } + else + regno += ((given >> 22) & 1) << 4; + break; + + case '2': /* Sn, Dn */ + regno = (given >> 16) & 0x0000000f; + if (single) + { + regno <<= 1; + regno += (given >> 7) & 1; + } + else + regno += ((given >> 7) & 1) << 4; + break; + + case '3': /* List */ + func (stream, "{"); + regno = (given >> 12) & 0x0000000f; + if (single) + { + regno <<= 1; + regno += (given >> 22) & 1; + } + else + regno += ((given >> 22) & 1) << 4; + break; + + default: + abort (); + } + + func (stream, "%c%d", single ? 's' : 'd', regno); + + if (*c == '3') + { + int count = given & 0xff; + + if (single == 0) + count >>= 1; + + if (--count) + { + func (stream, "-%c%d", + single ? 's' : 'd', + regno + count); + } + + func (stream, "}"); + } + else if (*c == '4') + func (stream, ", %c%d}", single ? 's' : 'd', + regno + 1); + } + break; + + case 'L': + switch (given & 0x00400100) + { + case 0x00000000: func (stream, "b"); break; + case 0x00400000: func (stream, "h"); break; + case 0x00000100: func (stream, "w"); break; + case 0x00400100: func (stream, "d"); break; + default: + break; + } + break; + + case 'Z': + { + int value; + /* given (20, 23) | given (0, 3) */ + value = ((given >> 16) & 0xf0) | (given & 0xf); + func (stream, "%d", value); + } + break; + + case 'l': + /* This is like the 'A' operator, except that if + the width field "M" is zero, then the offset is + *not* multiplied by four. */ + { + int offset = given & 0xff; + int multiplier = (given & 0x00000100) ? 4 : 1; + + func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]); + + if (offset) + { + if ((given & 0x01000000) != 0) + func (stream, ", #%s%d]%s", + ((given & 0x00800000) == 0 ? "-" : ""), + offset * multiplier, + ((given & 0x00200000) != 0 ? "!" : "")); + else + func (stream, "], #%s%d", + ((given & 0x00800000) == 0 ? "-" : ""), + offset * multiplier); + } + else + func (stream, "]"); + } + break; + + case 'r': + { + int imm4 = (given >> 4) & 0xf; + int puw_bits = ((given >> 22) & 6) | ((given >> 21) & 1); + int ubit = (given >> 23) & 1; + const char *rm = arm_regnames [given & 0xf]; + const char *rn = arm_regnames [(given >> 16) & 0xf]; + + switch (puw_bits) + { + case 1: + /* fall through */ + case 3: + func (stream, "[%s], %c%s", rn, ubit ? '+' : '-', rm); + if (imm4) + func (stream, ", lsl #%d", imm4); + break; + + case 4: + /* fall through */ + case 5: + /* fall through */ + case 6: + /* fall through */ + case 7: + func (stream, "[%s, %c%s", rn, ubit ? '+' : '-', rm); + if (imm4 > 0) + func (stream, ", lsl #%d", imm4); + func (stream, "]"); + if (puw_bits == 5 || puw_bits == 7) + func (stream, "!"); + break; + + default: + func (stream, "INVALID"); + } + } + break; + + case 'i': + { + long imm5; + imm5 = ((given & 0x100) >> 4) | (given & 0xf); + func (stream, "%ld", (imm5 == 0) ? 32 : imm5); + } + break; + + default: + abort (); + } + } + } + else + func (stream, "%c", *c); + } + return true; + } + } + return false; +} + +static void +print_arm_address (bfd_vma pc, struct disassemble_info *info, long given) +{ + void *stream = info->stream; + fprintf_function func = info->fprintf_func; + + if (((given & 0x000f0000) == 0x000f0000) + && ((given & 0x02000000) == 0)) + { + int offset = given & 0xfff; + + func (stream, "[pc"); + + if (given & 0x01000000) + { + if ((given & 0x00800000) == 0) + offset = - offset; + + /* Pre-indexed. */ + func (stream, ", #%d]", offset); + + offset += pc + 8; + + /* Cope with the possibility of write-back + being used. Probably a very dangerous thing + for the programmer to do, but who are we to + argue ? */ + if (given & 0x00200000) + func (stream, "!"); + } + else + { + /* Post indexed. */ + func (stream, "], #%d", offset); + + /* ie ignore the offset. */ + offset = pc + 8; + } + + func (stream, "\t; "); + info->print_address_func (offset, info); + } + else + { + func (stream, "[%s", + arm_regnames[(given >> 16) & 0xf]); + if ((given & 0x01000000) != 0) + { + if ((given & 0x02000000) == 0) + { + int offset = given & 0xfff; + if (offset) + func (stream, ", #%s%d", + (((given & 0x00800000) == 0) + ? "-" : ""), offset); + } + else + { + func (stream, ", %s", + (((given & 0x00800000) == 0) + ? "-" : "")); + arm_decode_shift (given, func, stream, 1); + } + + func (stream, "]%s", + ((given & 0x00200000) != 0) ? "!" : ""); + } + else + { + if ((given & 0x02000000) == 0) + { + int offset = given & 0xfff; + if (offset) + func (stream, "], #%s%d", + (((given & 0x00800000) == 0) + ? "-" : ""), offset); + else + func (stream, "]"); + } + else + { + func (stream, "], %s", + (((given & 0x00800000) == 0) + ? "-" : "")); + arm_decode_shift (given, func, stream, 1); + } + } + } +} + +/* Print one neon instruction on INFO->STREAM. + Return true if the instruction matched, false if this is not a + recognised neon instruction. */ + +static bfd_boolean +print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb) +{ + const struct opcode32 *insn; + void *stream = info->stream; + fprintf_function func = info->fprintf_func; + + if (thumb) + { + if ((given & 0xef000000) == 0xef000000) + { + /* move bit 28 to bit 24 to translate Thumb2 to ARM encoding. */ + unsigned long bit28 = given & (1 << 28); + + given &= 0x00ffffff; + if (bit28) + given |= 0xf3000000; + else + given |= 0xf2000000; + } + else if ((given & 0xff000000) == 0xf9000000) + given ^= 0xf9000000 ^ 0xf4000000; + else + return false; + } + + for (insn = neon_opcodes; insn->assembler; insn++) + { + if ((given & insn->mask) == insn->value) + { + const char *c; + + for (c = insn->assembler; *c; c++) + { + if (*c == '%') + { + switch (*++c) + { + case '%': + func (stream, "%%"); + break; + + case 'c': + if (thumb && ifthen_state) + func (stream, "%s", arm_conditional[IFTHEN_COND]); + break; + + case 'A': + { + static const unsigned char enc[16] = + { + 0x4, 0x14, /* st4 0,1 */ + 0x4, /* st1 2 */ + 0x4, /* st2 3 */ + 0x3, /* st3 4 */ + 0x13, /* st3 5 */ + 0x3, /* st1 6 */ + 0x1, /* st1 7 */ + 0x2, /* st2 8 */ + 0x12, /* st2 9 */ + 0x2, /* st1 10 */ + 0, 0, 0, 0, 0 + }; + int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4); + int rn = ((given >> 16) & 0xf); + int rm = ((given >> 0) & 0xf); + int align = ((given >> 4) & 0x3); + int type = ((given >> 8) & 0xf); + int n = enc[type] & 0xf; + int stride = (enc[type] >> 4) + 1; + int ix; + + func (stream, "{"); + if (stride > 1) + for (ix = 0; ix != n; ix++) + func (stream, "%sd%d", ix ? "," : "", rd + ix * stride); + else if (n == 1) + func (stream, "d%d", rd); + else + func (stream, "d%d-d%d", rd, rd + n - 1); + func (stream, "}, [%s", arm_regnames[rn]); + if (align) + func (stream, ", :%d", 32 << align); + func (stream, "]"); + if (rm == 0xd) + func (stream, "!"); + else if (rm != 0xf) + func (stream, ", %s", arm_regnames[rm]); + } + break; + + case 'B': + { + int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4); + int rn = ((given >> 16) & 0xf); + int rm = ((given >> 0) & 0xf); + int idx_align = ((given >> 4) & 0xf); + int align = 0; + int size = ((given >> 10) & 0x3); + int idx = idx_align >> (size + 1); + int length = ((given >> 8) & 3) + 1; + int stride = 1; + int i; + + if (length > 1 && size > 0) + stride = (idx_align & (1 << size)) ? 2 : 1; + + switch (length) + { + case 1: + { + int amask = (1 << size) - 1; + if ((idx_align & (1 << size)) != 0) + return false; + if (size > 0) + { + if ((idx_align & amask) == amask) + align = 8 << size; + else if ((idx_align & amask) != 0) + return false; + } + } + break; + + case 2: + if (size == 2 && (idx_align & 2) != 0) + return false; + align = (idx_align & 1) ? 16 << size : 0; + break; + + case 3: + if ((size == 2 && (idx_align & 3) != 0) + || (idx_align & 1) != 0) + return false; + break; + + case 4: + if (size == 2) + { + if ((idx_align & 3) == 3) + return false; + align = (idx_align & 3) * 64; + } + else + align = (idx_align & 1) ? 32 << size : 0; + break; + + default: + abort (); + } + + func (stream, "{"); + for (i = 0; i < length; i++) + func (stream, "%sd%d[%d]", (i == 0) ? "" : ",", + rd + i * stride, idx); + func (stream, "}, [%s", arm_regnames[rn]); + if (align) + func (stream, ", :%d", align); + func (stream, "]"); + if (rm == 0xd) + func (stream, "!"); + else if (rm != 0xf) + func (stream, ", %s", arm_regnames[rm]); + } + break; + + case 'C': + { + int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4); + int rn = ((given >> 16) & 0xf); + int rm = ((given >> 0) & 0xf); + int align = ((given >> 4) & 0x1); + int size = ((given >> 6) & 0x3); + int type = ((given >> 8) & 0x3); + int n = type + 1; + int stride = ((given >> 5) & 0x1); + int ix; + + if (stride && (n == 1)) + n++; + else + stride++; + + func (stream, "{"); + if (stride > 1) + for (ix = 0; ix != n; ix++) + func (stream, "%sd%d[]", ix ? "," : "", rd + ix * stride); + else if (n == 1) + func (stream, "d%d[]", rd); + else + func (stream, "d%d[]-d%d[]", rd, rd + n - 1); + func (stream, "}, [%s", arm_regnames[rn]); + if (align) + { + int align = (8 * (type + 1)) << size; + if (type == 3) + align = (size > 1) ? align >> 1 : align; + if (type == 2 || (type == 0 && !size)) + func (stream, ", :", align); + else + func (stream, ", :%d", align); + } + func (stream, "]"); + if (rm == 0xd) + func (stream, "!"); + else if (rm != 0xf) + func (stream, ", %s", arm_regnames[rm]); + } + break; + + case 'D': + { + int raw_reg = (given & 0xf) | ((given >> 1) & 0x10); + int size = (given >> 20) & 3; + int reg = raw_reg & ((4 << size) - 1); + int ix = raw_reg >> size >> 2; + + func (stream, "d%d[%d]", reg, ix); + } + break; + + case 'E': + /* Neon encoded constant for mov, mvn, vorr, vbic */ + { + int bits = 0; + int cmode = (given >> 8) & 0xf; + int op = (given >> 5) & 0x1; + unsigned long value = 0, hival = 0; + unsigned shift; + int size = 0; + int isfloat = 0; + + bits |= ((given >> 24) & 1) << 7; + bits |= ((given >> 16) & 7) << 4; + bits |= ((given >> 0) & 15) << 0; + + if (cmode < 8) + { + shift = (cmode >> 1) & 3; + value = (unsigned long)bits << (8 * shift); + size = 32; + } + else if (cmode < 12) + { + shift = (cmode >> 1) & 1; + value = (unsigned long)bits << (8 * shift); + size = 16; + } + else if (cmode < 14) + { + shift = (cmode & 1) + 1; + value = (unsigned long)bits << (8 * shift); + value |= (1ul << (8 * shift)) - 1; + size = 32; + } + else if (cmode == 14) + { + if (op) + { + /* bit replication into bytes */ + int ix; + unsigned long mask; + + value = 0; + hival = 0; + for (ix = 7; ix >= 0; ix--) + { + mask = ((bits >> ix) & 1) ? 0xff : 0; + if (ix <= 3) + value = (value << 8) | mask; + else + hival = (hival << 8) | mask; + } + size = 64; + } + else + { + /* byte replication */ + value = (unsigned long)bits; + size = 8; + } + } + else if (!op) + { + /* floating point encoding */ + int tmp; + + value = (unsigned long)(bits & 0x7f) << 19; + value |= (unsigned long)(bits & 0x80) << 24; + tmp = bits & 0x40 ? 0x3c : 0x40; + value |= (unsigned long)tmp << 24; + size = 32; + isfloat = 1; + } + else + { + func (stream, "", + bits, cmode, op); + break; + } + switch (size) + { + case 8: + func (stream, "#%ld\t; 0x%.2lx", value, value); + break; + + case 16: + func (stream, "#%ld\t; 0x%.4lx", value, value); + break; + + case 32: + if (isfloat) + { + unsigned char valbytes[4]; + double fvalue; + + /* Do this a byte at a time so we don't have to + worry about the host's endianness. */ + valbytes[0] = value & 0xff; + valbytes[1] = (value >> 8) & 0xff; + valbytes[2] = (value >> 16) & 0xff; + valbytes[3] = (value >> 24) & 0xff; + + floatformat_to_double (valbytes, &fvalue); + + func (stream, "#%.7g\t; 0x%.8lx", fvalue, + value); + } + else + func (stream, "#%ld\t; 0x%.8lx", + (long) ((value & 0x80000000) + ? value | ~0xffffffffl : value), value); + break; + + case 64: + func (stream, "#0x%.8lx%.8lx", hival, value); + break; + + default: + abort (); + } + } + break; + + case 'F': + { + int regno = ((given >> 16) & 0xf) | ((given >> (7 - 4)) & 0x10); + int num = (given >> 8) & 0x3; + + if (!num) + func (stream, "{d%d}", regno); + else if (num + regno >= 32) + func (stream, "{d%d-= '0' && *c <= '9') + limit = *c - '0'; + else if (*c >= 'a' && *c <= 'f') + limit = *c - 'a' + 10; + else + abort (); + low = limit >> 2; + high = limit & 3; + + if (value < low || value > high) + func (stream, "", base << value); + else + func (stream, "%d", base << value); + } + break; + case 'R': + if (given & (1 << 6)) + goto Q; + /* FALLTHROUGH */ + case 'D': + func (stream, "d%ld", value); + break; + case 'Q': + Q: + if (value & 1) + func (stream, "", value >> 1); + else + func (stream, "q%ld", value >> 1); + break; + + case '`': + c++; + if (value == 0) + func (stream, "%c", *c); + break; + case '\'': + c++; + if (value == ((1ul << width) - 1)) + func (stream, "%c", *c); + break; + case '?': + func (stream, "%c", c[(1 << width) - (int)value]); + c += 1 << width; + break; + default: + abort (); + } + break; + + default: + abort (); + } + } + } + else + func (stream, "%c", *c); + } + return true; + } + } + return false; +} + +/* Print one ARM instruction from PC on INFO->STREAM. */ + +static void +print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given) +{ + const struct opcode32 *insn; + void *stream = info->stream; + fprintf_function func = info->fprintf_func; + + if (print_insn_coprocessor (pc, info, given, false)) + return; + + if (print_insn_neon (info, given, false)) + return; + + for (insn = arm_opcodes; insn->assembler; insn++) + { + if (insn->value == FIRST_IWMMXT_INSN + && info->mach != bfd_mach_arm_XScale + && info->mach != bfd_mach_arm_iWMMXt) + insn = insn + IWMMXT_INSN_COUNT; + + if ((given & insn->mask) == insn->value + /* Special case: an instruction with all bits set in the condition field + (0xFnnn_nnnn) is only matched if all those bits are set in insn->mask, + or by the catchall at the end of the table. */ + && ((given & 0xF0000000) != 0xF0000000 + || (insn->mask & 0xF0000000) == 0xF0000000 + || (insn->mask == 0 && insn->value == 0))) + { + const char *c; + + for (c = insn->assembler; *c; c++) + { + if (*c == '%') + { + switch (*++c) + { + case '%': + func (stream, "%%"); + break; + + case 'a': + print_arm_address (pc, info, given); + break; + + case 'P': + /* Set P address bit and use normal address + printing routine. */ + print_arm_address (pc, info, given | (1 << 24)); + break; + + case 's': + if ((given & 0x004f0000) == 0x004f0000) + { + /* PC relative with immediate offset. */ + int offset = ((given & 0xf00) >> 4) | (given & 0xf); + + if ((given & 0x00800000) == 0) + offset = -offset; + + func (stream, "[pc, #%d]\t; ", offset); + info->print_address_func (offset + pc + 8, info); + } + else + { + func (stream, "[%s", + arm_regnames[(given >> 16) & 0xf]); + if ((given & 0x01000000) != 0) + { + /* Pre-indexed. */ + if ((given & 0x00400000) == 0x00400000) + { + /* Immediate. */ + int offset = ((given & 0xf00) >> 4) | (given & 0xf); + if (offset) + func (stream, ", #%s%d", + (((given & 0x00800000) == 0) + ? "-" : ""), offset); + } + else + { + /* Register. */ + func (stream, ", %s%s", + (((given & 0x00800000) == 0) + ? "-" : ""), + arm_regnames[given & 0xf]); + } + + func (stream, "]%s", + ((given & 0x00200000) != 0) ? "!" : ""); + } + else + { + /* Post-indexed. */ + if ((given & 0x00400000) == 0x00400000) + { + /* Immediate. */ + int offset = ((given & 0xf00) >> 4) | (given & 0xf); + if (offset) + func (stream, "], #%s%d", + (((given & 0x00800000) == 0) + ? "-" : ""), offset); + else + func (stream, "]"); + } + else + { + /* Register. */ + func (stream, "], %s%s", + (((given & 0x00800000) == 0) + ? "-" : ""), + arm_regnames[given & 0xf]); + } + } + } + break; + + case 'b': + { + int disp = (((given & 0xffffff) ^ 0x800000) - 0x800000); + info->print_address_func (disp*4 + pc + 8, info); + } + break; + + case 'c': + if (((given >> 28) & 0xf) != 0xe) + func (stream, "%s", + arm_conditional [(given >> 28) & 0xf]); + break; + + case 'm': + { + int started = 0; + int reg; + + func (stream, "{"); + for (reg = 0; reg < 16; reg++) + if ((given & (1 << reg)) != 0) + { + if (started) + func (stream, ", "); + started = 1; + func (stream, "%s", arm_regnames[reg]); + } + func (stream, "}"); + } + break; + + case 'q': + arm_decode_shift (given, func, stream, 0); + break; + + case 'o': + if ((given & 0x02000000) != 0) + { + int rotate = (given & 0xf00) >> 7; + int immed = (given & 0xff); + immed = (((immed << (32 - rotate)) + | (immed >> rotate)) & 0xffffffff); + func (stream, "#%d\t; 0x%x", immed, immed); + } + else + arm_decode_shift (given, func, stream, 1); + break; + + case 'p': + if ((given & 0x0000f000) == 0x0000f000) + func (stream, "p"); + break; + + case 't': + if ((given & 0x01200000) == 0x00200000) + func (stream, "t"); + break; + + case 'A': + func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]); + + if ((given & (1 << 24)) != 0) + { + int offset = given & 0xff; + + if (offset) + func (stream, ", #%s%d]%s", + ((given & 0x00800000) == 0 ? "-" : ""), + offset * 4, + ((given & 0x00200000) != 0 ? "!" : "")); + else + func (stream, "]"); + } + else + { + int offset = given & 0xff; + + func (stream, "]"); + + if (given & (1 << 21)) + { + if (offset) + func (stream, ", #%s%d", + ((given & 0x00800000) == 0 ? "-" : ""), + offset * 4); + } + else + func (stream, ", {%d}", offset); + } + break; + + case 'B': + /* Print ARM V5 BLX(1) address: pc+25 bits. */ + { + bfd_vma address; + bfd_vma offset = 0; + + if (given & 0x00800000) + /* Is signed, hi bits should be ones. */ + offset = (-1) ^ 0x00ffffff; + + /* Offset is (SignExtend(offset field)<<2). */ + offset += given & 0x00ffffff; + offset <<= 2; + address = offset + pc + 8; + + if (given & 0x01000000) + /* H bit allows addressing to 2-byte boundaries. */ + address += 2; + + info->print_address_func (address, info); + } + break; + + case 'C': + func (stream, "_"); + if (given & 0x80000) + func (stream, "f"); + if (given & 0x40000) + func (stream, "s"); + if (given & 0x20000) + func (stream, "x"); + if (given & 0x10000) + func (stream, "c"); + break; + + case 'U': + switch (given & 0xf) + { + case 0xf: func(stream, "sy"); break; + case 0x7: func(stream, "un"); break; + case 0xe: func(stream, "st"); break; + case 0x6: func(stream, "unst"); break; + default: + func(stream, "#%d", (int)given & 0xf); + break; + } + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int width; + unsigned long value; + + c = arm_decode_bitfield (c, given, &value, &width); + + switch (*c) + { + case 'r': + func (stream, "%s", arm_regnames[value]); + break; + case 'd': + func (stream, "%ld", value); + break; + case 'b': + func (stream, "%ld", value * 8); + break; + case 'W': + func (stream, "%ld", value + 1); + break; + case 'x': + func (stream, "0x%08lx", value); + + /* Some SWI instructions have special + meanings. */ + if ((given & 0x0fffffff) == 0x0FF00000) + func (stream, "\t; IMB"); + else if ((given & 0x0fffffff) == 0x0FF00001) + func (stream, "\t; IMBRange"); + break; + case 'X': + func (stream, "%01lx", value & 0xf); + break; + case '`': + c++; + if (value == 0) + func (stream, "%c", *c); + break; + case '\'': + c++; + if (value == ((1ul << width) - 1)) + func (stream, "%c", *c); + break; + case '?': + func (stream, "%c", c[(1 << width) - (int)value]); + c += 1 << width; + break; + default: + abort (); + } + break; + + case 'e': + { + int imm; + + imm = (given & 0xf) | ((given & 0xfff00) >> 4); + func (stream, "%d", imm); + } + break; + + case 'E': + /* LSB and WIDTH fields of BFI or BFC. The machine- + language instruction encodes LSB and MSB. */ + { + long msb = (given & 0x001f0000) >> 16; + long lsb = (given & 0x00000f80) >> 7; + + long width = msb - lsb + 1; + if (width > 0) + func (stream, "#%lu, #%lu", lsb, width); + else + func (stream, "(invalid: %lu:%lu)", lsb, msb); + } + break; + + case 'V': + /* 16-bit unsigned immediate from a MOVT or MOVW + instruction, encoded in bits 0:11 and 15:19. */ + { + long hi = (given & 0x000f0000) >> 4; + long lo = (given & 0x00000fff); + long imm16 = hi | lo; + func (stream, "#%lu\t; 0x%lx", imm16, imm16); + } + break; + + default: + abort (); + } + } + } + else + func (stream, "%c", *c); + } + return; + } + } + abort (); +} + +/* Print one 16-bit Thumb instruction from PC on INFO->STREAM. */ + +static void +print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given) +{ + const struct opcode16 *insn; + void *stream = info->stream; + fprintf_function func = info->fprintf_func; + + for (insn = thumb_opcodes; insn->assembler; insn++) + if ((given & insn->mask) == insn->value) + { + const char *c = insn->assembler; + for (; *c; c++) + { + int domaskpc = 0; + int domasklr = 0; + + if (*c != '%') + { + func (stream, "%c", *c); + continue; + } + + switch (*++c) + { + case '%': + func (stream, "%%"); + break; + + case 'c': + if (ifthen_state) + func (stream, "%s", arm_conditional[IFTHEN_COND]); + break; + + case 'C': + if (ifthen_state) + func (stream, "%s", arm_conditional[IFTHEN_COND]); + else + func (stream, "s"); + break; + + case 'I': + { + unsigned int tmp; + + ifthen_next_state = given & 0xff; + for (tmp = given << 1; tmp & 0xf; tmp <<= 1) + func (stream, ((given ^ tmp) & 0x10) ? "e" : "t"); + func (stream, "\t%s", arm_conditional[(given >> 4) & 0xf]); + } + break; + + case 'x': + if (ifthen_next_state) + func (stream, "\t; unpredictable branch in IT block\n"); + break; + + case 'X': + if (ifthen_state) + func (stream, "\t; unpredictable ", + arm_conditional[IFTHEN_COND]); + break; + + case 'S': + { + long reg; + + reg = (given >> 3) & 0x7; + if (given & (1 << 6)) + reg += 8; + + func (stream, "%s", arm_regnames[reg]); + } + break; + + case 'D': + { + long reg; + + reg = given & 0x7; + if (given & (1 << 7)) + reg += 8; + + func (stream, "%s", arm_regnames[reg]); + } + break; + + case 'N': + if (given & (1 << 8)) + domasklr = 1; + /* Fall through. */ + case 'O': + if (*c == 'O' && (given & (1 << 8))) + domaskpc = 1; + /* Fall through. */ + case 'M': + { + int started = 0; + int reg; + + func (stream, "{"); + + /* It would be nice if we could spot + ranges, and generate the rS-rE format: */ + for (reg = 0; (reg < 8); reg++) + if ((given & (1 << reg)) != 0) + { + if (started) + func (stream, ", "); + started = 1; + func (stream, "%s", arm_regnames[reg]); + } + + if (domasklr) + { + if (started) + func (stream, ", "); + started = 1; + func (stream, "%s", arm_regnames[14] /* "lr" */); + } + + if (domaskpc) + { + if (started) + func (stream, ", "); + func (stream, "%s", arm_regnames[15] /* "pc" */); + } + + func (stream, "}"); + } + break; + + case 'b': + /* Print ARM V6T2 CZB address: pc+4+6 bits. */ + { + bfd_vma address = (pc + 4 + + ((given & 0x00f8) >> 2) + + ((given & 0x0200) >> 3)); + info->print_address_func (address, info); + } + break; + + case 's': + /* Right shift immediate -- bits 6..10; 1-31 print + as themselves, 0 prints as 32. */ + { + long imm = (given & 0x07c0) >> 6; + if (imm == 0) + imm = 32; + func (stream, "#%ld", imm); + } + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int bitstart = *c++ - '0'; + int bitend = 0; + + while (*c >= '0' && *c <= '9') + bitstart = (bitstart * 10) + *c++ - '0'; + + switch (*c) + { + case '-': + { + long reg; + + c++; + while (*c >= '0' && *c <= '9') + bitend = (bitend * 10) + *c++ - '0'; + if (!bitend) + abort (); + reg = given >> bitstart; + reg &= (2 << (bitend - bitstart)) - 1; + switch (*c) + { + case 'r': + func (stream, "%s", arm_regnames[reg]); + break; + + case 'd': + func (stream, "%ld", reg); + break; + + case 'H': + func (stream, "%ld", reg << 1); + break; + + case 'W': + func (stream, "%ld", reg << 2); + break; + + case 'a': + /* PC-relative address -- the bottom two + bits of the address are dropped + before the calculation. */ + info->print_address_func + (((pc + 4) & ~3) + (reg << 2), info); + break; + + case 'x': + func (stream, "0x%04lx", reg); + break; + + case 'B': + reg = ((reg ^ (1 << bitend)) - (1 << bitend)); + info->print_address_func (reg * 2 + pc + 4, info); + break; + + case 'c': + func (stream, "%s", arm_conditional [reg]); + break; + + default: + abort (); + } + } + break; + + case '\'': + c++; + if ((given & (1 << bitstart)) != 0) + func (stream, "%c", *c); + break; + + case '?': + ++c; + if ((given & (1 << bitstart)) != 0) + func (stream, "%c", *c++); + else + func (stream, "%c", *++c); + break; + + default: + abort (); + } + } + break; + + default: + abort (); + } + } + return; + } + + /* No match. */ + abort (); +} + +/* Return the name of an V7M special register. */ +static const char * +psr_name (int regno) +{ + switch (regno) + { + case 0: return "APSR"; + case 1: return "IAPSR"; + case 2: return "EAPSR"; + case 3: return "PSR"; + case 5: return "IPSR"; + case 6: return "EPSR"; + case 7: return "IEPSR"; + case 8: return "MSP"; + case 9: return "PSP"; + case 16: return "PRIMASK"; + case 17: return "BASEPRI"; + case 18: return "BASEPRI_MASK"; + case 19: return "FAULTMASK"; + case 20: return "CONTROL"; + default: return ""; + } +} + +/* Print one 32-bit Thumb instruction from PC on INFO->STREAM. */ + +static void +print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) +{ + const struct opcode32 *insn; + void *stream = info->stream; + fprintf_function func = info->fprintf_func; + + if (print_insn_coprocessor (pc, info, given, true)) + return; + + if (print_insn_neon (info, given, true)) + return; + + for (insn = thumb32_opcodes; insn->assembler; insn++) + if ((given & insn->mask) == insn->value) + { + const char *c = insn->assembler; + for (; *c; c++) + { + if (*c != '%') + { + func (stream, "%c", *c); + continue; + } + + switch (*++c) + { + case '%': + func (stream, "%%"); + break; + + case 'c': + if (ifthen_state) + func (stream, "%s", arm_conditional[IFTHEN_COND]); + break; + + case 'x': + if (ifthen_next_state) + func (stream, "\t; unpredictable branch in IT block\n"); + break; + + case 'X': + if (ifthen_state) + func (stream, "\t; unpredictable ", + arm_conditional[IFTHEN_COND]); + break; + + case 'I': + { + unsigned int imm12 = 0; + imm12 |= (given & 0x000000ffu); + imm12 |= (given & 0x00007000u) >> 4; + imm12 |= (given & 0x04000000u) >> 15; + func (stream, "#%u\t; 0x%x", imm12, imm12); + } + break; + + case 'M': + { + unsigned int bits = 0, imm, imm8, mod; + bits |= (given & 0x000000ffu); + bits |= (given & 0x00007000u) >> 4; + bits |= (given & 0x04000000u) >> 15; + imm8 = (bits & 0x0ff); + mod = (bits & 0xf00) >> 8; + switch (mod) + { + case 0: imm = imm8; break; + case 1: imm = ((imm8<<16) | imm8); break; + case 2: imm = ((imm8<<24) | (imm8 << 8)); break; + case 3: imm = ((imm8<<24) | (imm8 << 16) | (imm8 << 8) | imm8); break; + default: + mod = (bits & 0xf80) >> 7; + imm8 = (bits & 0x07f) | 0x80; + imm = (((imm8 << (32 - mod)) | (imm8 >> mod)) & 0xffffffff); + } + func (stream, "#%u\t; 0x%x", imm, imm); + } + break; + + case 'J': + { + unsigned int imm = 0; + imm |= (given & 0x000000ffu); + imm |= (given & 0x00007000u) >> 4; + imm |= (given & 0x04000000u) >> 15; + imm |= (given & 0x000f0000u) >> 4; + func (stream, "#%u\t; 0x%x", imm, imm); + } + break; + + case 'K': + { + unsigned int imm = 0; + imm |= (given & 0x000f0000u) >> 16; + imm |= (given & 0x00000ff0u) >> 0; + imm |= (given & 0x0000000fu) << 12; + func (stream, "#%u\t; 0x%x", imm, imm); + } + break; + + case 'S': + { + unsigned int reg = (given & 0x0000000fu); + unsigned int stp = (given & 0x00000030u) >> 4; + unsigned int imm = 0; + imm |= (given & 0x000000c0u) >> 6; + imm |= (given & 0x00007000u) >> 10; + + func (stream, "%s", arm_regnames[reg]); + switch (stp) + { + case 0: + if (imm > 0) + func (stream, ", lsl #%u", imm); + break; + + case 1: + if (imm == 0) + imm = 32; + func (stream, ", lsr #%u", imm); + break; + + case 2: + if (imm == 0) + imm = 32; + func (stream, ", asr #%u", imm); + break; + + case 3: + if (imm == 0) + func (stream, ", rrx"); + else + func (stream, ", ror #%u", imm); + } + } + break; + + case 'a': + { + unsigned int Rn = (given & 0x000f0000) >> 16; + unsigned int U = (given & 0x00800000) >> 23; + unsigned int op = (given & 0x00000f00) >> 8; + unsigned int i12 = (given & 0x00000fff); + unsigned int i8 = (given & 0x000000ff); + bfd_boolean writeback = false, postind = false; + int offset = 0; + + func (stream, "[%s", arm_regnames[Rn]); + if (U) /* 12-bit positive immediate offset */ + offset = i12; + else if (Rn == 15) /* 12-bit negative immediate offset */ + offset = -(int)i12; + else if (op == 0x0) /* shifted register offset */ + { + unsigned int Rm = (i8 & 0x0f); + unsigned int sh = (i8 & 0x30) >> 4; + func (stream, ", %s", arm_regnames[Rm]); + if (sh) + func (stream, ", lsl #%u", sh); + func (stream, "]"); + break; + } + else switch (op) + { + case 0xE: /* 8-bit positive immediate offset */ + offset = i8; + break; + + case 0xC: /* 8-bit negative immediate offset */ + offset = -i8; + break; + + case 0xF: /* 8-bit + preindex with wb */ + offset = i8; + writeback = true; + break; + + case 0xD: /* 8-bit - preindex with wb */ + offset = -i8; + writeback = true; + break; + + case 0xB: /* 8-bit + postindex */ + offset = i8; + postind = true; + break; + + case 0x9: /* 8-bit - postindex */ + offset = -i8; + postind = true; + break; + + default: + func (stream, ", ]"); + goto skip; + } + + if (postind) + func (stream, "], #%d", offset); + else + { + if (offset) + func (stream, ", #%d", offset); + func (stream, writeback ? "]!" : "]"); + } + + if (Rn == 15) + { + func (stream, "\t; "); + info->print_address_func (((pc + 4) & ~3) + offset, info); + } + } + skip: + break; + + case 'A': + { + unsigned int P = (given & 0x01000000) >> 24; + unsigned int U = (given & 0x00800000) >> 23; + unsigned int W = (given & 0x00400000) >> 21; + unsigned int Rn = (given & 0x000f0000) >> 16; + unsigned int off = (given & 0x000000ff); + + func (stream, "[%s", arm_regnames[Rn]); + if (P) + { + if (off || !U) + func (stream, ", #%c%u", U ? '+' : '-', off * 4); + func (stream, "]"); + if (W) + func (stream, "!"); + } + else + { + func (stream, "], "); + if (W) + func (stream, "#%c%u", U ? '+' : '-', off * 4); + else + func (stream, "{%u}", off); + } + } + break; + + case 'w': + { + unsigned int Sbit = (given & 0x01000000) >> 24; + unsigned int type = (given & 0x00600000) >> 21; + switch (type) + { + case 0: func (stream, Sbit ? "sb" : "b"); break; + case 1: func (stream, Sbit ? "sh" : "h"); break; + case 2: + if (Sbit) + func (stream, "??"); + break; + case 3: + func (stream, "??"); + break; + } + } + break; + + case 'm': + { + int started = 0; + int reg; + + func (stream, "{"); + for (reg = 0; reg < 16; reg++) + if ((given & (1 << reg)) != 0) + { + if (started) + func (stream, ", "); + started = 1; + func (stream, "%s", arm_regnames[reg]); + } + func (stream, "}"); + } + break; + + case 'E': + { + unsigned int msb = (given & 0x0000001f); + unsigned int lsb = 0; + lsb |= (given & 0x000000c0u) >> 6; + lsb |= (given & 0x00007000u) >> 10; + func (stream, "#%u, #%u", lsb, msb - lsb + 1); + } + break; + + case 'F': + { + unsigned int width = (given & 0x0000001f) + 1; + unsigned int lsb = 0; + lsb |= (given & 0x000000c0u) >> 6; + lsb |= (given & 0x00007000u) >> 10; + func (stream, "#%u, #%u", lsb, width); + } + break; + + case 'b': + { + unsigned int S = (given & 0x04000000u) >> 26; + unsigned int J1 = (given & 0x00002000u) >> 13; + unsigned int J2 = (given & 0x00000800u) >> 11; + int offset = 0; + + offset |= !S << 20; + offset |= J2 << 19; + offset |= J1 << 18; + offset |= (given & 0x003f0000) >> 4; + offset |= (given & 0x000007ff) << 1; + offset -= (1 << 20); + + info->print_address_func (pc + 4 + offset, info); + } + break; + + case 'B': + { + unsigned int S = (given & 0x04000000u) >> 26; + unsigned int I1 = (given & 0x00002000u) >> 13; + unsigned int I2 = (given & 0x00000800u) >> 11; + int offset = 0; + + offset |= !S << 24; + offset |= !(I1 ^ S) << 23; + offset |= !(I2 ^ S) << 22; + offset |= (given & 0x03ff0000u) >> 4; + offset |= (given & 0x000007ffu) << 1; + offset -= (1 << 24); + offset += pc + 4; + + /* BLX target addresses are always word aligned. */ + if ((given & 0x00001000u) == 0) + offset &= ~2u; + + info->print_address_func (offset, info); + } + break; + + case 's': + { + unsigned int shift = 0; + shift |= (given & 0x000000c0u) >> 6; + shift |= (given & 0x00007000u) >> 10; + if (given & 0x00200000u) + func (stream, ", asr #%u", shift); + else if (shift) + func (stream, ", lsl #%u", shift); + /* else print nothing - lsl #0 */ + } + break; + + case 'R': + { + unsigned int rot = (given & 0x00000030) >> 4; + if (rot) + func (stream, ", ror #%u", rot * 8); + } + break; + + case 'U': + switch (given & 0xf) + { + case 0xf: func(stream, "sy"); break; + case 0x7: func(stream, "un"); break; + case 0xe: func(stream, "st"); break; + case 0x6: func(stream, "unst"); break; + default: + func(stream, "#%d", (int)given & 0xf); + break; + } + break; + + case 'C': + if ((given & 0xff) == 0) + { + func (stream, "%cPSR_", (given & 0x100000) ? 'S' : 'C'); + if (given & 0x800) + func (stream, "f"); + if (given & 0x400) + func (stream, "s"); + if (given & 0x200) + func (stream, "x"); + if (given & 0x100) + func (stream, "c"); + } + else + { + func (stream, "%s", psr_name (given & 0xff)); + } + break; + + case 'D': + if ((given & 0xff) == 0) + func (stream, "%cPSR", (given & 0x100000) ? 'S' : 'C'); + else + func (stream, "%s", psr_name (given & 0xff)); + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int width; + unsigned long val; + + c = arm_decode_bitfield (c, given, &val, &width); + + switch (*c) + { + case 'd': func (stream, "%lu", val); break; + case 'W': func (stream, "%lu", val * 4); break; + case 'r': func (stream, "%s", arm_regnames[val]); break; + + case 'c': + func (stream, "%s", arm_conditional[val]); + break; + + case '\'': + c++; + if (val == ((1ul << width) - 1)) + func (stream, "%c", *c); + break; + + case '`': + c++; + if (val == 0) + func (stream, "%c", *c); + break; + + case '?': + func (stream, "%c", c[(1 << width) - (int)val]); + c += 1 << width; + break; + + default: + abort (); + } + } + break; + + default: + abort (); + } + } + return; + } + + /* No match. */ + abort (); +} + +/* Print data bytes on INFO->STREAM. */ + +static void +print_insn_data (bfd_vma pc ATTRIBUTE_UNUSED, struct disassemble_info *info, + long given) +{ + switch (info->bytes_per_chunk) + { + case 1: + info->fprintf_func (info->stream, ".byte\t0x%02lx", given); + break; + case 2: + info->fprintf_func (info->stream, ".short\t0x%04lx", given); + break; + case 4: + info->fprintf_func (info->stream, ".word\t0x%08lx", given); + break; + default: + abort (); + } +} + +/* Search back through the insn stream to determine if this instruction is + conditionally executed. */ +static void +find_ifthen_state (bfd_vma pc, struct disassemble_info *info, + bfd_boolean little) +{ + unsigned char b[2]; + unsigned int insn; + int status; + /* COUNT is twice the number of instructions seen. It will be odd if we + just crossed an instruction boundary. */ + int count; + int it_count; + unsigned int seen_it; + bfd_vma addr; + + ifthen_address = pc; + ifthen_state = 0; + + addr = pc; + count = 1; + it_count = 0; + seen_it = 0; + /* Scan backwards looking for IT instructions, keeping track of where + instruction boundaries are. We don't know if something is actually an + IT instruction until we find a definite instruction boundary. */ + for (;;) + { + if (addr == 0 || info->symbol_at_address_func(addr, info)) + { + /* A symbol must be on an instruction boundary, and will not + be within an IT block. */ + if (seen_it && (count & 1)) + break; + + return; + } + addr -= 2; + status = info->read_memory_func (addr, (bfd_byte *)b, 2, info); + if (status) + return; + + if (little) + insn = (b[0]) | (b[1] << 8); + else + insn = (b[1]) | (b[0] << 8); + if (seen_it) + { + if ((insn & 0xf800) < 0xe800) + { + /* Addr + 2 is an instruction boundary. See if this matches + the expected boundary based on the position of the last + IT candidate. */ + if (count & 1) + break; + seen_it = 0; + } + } + if ((insn & 0xff00) == 0xbf00 && (insn & 0xf) != 0) + { + /* This could be an IT instruction. */ + seen_it = insn; + it_count = count >> 1; + } + if ((insn & 0xf800) >= 0xe800) + count++; + else + count = (count + 2) | 1; + /* IT blocks contain at most 4 instructions. */ + if (count >= 8 && !seen_it) + return; + } + /* We found an IT instruction. */ + ifthen_state = (seen_it & 0xe0) | ((seen_it << it_count) & 0x1f); + if ((ifthen_state & 0xf) == 0) + ifthen_state = 0; +} + +/* NOTE: There are no checks in these routines that + the relevant number of data bytes exist. */ + +int +print_insn_arm (bfd_vma pc, struct disassemble_info *info) +{ + unsigned char b[4]; + long given; + int status; + int is_thumb = false; + int is_data = false; + unsigned int size = 4; + void (*printer) (bfd_vma, struct disassemble_info *, long); + int little; + + little = (info->endian == BFD_ENDIAN_LITTLE); + is_thumb |= (pc & 1); + pc &= ~(bfd_vma)1; + + if (force_thumb) + is_thumb = true; + + info->bytes_per_line = 4; + + if (is_data) + { + int i; + + /* size was already set above. */ + info->bytes_per_chunk = size; + printer = print_insn_data; + + status = info->read_memory_func (pc, (bfd_byte *)b, size, info); + given = 0; + if (little) + for (i = size - 1; i >= 0; i--) + given = b[i] | (given << 8); + else + for (i = 0; i < (int) size; i++) + given = b[i] | (given << 8); + } + else if (!is_thumb) + { + /* In ARM mode endianness is a straightforward issue: the instruction + is four bytes long and is either ordered 0123 or 3210. */ + printer = print_insn_arm_internal; + info->bytes_per_chunk = 4; + size = 4; + + status = info->read_memory_func (pc, (bfd_byte *)b, 4, info); + if (little) + given = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); + else + given = (b[3]) | (b[2] << 8) | (b[1] << 16) | (b[0] << 24); + } + else + { + /* In Thumb mode we have the additional wrinkle of two + instruction lengths. Fortunately, the bits that determine + the length of the current instruction are always to be found + in the first two bytes. */ + printer = print_insn_thumb16; + info->bytes_per_chunk = 2; + size = 2; + + status = info->read_memory_func (pc, (bfd_byte *)b, 2, info); + if (little) + given = (b[0]) | (b[1] << 8); + else + given = (b[1]) | (b[0] << 8); + + if (!status) + { + /* These bit patterns signal a four-byte Thumb + instruction. */ + if ((given & 0xF800) == 0xF800 + || (given & 0xF800) == 0xF000 + || (given & 0xF800) == 0xE800) + { + status = info->read_memory_func (pc + 2, (bfd_byte *)b, 2, info); + if (little) + given = (b[0]) | (b[1] << 8) | (given << 16); + else + given = (b[1]) | (b[0] << 8) | (given << 16); + + printer = print_insn_thumb32; + size = 4; + } + } + + if (ifthen_address != pc) + find_ifthen_state(pc, info, little); + + if (ifthen_state) + { + if ((ifthen_state & 0xf) == 0x8) + ifthen_next_state = 0; + else + ifthen_next_state = (ifthen_state & 0xe0) + | ((ifthen_state & 0xf) << 1); + } + } + + if (status) + { + info->memory_error_func (status, pc, info); + return -1; + } + if (info->flags & INSN_HAS_RELOC) + /* If the instruction has a reloc associated with it, then + the offset field in the instruction will actually be the + addend for the reloc. (We are using REL type relocs). + In such cases, we can ignore the pc when computing + addresses, since the addend is not currently pc-relative. */ + pc = 0; + + /* We include the hexdump of the instruction. The format here + matches that used by objdump and the ARM ARM (in particular, + 32 bit Thumb instructions are displayed as pairs of halfwords, + not as a single word.) */ + if (is_thumb) + { + if (size == 2) + { + info->fprintf_func(info->stream, "%04lx ", + ((unsigned long)given) & 0xffff); + } + else + { + info->fprintf_func(info->stream, "%04lx %04lx ", + (((unsigned long)given) >> 16) & 0xffff, + ((unsigned long)given) & 0xffff); + } + } + else + { + info->fprintf_func(info->stream, "%08lx ", + ((unsigned long)given) & 0xffffffff); + } + + printer (pc, info, given); + + if (is_thumb) + { + ifthen_state = ifthen_next_state; + ifthen_address += size; + } + return size; +} diff --git a/disas/cris.c b/disas/cris.c new file mode 100644 index 0000000..e6cff7a --- /dev/null +++ b/disas/cris.c @@ -0,0 +1,2860 @@ +/* Disassembler code for CRIS. + Copyright 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. + Contributed by Axis Communications AB, Lund, Sweden. + Written by Hans-Peter Nilsson. + + This file is part of the GNU binutils and GDB, the GNU debugger. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include "qemu-common.h" +#include "disas/bfd.h" +//#include "sysdep.h" +#include "target-cris/opcode-cris.h" +//#include "libiberty.h" + +#define CONST_STRNEQ(STR1,STR2) (strncmp ((STR1), (STR2), sizeof (STR2) - 1) == 0) + +/* cris-opc.c -- Table of opcodes for the CRIS processor. + Copyright 2000, 2001, 2004 Free Software Foundation, Inc. + Contributed by Axis Communications AB, Lund, Sweden. + Originally written for GAS 1.38.1 by Mikael Asker. + Reorganized by Hans-Peter Nilsson. + +This file is part of GAS, GDB and the GNU binutils. + +GAS, GDB, and GNU binutils is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2, or (at your +option) any later version. + +GAS, GDB, and GNU binutils are distributed in the hope that they will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . */ + +#ifndef NULL +#define NULL (0) +#endif + +/* This table isn't used for CRISv32 and the size of immediate operands. */ +const struct cris_spec_reg +cris_spec_regs[] = +{ + {"bz", 0, 1, cris_ver_v32p, NULL}, + {"p0", 0, 1, 0, NULL}, + {"vr", 1, 1, 0, NULL}, + {"p1", 1, 1, 0, NULL}, + {"pid", 2, 1, cris_ver_v32p, NULL}, + {"p2", 2, 1, cris_ver_v32p, NULL}, + {"p2", 2, 1, cris_ver_warning, NULL}, + {"srs", 3, 1, cris_ver_v32p, NULL}, + {"p3", 3, 1, cris_ver_v32p, NULL}, + {"p3", 3, 1, cris_ver_warning, NULL}, + {"wz", 4, 2, cris_ver_v32p, NULL}, + {"p4", 4, 2, 0, NULL}, + {"ccr", 5, 2, cris_ver_v0_10, NULL}, + {"exs", 5, 4, cris_ver_v32p, NULL}, + {"p5", 5, 2, cris_ver_v0_10, NULL}, + {"p5", 5, 4, cris_ver_v32p, NULL}, + {"dcr0",6, 2, cris_ver_v0_3, NULL}, + {"eda", 6, 4, cris_ver_v32p, NULL}, + {"p6", 6, 2, cris_ver_v0_3, NULL}, + {"p6", 6, 4, cris_ver_v32p, NULL}, + {"dcr1/mof", 7, 4, cris_ver_v10p, + "Register `dcr1/mof' with ambiguous size specified. Guessing 4 bytes"}, + {"dcr1/mof", 7, 2, cris_ver_v0_3, + "Register `dcr1/mof' with ambiguous size specified. Guessing 2 bytes"}, + {"mof", 7, 4, cris_ver_v10p, NULL}, + {"dcr1",7, 2, cris_ver_v0_3, NULL}, + {"p7", 7, 4, cris_ver_v10p, NULL}, + {"p7", 7, 2, cris_ver_v0_3, NULL}, + {"dz", 8, 4, cris_ver_v32p, NULL}, + {"p8", 8, 4, 0, NULL}, + {"ibr", 9, 4, cris_ver_v0_10, NULL}, + {"ebp", 9, 4, cris_ver_v32p, NULL}, + {"p9", 9, 4, 0, NULL}, + {"irp", 10, 4, cris_ver_v0_10, NULL}, + {"erp", 10, 4, cris_ver_v32p, NULL}, + {"p10", 10, 4, 0, NULL}, + {"srp", 11, 4, 0, NULL}, + {"p11", 11, 4, 0, NULL}, + /* For disassembly use only. Accept at assembly with a warning. */ + {"bar/dtp0", 12, 4, cris_ver_warning, + "Ambiguous register `bar/dtp0' specified"}, + {"nrp", 12, 4, cris_ver_v32p, NULL}, + {"bar", 12, 4, cris_ver_v8_10, NULL}, + {"dtp0",12, 4, cris_ver_v0_3, NULL}, + {"p12", 12, 4, 0, NULL}, + /* For disassembly use only. Accept at assembly with a warning. */ + {"dccr/dtp1",13, 4, cris_ver_warning, + "Ambiguous register `dccr/dtp1' specified"}, + {"ccs", 13, 4, cris_ver_v32p, NULL}, + {"dccr",13, 4, cris_ver_v8_10, NULL}, + {"dtp1",13, 4, cris_ver_v0_3, NULL}, + {"p13", 13, 4, 0, NULL}, + {"brp", 14, 4, cris_ver_v3_10, NULL}, + {"usp", 14, 4, cris_ver_v32p, NULL}, + {"p14", 14, 4, cris_ver_v3p, NULL}, + {"usp", 15, 4, cris_ver_v10, NULL}, + {"spc", 15, 4, cris_ver_v32p, NULL}, + {"p15", 15, 4, cris_ver_v10p, NULL}, + {NULL, 0, 0, cris_ver_version_all, NULL} +}; + +/* Add version specifiers to this table when necessary. + The (now) regular coding of register names suggests a simpler + implementation. */ +const struct cris_support_reg cris_support_regs[] = +{ + {"s0", 0}, + {"s1", 1}, + {"s2", 2}, + {"s3", 3}, + {"s4", 4}, + {"s5", 5}, + {"s6", 6}, + {"s7", 7}, + {"s8", 8}, + {"s9", 9}, + {"s10", 10}, + {"s11", 11}, + {"s12", 12}, + {"s13", 13}, + {"s14", 14}, + {"s15", 15}, + {NULL, 0} +}; + +/* All CRIS opcodes are 16 bits. + + - The match component is a mask saying which bits must match a + particular opcode in order for an instruction to be an instance + of that opcode. + + - The args component is a string containing characters symbolically + matching the operands of an instruction. Used for both assembly + and disassembly. + + Operand-matching characters: + [ ] , space + Verbatim. + A The string "ACR" (case-insensitive). + B Not really an operand. It causes a "BDAP -size,SP" prefix to be + output for the PUSH alias-instructions and recognizes a push- + prefix at disassembly. This letter isn't recognized for v32. + Must be followed by a R or P letter. + ! Non-match pattern, will not match if there's a prefix insn. + b Non-matching operand, used for branches with 16-bit + displacement. Only recognized by the disassembler. + c 5-bit unsigned immediate in bits <4:0>. + C 4-bit unsigned immediate in bits <3:0>. + d At assembly, optionally (as in put other cases before this one) + ".d" or ".D" at the start of the operands, followed by one space + character. At disassembly, nothing. + D General register in bits <15:12> and <3:0>. + f List of flags in bits <15:12> and <3:0>. + i 6-bit signed immediate in bits <5:0>. + I 6-bit unsigned immediate in bits <5:0>. + M Size modifier (B, W or D) for CLEAR instructions. + m Size modifier (B, W or D) in bits <5:4> + N A 32-bit dword, like in the difference between s and y. + This has no effect on bits in the opcode. Can also be expressed + as "[pc+]" in input. + n As N, but PC-relative (to the start of the instruction). + o [-128..127] word offset in bits <7:1> and <0>. Used by 8-bit + branch instructions. + O [-128..127] offset in bits <7:0>. Also matches a comma and a + general register after the expression, in bits <15:12>. Used + only for the BDAP prefix insn (in v32 the ADDOQ insn; same opcode). + P Special register in bits <15:12>. + p Indicates that the insn is a prefix insn. Must be first + character. + Q As O, but don't relax; force an 8-bit offset. + R General register in bits <15:12>. + r General register in bits <3:0>. + S Source operand in bit <10> and a prefix; a 3-operand prefix + without side-effect. + s Source operand in bits <10> and <3:0>, optionally with a + side-effect prefix, except [pc] (the name, not R15 as in ACR) + isn't allowed for v32 and higher. + T Support register in bits <15:12>. + u 4-bit (PC-relative) unsigned immediate word offset in bits <3:0>. + U Relaxes to either u or n, instruction is assumed LAPCQ or LAPC. + Not recognized at disassembly. + x Register-dot-modifier, for example "r5.w" in bits <15:12> and <5:4>. + y Like 's' but do not allow an integer at assembly. + Y The difference s-y; only an integer is allowed. + z Size modifier (B or W) in bit <4>. */ + + +/* Please note the order of the opcodes in this table is significant. + The assembler requires that all instances of the same mnemonic must + be consecutive. If they aren't, the assembler might not recognize + them, or may indicate an internal error. + + The disassembler should not normally care about the order of the + opcodes, but will prefer an earlier alternative if the "match-score" + (see cris-dis.c) is computed as equal. + + It should not be significant for proper execution that this table is + in alphabetical order, but please follow that convention for an easy + overview. */ + +const struct cris_opcode +cris_opcodes[] = +{ + {"abs", 0x06B0, 0x0940, "r,R", 0, SIZE_NONE, 0, + cris_abs_op}, + + {"add", 0x0600, 0x09c0, "m r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + {"add", 0x0A00, 0x01c0, "m s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"add", 0x0A00, 0x01c0, "m S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"add", 0x0a00, 0x05c0, "m S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_add_sub_cmp_and_or_op}, + + {"add", 0x0A00, 0x01c0, "m s,R", 0, SIZE_FIELD, + cris_ver_v32p, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"addc", 0x0570, 0x0A80, "r,R", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_not_implemented_op}, + + {"addc", 0x09A0, 0x0250, "s,R", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_not_implemented_op}, + + {"addi", 0x0540, 0x0A80, "x,r,A", 0, SIZE_NONE, + cris_ver_v32p, + cris_addi_op}, + + {"addi", 0x0500, 0x0Ac0, "x,r", 0, SIZE_NONE, 0, + cris_addi_op}, + + /* This collates after "addo", but we want to disassemble as "addoq", + not "addo". */ + {"addoq", 0x0100, 0x0E00, "Q,A", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"addo", 0x0940, 0x0280, "m s,R,A", 0, SIZE_FIELD_SIGNED, + cris_ver_v32p, + cris_not_implemented_op}, + + /* This must be located after the insn above, lest we misinterpret + "addo.b -1,r0,acr" as "addo .b-1,r0,acr". FIXME: Sounds like a + parser bug. */ + {"addo", 0x0100, 0x0E00, "O,A", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"addq", 0x0200, 0x0Dc0, "I,R", 0, SIZE_NONE, 0, + cris_quick_mode_add_sub_op}, + + {"adds", 0x0420, 0x0Bc0, "z r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */ + {"adds", 0x0820, 0x03c0, "z s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"adds", 0x0820, 0x03c0, "z S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"adds", 0x0820, 0x07c0, "z S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_add_sub_cmp_and_or_op}, + + {"addu", 0x0400, 0x0be0, "z r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ + {"addu", 0x0800, 0x03e0, "z s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"addu", 0x0800, 0x03e0, "z S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"addu", 0x0800, 0x07e0, "z S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_add_sub_cmp_and_or_op}, + + {"and", 0x0700, 0x08C0, "m r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + {"and", 0x0B00, 0x00C0, "m s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"and", 0x0B00, 0x00C0, "m S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"and", 0x0B00, 0x04C0, "m S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_add_sub_cmp_and_or_op}, + + {"andq", 0x0300, 0x0CC0, "i,R", 0, SIZE_NONE, 0, + cris_quick_mode_and_cmp_move_or_op}, + + {"asr", 0x0780, 0x0840, "m r,R", 0, SIZE_NONE, 0, + cris_asr_op}, + + {"asrq", 0x03a0, 0x0c40, "c,R", 0, SIZE_NONE, 0, + cris_asrq_op}, + + {"ax", 0x15B0, 0xEA4F, "", 0, SIZE_NONE, 0, + cris_ax_ei_setf_op}, + + /* FIXME: Should use branch #defines. */ + {"b", 0x0dff, 0x0200, "b", 1, SIZE_NONE, 0, + cris_sixteen_bit_offset_branch_op}, + + {"ba", + BA_QUICK_OPCODE, + 0x0F00+(0xF-CC_A)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + /* Needs to come after the usual "ba o", which might be relaxed to + this one. */ + {"ba", BA_DWORD_OPCODE, + 0xffff & (~BA_DWORD_OPCODE), "n", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_none_reg_mode_jump_op}, + + {"bas", 0x0EBF, 0x0140, "n,P", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_none_reg_mode_jump_op}, + + {"basc", 0x0EFF, 0x0100, "n,P", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_none_reg_mode_jump_op}, + + {"bcc", + BRANCH_QUICK_OPCODE+CC_CC*0x1000, + 0x0f00+(0xF-CC_CC)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bcs", + BRANCH_QUICK_OPCODE+CC_CS*0x1000, + 0x0f00+(0xF-CC_CS)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bdap", + BDAP_INDIR_OPCODE, BDAP_INDIR_Z_BITS, "pm s,R", 0, SIZE_FIELD_SIGNED, + cris_ver_v0_10, + cris_bdap_prefix}, + + {"bdap", + BDAP_QUICK_OPCODE, BDAP_QUICK_Z_BITS, "pO", 0, SIZE_NONE, + cris_ver_v0_10, + cris_quick_mode_bdap_prefix}, + + {"beq", + BRANCH_QUICK_OPCODE+CC_EQ*0x1000, + 0x0f00+(0xF-CC_EQ)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + /* This is deliberately put before "bext" to trump it, even though not + in alphabetical order, since we don't do excluding version checks + for v0..v10. */ + {"bwf", + BRANCH_QUICK_OPCODE+CC_EXT*0x1000, + 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE, + cris_ver_v10, + cris_eight_bit_offset_branch_op}, + + {"bext", + BRANCH_QUICK_OPCODE+CC_EXT*0x1000, + 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE, + cris_ver_v0_3, + cris_eight_bit_offset_branch_op}, + + {"bge", + BRANCH_QUICK_OPCODE+CC_GE*0x1000, + 0x0f00+(0xF-CC_GE)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bgt", + BRANCH_QUICK_OPCODE+CC_GT*0x1000, + 0x0f00+(0xF-CC_GT)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bhi", + BRANCH_QUICK_OPCODE+CC_HI*0x1000, + 0x0f00+(0xF-CC_HI)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bhs", + BRANCH_QUICK_OPCODE+CC_HS*0x1000, + 0x0f00+(0xF-CC_HS)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"biap", BIAP_OPCODE, BIAP_Z_BITS, "pm r,R", 0, SIZE_NONE, + cris_ver_v0_10, + cris_biap_prefix}, + + {"ble", + BRANCH_QUICK_OPCODE+CC_LE*0x1000, + 0x0f00+(0xF-CC_LE)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"blo", + BRANCH_QUICK_OPCODE+CC_LO*0x1000, + 0x0f00+(0xF-CC_LO)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bls", + BRANCH_QUICK_OPCODE+CC_LS*0x1000, + 0x0f00+(0xF-CC_LS)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"blt", + BRANCH_QUICK_OPCODE+CC_LT*0x1000, + 0x0f00+(0xF-CC_LT)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bmi", + BRANCH_QUICK_OPCODE+CC_MI*0x1000, + 0x0f00+(0xF-CC_MI)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bmod", 0x0ab0, 0x0140, "s,R", 0, SIZE_FIX_32, + cris_ver_sim_v0_10, + cris_not_implemented_op}, + + {"bmod", 0x0ab0, 0x0140, "S,D", 0, SIZE_NONE, + cris_ver_sim_v0_10, + cris_not_implemented_op}, + + {"bmod", 0x0ab0, 0x0540, "S,R,r", 0, SIZE_NONE, + cris_ver_sim_v0_10, + cris_not_implemented_op}, + + {"bne", + BRANCH_QUICK_OPCODE+CC_NE*0x1000, + 0x0f00+(0xF-CC_NE)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bound", 0x05c0, 0x0A00, "m r,R", 0, SIZE_NONE, 0, + cris_two_operand_bound_op}, + /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ + {"bound", 0x09c0, 0x0200, "m s,R", 0, SIZE_FIELD, + cris_ver_v0_10, + cris_two_operand_bound_op}, + /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ + {"bound", 0x0dcf, 0x0200, "m Y,R", 0, SIZE_FIELD, 0, + cris_two_operand_bound_op}, + {"bound", 0x09c0, 0x0200, "m S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_two_operand_bound_op}, + {"bound", 0x09c0, 0x0600, "m S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_bound_op}, + + {"bpl", + BRANCH_QUICK_OPCODE+CC_PL*0x1000, + 0x0f00+(0xF-CC_PL)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"break", 0xe930, 0x16c0, "C", 0, SIZE_NONE, + cris_ver_v3p, + cris_break_op}, + + {"bsb", + BRANCH_QUICK_OPCODE+CC_EXT*0x1000, + 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE, + cris_ver_v32p, + cris_eight_bit_offset_branch_op}, + + {"bsr", 0xBEBF, 0x4140, "n", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_none_reg_mode_jump_op}, + + {"bsrc", 0xBEFF, 0x4100, "n", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_none_reg_mode_jump_op}, + + {"bstore", 0x0af0, 0x0100, "s,R", 0, SIZE_FIX_32, + cris_ver_warning, + cris_not_implemented_op}, + + {"bstore", 0x0af0, 0x0100, "S,D", 0, SIZE_NONE, + cris_ver_warning, + cris_not_implemented_op}, + + {"bstore", 0x0af0, 0x0500, "S,R,r", 0, SIZE_NONE, + cris_ver_warning, + cris_not_implemented_op}, + + {"btst", 0x04F0, 0x0B00, "r,R", 0, SIZE_NONE, 0, + cris_btst_nop_op}, + {"btstq", 0x0380, 0x0C60, "c,R", 0, SIZE_NONE, 0, + cris_btst_nop_op}, + + {"bvc", + BRANCH_QUICK_OPCODE+CC_VC*0x1000, + 0x0f00+(0xF-CC_VC)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"bvs", + BRANCH_QUICK_OPCODE+CC_VS*0x1000, + 0x0f00+(0xF-CC_VS)*0x1000, "o", 1, SIZE_NONE, 0, + cris_eight_bit_offset_branch_op}, + + {"clear", 0x0670, 0x3980, "M r", 0, SIZE_NONE, 0, + cris_reg_mode_clear_op}, + + {"clear", 0x0A70, 0x3180, "M y", 0, SIZE_NONE, 0, + cris_none_reg_mode_clear_test_op}, + + {"clear", 0x0A70, 0x3180, "M S", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_clear_test_op}, + + {"clearf", 0x05F0, 0x0A00, "f", 0, SIZE_NONE, 0, + cris_clearf_di_op}, + + {"cmp", 0x06C0, 0x0900, "m r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + {"cmp", 0x0Ac0, 0x0100, "m s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"cmp", 0x0Ac0, 0x0100, "m S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"cmpq", 0x02C0, 0x0D00, "i,R", 0, SIZE_NONE, 0, + cris_quick_mode_and_cmp_move_or_op}, + + /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */ + {"cmps", 0x08e0, 0x0300, "z s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"cmps", 0x08e0, 0x0300, "z S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ + {"cmpu", 0x08c0, 0x0320, "z s,R" , 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"cmpu", 0x08c0, 0x0320, "z S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"di", 0x25F0, 0xDA0F, "", 0, SIZE_NONE, 0, + cris_clearf_di_op}, + + {"dip", DIP_OPCODE, DIP_Z_BITS, "ps", 0, SIZE_FIX_32, + cris_ver_v0_10, + cris_dip_prefix}, + + {"div", 0x0980, 0x0640, "m R,r", 0, SIZE_FIELD, 0, + cris_not_implemented_op}, + + {"dstep", 0x06f0, 0x0900, "r,R", 0, SIZE_NONE, 0, + cris_dstep_logshift_mstep_neg_not_op}, + + {"ei", 0x25B0, 0xDA4F, "", 0, SIZE_NONE, 0, + cris_ax_ei_setf_op}, + + {"fidxd", 0x0ab0, 0xf540, "[r]", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"fidxi", 0x0d30, 0xF2C0, "[r]", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"ftagd", 0x1AB0, 0xE540, "[r]", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"ftagi", 0x1D30, 0xE2C0, "[r]", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"halt", 0xF930, 0x06CF, "", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"jas", 0x09B0, 0x0640, "r,P", 0, SIZE_NONE, + cris_ver_v32p, + cris_reg_mode_jump_op}, + + {"jas", 0x0DBF, 0x0240, "N,P", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_reg_mode_jump_op}, + + {"jasc", 0x0B30, 0x04C0, "r,P", 0, SIZE_NONE, + cris_ver_v32p, + cris_reg_mode_jump_op}, + + {"jasc", 0x0F3F, 0x00C0, "N,P", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_reg_mode_jump_op}, + + {"jbrc", 0x69b0, 0x9640, "r", 0, SIZE_NONE, + cris_ver_v8_10, + cris_reg_mode_jump_op}, + + {"jbrc", 0x6930, 0x92c0, "s", 0, SIZE_FIX_32, + cris_ver_v8_10, + cris_none_reg_mode_jump_op}, + + {"jbrc", 0x6930, 0x92c0, "S", 0, SIZE_NONE, + cris_ver_v8_10, + cris_none_reg_mode_jump_op}, + + {"jir", 0xA9b0, 0x5640, "r", 0, SIZE_NONE, + cris_ver_v8_10, + cris_reg_mode_jump_op}, + + {"jir", 0xA930, 0x52c0, "s", 0, SIZE_FIX_32, + cris_ver_v8_10, + cris_none_reg_mode_jump_op}, + + {"jir", 0xA930, 0x52c0, "S", 0, SIZE_NONE, + cris_ver_v8_10, + cris_none_reg_mode_jump_op}, + + {"jirc", 0x29b0, 0xd640, "r", 0, SIZE_NONE, + cris_ver_v8_10, + cris_reg_mode_jump_op}, + + {"jirc", 0x2930, 0xd2c0, "s", 0, SIZE_FIX_32, + cris_ver_v8_10, + cris_none_reg_mode_jump_op}, + + {"jirc", 0x2930, 0xd2c0, "S", 0, SIZE_NONE, + cris_ver_v8_10, + cris_none_reg_mode_jump_op}, + + {"jsr", 0xB9b0, 0x4640, "r", 0, SIZE_NONE, 0, + cris_reg_mode_jump_op}, + + {"jsr", 0xB930, 0x42c0, "s", 0, SIZE_FIX_32, + cris_ver_v0_10, + cris_none_reg_mode_jump_op}, + + {"jsr", 0xBDBF, 0x4240, "N", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_none_reg_mode_jump_op}, + + {"jsr", 0xB930, 0x42c0, "S", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_jump_op}, + + {"jsrc", 0x39b0, 0xc640, "r", 0, SIZE_NONE, + cris_ver_v8_10, + cris_reg_mode_jump_op}, + + {"jsrc", 0x3930, 0xc2c0, "s", 0, SIZE_FIX_32, + cris_ver_v8_10, + cris_none_reg_mode_jump_op}, + + {"jsrc", 0x3930, 0xc2c0, "S", 0, SIZE_NONE, + cris_ver_v8_10, + cris_none_reg_mode_jump_op}, + + {"jsrc", 0xBB30, 0x44C0, "r", 0, SIZE_NONE, + cris_ver_v32p, + cris_reg_mode_jump_op}, + + {"jsrc", 0xBF3F, 0x40C0, "N", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_reg_mode_jump_op}, + + {"jump", 0x09b0, 0xF640, "r", 0, SIZE_NONE, 0, + cris_reg_mode_jump_op}, + + {"jump", + JUMP_INDIR_OPCODE, JUMP_INDIR_Z_BITS, "s", 0, SIZE_FIX_32, + cris_ver_v0_10, + cris_none_reg_mode_jump_op}, + + {"jump", + JUMP_INDIR_OPCODE, JUMP_INDIR_Z_BITS, "S", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_jump_op}, + + {"jump", 0x09F0, 0x060F, "P", 0, SIZE_NONE, + cris_ver_v32p, + cris_none_reg_mode_jump_op}, + + {"jump", + JUMP_PC_INCR_OPCODE_V32, + (0xffff & ~JUMP_PC_INCR_OPCODE_V32), "N", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_none_reg_mode_jump_op}, + + {"jmpu", 0x8930, 0x72c0, "s", 0, SIZE_FIX_32, + cris_ver_v10, + cris_none_reg_mode_jump_op}, + + {"jmpu", 0x8930, 0x72c0, "S", 0, SIZE_NONE, + cris_ver_v10, + cris_none_reg_mode_jump_op}, + + {"lapc", 0x0970, 0x0680, "U,R", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"lapc", 0x0D7F, 0x0280, "dn,R", 0, SIZE_FIX_32, + cris_ver_v32p, + cris_not_implemented_op}, + + {"lapcq", 0x0970, 0x0680, "u,R", 0, SIZE_NONE, + cris_ver_v32p, + cris_addi_op}, + + {"lsl", 0x04C0, 0x0B00, "m r,R", 0, SIZE_NONE, 0, + cris_dstep_logshift_mstep_neg_not_op}, + + {"lslq", 0x03c0, 0x0C20, "c,R", 0, SIZE_NONE, 0, + cris_dstep_logshift_mstep_neg_not_op}, + + {"lsr", 0x07C0, 0x0800, "m r,R", 0, SIZE_NONE, 0, + cris_dstep_logshift_mstep_neg_not_op}, + + {"lsrq", 0x03e0, 0x0C00, "c,R", 0, SIZE_NONE, 0, + cris_dstep_logshift_mstep_neg_not_op}, + + {"lz", 0x0730, 0x08C0, "r,R", 0, SIZE_NONE, + cris_ver_v3p, + cris_not_implemented_op}, + + {"mcp", 0x07f0, 0x0800, "P,r", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"move", 0x0640, 0x0980, "m r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + {"move", 0x0A40, 0x0180, "m s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"move", 0x0A40, 0x0180, "m S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"move", 0x0630, 0x09c0, "r,P", 0, SIZE_NONE, 0, + cris_move_to_preg_op}, + + {"move", 0x0670, 0x0980, "P,r", 0, SIZE_NONE, 0, + cris_reg_mode_move_from_preg_op}, + + {"move", 0x0BC0, 0x0000, "m R,y", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"move", 0x0BC0, 0x0000, "m D,S", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"move", + MOVE_M_TO_PREG_OPCODE, MOVE_M_TO_PREG_ZBITS, + "s,P", 0, SIZE_SPEC_REG, 0, + cris_move_to_preg_op}, + + {"move", 0x0A30, 0x01c0, "S,P", 0, SIZE_NONE, + cris_ver_v0_10, + cris_move_to_preg_op}, + + {"move", 0x0A70, 0x0180, "P,y", 0, SIZE_SPEC_REG, 0, + cris_none_reg_mode_move_from_preg_op}, + + {"move", 0x0A70, 0x0180, "P,S", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_move_from_preg_op}, + + {"move", 0x0B70, 0x0480, "r,T", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"move", 0x0F70, 0x0080, "T,r", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"movem", 0x0BF0, 0x0000, "R,y", 0, SIZE_FIX_32, 0, + cris_move_reg_to_mem_movem_op}, + + {"movem", 0x0BF0, 0x0000, "D,S", 0, SIZE_NONE, + cris_ver_v0_10, + cris_move_reg_to_mem_movem_op}, + + {"movem", 0x0BB0, 0x0040, "s,R", 0, SIZE_FIX_32, 0, + cris_move_mem_to_reg_movem_op}, + + {"movem", 0x0BB0, 0x0040, "S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_move_mem_to_reg_movem_op}, + + {"moveq", 0x0240, 0x0D80, "i,R", 0, SIZE_NONE, 0, + cris_quick_mode_and_cmp_move_or_op}, + + {"movs", 0x0460, 0x0B80, "z r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */ + {"movs", 0x0860, 0x0380, "z s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"movs", 0x0860, 0x0380, "z S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"movu", 0x0440, 0x0Ba0, "z r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ + {"movu", 0x0840, 0x03a0, "z s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"movu", 0x0840, 0x03a0, "z S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"mstep", 0x07f0, 0x0800, "r,R", 0, SIZE_NONE, + cris_ver_v0_10, + cris_dstep_logshift_mstep_neg_not_op}, + + {"muls", 0x0d00, 0x02c0, "m r,R", 0, SIZE_NONE, + cris_ver_v10p, + cris_muls_op}, + + {"mulu", 0x0900, 0x06c0, "m r,R", 0, SIZE_NONE, + cris_ver_v10p, + cris_mulu_op}, + + {"neg", 0x0580, 0x0A40, "m r,R", 0, SIZE_NONE, 0, + cris_dstep_logshift_mstep_neg_not_op}, + + {"nop", NOP_OPCODE, NOP_Z_BITS, "", 0, SIZE_NONE, + cris_ver_v0_10, + cris_btst_nop_op}, + + {"nop", NOP_OPCODE_V32, NOP_Z_BITS_V32, "", 0, SIZE_NONE, + cris_ver_v32p, + cris_btst_nop_op}, + + {"not", 0x8770, 0x7880, "r", 0, SIZE_NONE, 0, + cris_dstep_logshift_mstep_neg_not_op}, + + {"or", 0x0740, 0x0880, "m r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + {"or", 0x0B40, 0x0080, "m s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"or", 0x0B40, 0x0080, "m S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"or", 0x0B40, 0x0480, "m S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_add_sub_cmp_and_or_op}, + + {"orq", 0x0340, 0x0C80, "i,R", 0, SIZE_NONE, 0, + cris_quick_mode_and_cmp_move_or_op}, + + {"pop", 0x0E6E, 0x0191, "!R", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"pop", 0x0e3e, 0x01c1, "!P", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_move_from_preg_op}, + + {"push", 0x0FEE, 0x0011, "BR", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"push", 0x0E7E, 0x0181, "BP", 0, SIZE_NONE, + cris_ver_v0_10, + cris_move_to_preg_op}, + + {"rbf", 0x3b30, 0xc0c0, "y", 0, SIZE_NONE, + cris_ver_v10, + cris_not_implemented_op}, + + {"rbf", 0x3b30, 0xc0c0, "S", 0, SIZE_NONE, + cris_ver_v10, + cris_not_implemented_op}, + + {"rfe", 0x2930, 0xD6CF, "", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"rfg", 0x4930, 0xB6CF, "", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"rfn", 0x5930, 0xA6CF, "", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + {"ret", 0xB67F, 0x4980, "", 1, SIZE_NONE, + cris_ver_v0_10, + cris_reg_mode_move_from_preg_op}, + + {"ret", 0xB9F0, 0x460F, "", 1, SIZE_NONE, + cris_ver_v32p, + cris_reg_mode_move_from_preg_op}, + + {"retb", 0xe67f, 0x1980, "", 1, SIZE_NONE, + cris_ver_v0_10, + cris_reg_mode_move_from_preg_op}, + + {"rete", 0xA9F0, 0x560F, "", 1, SIZE_NONE, + cris_ver_v32p, + cris_reg_mode_move_from_preg_op}, + + {"reti", 0xA67F, 0x5980, "", 1, SIZE_NONE, + cris_ver_v0_10, + cris_reg_mode_move_from_preg_op}, + + {"retn", 0xC9F0, 0x360F, "", 1, SIZE_NONE, + cris_ver_v32p, + cris_reg_mode_move_from_preg_op}, + + {"sbfs", 0x3b70, 0xc080, "y", 0, SIZE_NONE, + cris_ver_v10, + cris_not_implemented_op}, + + {"sbfs", 0x3b70, 0xc080, "S", 0, SIZE_NONE, + cris_ver_v10, + cris_not_implemented_op}, + + {"sa", + 0x0530+CC_A*0x1000, + 0x0AC0+(0xf-CC_A)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"ssb", + 0x0530+CC_EXT*0x1000, + 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE, + cris_ver_v32p, + cris_scc_op}, + + {"scc", + 0x0530+CC_CC*0x1000, + 0x0AC0+(0xf-CC_CC)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"scs", + 0x0530+CC_CS*0x1000, + 0x0AC0+(0xf-CC_CS)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"seq", + 0x0530+CC_EQ*0x1000, + 0x0AC0+(0xf-CC_EQ)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"setf", 0x05b0, 0x0A40, "f", 0, SIZE_NONE, 0, + cris_ax_ei_setf_op}, + + {"sfe", 0x3930, 0xC6CF, "", 0, SIZE_NONE, + cris_ver_v32p, + cris_not_implemented_op}, + + /* Need to have "swf" in front of "sext" so it is the one displayed in + disassembly. */ + {"swf", + 0x0530+CC_EXT*0x1000, + 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE, + cris_ver_v10, + cris_scc_op}, + + {"sext", + 0x0530+CC_EXT*0x1000, + 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE, + cris_ver_v0_3, + cris_scc_op}, + + {"sge", + 0x0530+CC_GE*0x1000, + 0x0AC0+(0xf-CC_GE)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"sgt", + 0x0530+CC_GT*0x1000, + 0x0AC0+(0xf-CC_GT)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"shi", + 0x0530+CC_HI*0x1000, + 0x0AC0+(0xf-CC_HI)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"shs", + 0x0530+CC_HS*0x1000, + 0x0AC0+(0xf-CC_HS)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"sle", + 0x0530+CC_LE*0x1000, + 0x0AC0+(0xf-CC_LE)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"slo", + 0x0530+CC_LO*0x1000, + 0x0AC0+(0xf-CC_LO)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"sls", + 0x0530+CC_LS*0x1000, + 0x0AC0+(0xf-CC_LS)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"slt", + 0x0530+CC_LT*0x1000, + 0x0AC0+(0xf-CC_LT)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"smi", + 0x0530+CC_MI*0x1000, + 0x0AC0+(0xf-CC_MI)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"sne", + 0x0530+CC_NE*0x1000, + 0x0AC0+(0xf-CC_NE)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"spl", + 0x0530+CC_PL*0x1000, + 0x0AC0+(0xf-CC_PL)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"sub", 0x0680, 0x0940, "m r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + {"sub", 0x0a80, 0x0140, "m s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"sub", 0x0a80, 0x0140, "m S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"sub", 0x0a80, 0x0540, "m S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_add_sub_cmp_and_or_op}, + + {"subq", 0x0280, 0x0d40, "I,R", 0, SIZE_NONE, 0, + cris_quick_mode_add_sub_op}, + + {"subs", 0x04a0, 0x0b40, "z r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */ + {"subs", 0x08a0, 0x0340, "z s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"subs", 0x08a0, 0x0340, "z S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"subs", 0x08a0, 0x0740, "z S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_add_sub_cmp_and_or_op}, + + {"subu", 0x0480, 0x0b60, "z r,R", 0, SIZE_NONE, 0, + cris_reg_mode_add_sub_cmp_and_or_move_op}, + + /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ + {"subu", 0x0880, 0x0360, "z s,R", 0, SIZE_FIELD, 0, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"subu", 0x0880, 0x0360, "z S,D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_add_sub_cmp_and_or_move_op}, + + {"subu", 0x0880, 0x0760, "z S,R,r", 0, SIZE_NONE, + cris_ver_v0_10, + cris_three_operand_add_sub_cmp_and_or_op}, + + {"svc", + 0x0530+CC_VC*0x1000, + 0x0AC0+(0xf-CC_VC)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + {"svs", + 0x0530+CC_VS*0x1000, + 0x0AC0+(0xf-CC_VS)*0x1000, "r", 0, SIZE_NONE, 0, + cris_scc_op}, + + /* The insn "swapn" is the same as "not" and will be disassembled as + such, but the swap* family of mnmonics are generally v8-and-higher + only, so count it in. */ + {"swapn", 0x8770, 0x7880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapw", 0x4770, 0xb880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapnw", 0xc770, 0x3880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapb", 0x2770, 0xd880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapnb", 0xA770, 0x5880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapwb", 0x6770, 0x9880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapnwb", 0xE770, 0x1880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapr", 0x1770, 0xe880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapnr", 0x9770, 0x6880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapwr", 0x5770, 0xa880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapnwr", 0xd770, 0x2880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapbr", 0x3770, 0xc880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapnbr", 0xb770, 0x4880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapwbr", 0x7770, 0x8880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"swapnwbr", 0xf770, 0x0880, "r", 0, SIZE_NONE, + cris_ver_v8p, + cris_not_implemented_op}, + + {"test", 0x0640, 0x0980, "m D", 0, SIZE_NONE, + cris_ver_v0_10, + cris_reg_mode_test_op}, + + {"test", 0x0b80, 0xf040, "m y", 0, SIZE_FIELD, 0, + cris_none_reg_mode_clear_test_op}, + + {"test", 0x0b80, 0xf040, "m S", 0, SIZE_NONE, + cris_ver_v0_10, + cris_none_reg_mode_clear_test_op}, + + {"xor", 0x07B0, 0x0840, "r,R", 0, SIZE_NONE, 0, + cris_xor_op}, + + {NULL, 0, 0, NULL, 0, 0, 0, cris_not_implemented_op} +}; + +/* Condition-names, indexed by the CC_* numbers as found in cris.h. */ +const char * const +cris_cc_strings[] = +{ + "hs", + "lo", + "ne", + "eq", + "vc", + "vs", + "pl", + "mi", + "ls", + "hi", + "ge", + "lt", + "gt", + "le", + "a", + /* This is a placeholder. In v0, this would be "ext". In v32, this + is "sb". */ + "wf" +}; + +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ + + +/* No instruction will be disassembled longer than this. In theory, and + in silicon, address prefixes can be cascaded. In practice, cascading + is not used by GCC, and not supported by the assembler. */ +#ifndef MAX_BYTES_PER_CRIS_INSN +#define MAX_BYTES_PER_CRIS_INSN 8 +#endif + +/* Whether or not to decode prefixes, folding it into the following + instruction. FIXME: Make this optional later. */ +#ifndef PARSE_PREFIX +#define PARSE_PREFIX 1 +#endif + +/* Sometimes we prefix all registers with this character. */ +#define REGISTER_PREFIX_CHAR '$' + +/* Whether or not to trace the following sequence: + sub* X,r%d + bound* Y,r%d + adds.w [pc+r%d.w],pc + + This is the assembly form of a switch-statement in C. + The "sub is optional. If there is none, then X will be zero. + X is the value of the first case, + Y is the number of cases (including default). + + This results in case offsets printed on the form: + case N: -> case_address + where N is an estimation on the corresponding 'case' operand in C, + and case_address is where execution of that case continues after the + sequence presented above. + + The old style of output was to print the offsets as instructions, + which made it hard to follow "case"-constructs in the disassembly, + and caused a lot of annoying warnings about undefined instructions. + + FIXME: Make this optional later. */ +#ifndef TRACE_CASE +#define TRACE_CASE (disdata->trace_case) +#endif + +enum cris_disass_family + { cris_dis_v0_v10, cris_dis_common_v10_v32, cris_dis_v32 }; + +/* Stored in the disasm_info->private_data member. */ +struct cris_disasm_data +{ + /* Whether to print something less confusing if we find something + matching a switch-construct. */ + bfd_boolean trace_case; + + /* Whether this code is flagged as crisv32. FIXME: Should be an enum + that includes "compatible". */ + enum cris_disass_family distype; +}; + +/* Value of first element in switch. */ +static long case_offset = 0; + +/* How many more case-offsets to print. */ +static long case_offset_counter = 0; + +/* Number of case offsets. */ +static long no_of_case_offsets = 0; + +/* Candidate for next case_offset. */ +static long last_immediate = 0; + +static int cris_constraint + (const char *, unsigned, unsigned, struct cris_disasm_data *); + +/* Parse disassembler options and store state in info. FIXME: For the + time being, we abuse static variables. */ + +static bfd_boolean +cris_parse_disassembler_options (disassemble_info *info, + enum cris_disass_family distype) +{ + struct cris_disasm_data *disdata; + + info->private_data = calloc (1, sizeof (struct cris_disasm_data)); + disdata = (struct cris_disasm_data *) info->private_data; + if (disdata == NULL) + return false; + + /* Default true. */ + disdata->trace_case + = (info->disassembler_options == NULL + || (strcmp (info->disassembler_options, "nocase") != 0)); + + disdata->distype = distype; + return true; +} + +static const struct cris_spec_reg * +spec_reg_info (unsigned int sreg, enum cris_disass_family distype) +{ + int i; + + for (i = 0; cris_spec_regs[i].name != NULL; i++) + { + if (cris_spec_regs[i].number == sreg) + { + if (distype == cris_dis_v32) + switch (cris_spec_regs[i].applicable_version) + { + case cris_ver_warning: + case cris_ver_version_all: + case cris_ver_v3p: + case cris_ver_v8p: + case cris_ver_v10p: + case cris_ver_v32p: + /* No ambiguous sizes or register names with CRISv32. */ + if (cris_spec_regs[i].warning == NULL) + return &cris_spec_regs[i]; + default: + ; + } + else if (cris_spec_regs[i].applicable_version != cris_ver_v32p) + return &cris_spec_regs[i]; + } + } + + return NULL; +} + +/* Return the number of bits in the argument. */ + +static int +number_of_bits (unsigned int val) +{ + int bits; + + for (bits = 0; val != 0; val &= val - 1) + bits++; + + return bits; +} + +/* Get an entry in the opcode-table. */ + +static const struct cris_opcode * +get_opcode_entry (unsigned int insn, + unsigned int prefix_insn, + struct cris_disasm_data *disdata) +{ + /* For non-prefixed insns, we keep a table of pointers, indexed by the + insn code. Each entry is initialized when found to be NULL. */ + static const struct cris_opcode **opc_table = NULL; + + const struct cris_opcode *max_matchedp = NULL; + const struct cris_opcode **prefix_opc_table = NULL; + + /* We hold a table for each prefix that need to be handled differently. */ + static const struct cris_opcode **dip_prefixes = NULL; + static const struct cris_opcode **bdapq_m1_prefixes = NULL; + static const struct cris_opcode **bdapq_m2_prefixes = NULL; + static const struct cris_opcode **bdapq_m4_prefixes = NULL; + static const struct cris_opcode **rest_prefixes = NULL; + + /* Allocate and clear the opcode-table. */ + if (opc_table == NULL) + { + opc_table = g_new0(const struct cris_opcode *, 65536); + dip_prefixes = g_new0(const struct cris_opcode *, 65536); + bdapq_m1_prefixes = g_new0(const struct cris_opcode *, 65536); + bdapq_m2_prefixes = g_new0(const struct cris_opcode *, 65536); + bdapq_m4_prefixes = g_new0(const struct cris_opcode *, 65536); + rest_prefixes = g_new0(const struct cris_opcode *, 65536); + } + + /* Get the right table if this is a prefix. + This code is connected to cris_constraints in that it knows what + prefixes play a role in recognition of patterns; the necessary + state is reflected by which table is used. If constraints + involving match or non-match of prefix insns are changed, then this + probably needs changing too. */ + if (prefix_insn != NO_CRIS_PREFIX) + { + const struct cris_opcode *popcodep + = (opc_table[prefix_insn] != NULL + ? opc_table[prefix_insn] + : get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata)); + + if (popcodep == NULL) + return NULL; + + if (popcodep->match == BDAP_QUICK_OPCODE) + { + /* Since some offsets are recognized with "push" macros, we + have to have different tables for them. */ + int offset = (prefix_insn & 255); + + if (offset > 127) + offset -= 256; + + switch (offset) + { + case -4: + prefix_opc_table = bdapq_m4_prefixes; + break; + + case -2: + prefix_opc_table = bdapq_m2_prefixes; + break; + + case -1: + prefix_opc_table = bdapq_m1_prefixes; + break; + + default: + prefix_opc_table = rest_prefixes; + break; + } + } + else if (popcodep->match == DIP_OPCODE) + /* We don't allow postincrement when the prefix is DIP, so use a + different table for DIP. */ + prefix_opc_table = dip_prefixes; + else + prefix_opc_table = rest_prefixes; + } + + if (prefix_insn != NO_CRIS_PREFIX + && prefix_opc_table[insn] != NULL) + max_matchedp = prefix_opc_table[insn]; + else if (prefix_insn == NO_CRIS_PREFIX && opc_table[insn] != NULL) + max_matchedp = opc_table[insn]; + else + { + const struct cris_opcode *opcodep; + int max_level_of_match = -1; + + for (opcodep = cris_opcodes; + opcodep->name != NULL; + opcodep++) + { + int level_of_match; + + if (disdata->distype == cris_dis_v32) + { + switch (opcodep->applicable_version) + { + case cris_ver_version_all: + break; + + case cris_ver_v0_3: + case cris_ver_v0_10: + case cris_ver_v3_10: + case cris_ver_sim_v0_10: + case cris_ver_v8_10: + case cris_ver_v10: + case cris_ver_warning: + continue; + + case cris_ver_v3p: + case cris_ver_v8p: + case cris_ver_v10p: + case cris_ver_v32p: + break; + + case cris_ver_v8: + abort (); + default: + abort (); + } + } + else + { + switch (opcodep->applicable_version) + { + case cris_ver_version_all: + case cris_ver_v0_3: + case cris_ver_v3p: + case cris_ver_v0_10: + case cris_ver_v8p: + case cris_ver_v8_10: + case cris_ver_v10: + case cris_ver_sim_v0_10: + case cris_ver_v10p: + case cris_ver_warning: + break; + + case cris_ver_v32p: + continue; + + case cris_ver_v8: + abort (); + default: + abort (); + } + } + + /* We give a double lead for bits matching the template in + cris_opcodes. Not even, because then "move p8,r10" would + be given 2 bits lead over "clear.d r10". When there's a + tie, the first entry in the table wins. This is + deliberate, to avoid a more complicated recognition + formula. */ + if ((opcodep->match & insn) == opcodep->match + && (opcodep->lose & insn) == 0 + && ((level_of_match + = cris_constraint (opcodep->args, + insn, + prefix_insn, + disdata)) + >= 0) + && ((level_of_match + += 2 * number_of_bits (opcodep->match + | opcodep->lose)) + > max_level_of_match)) + { + max_matchedp = opcodep; + max_level_of_match = level_of_match; + + /* If there was a full match, never mind looking + further. */ + if (level_of_match >= 2 * 16) + break; + } + } + /* Fill in the new entry. + + If there are changes to the opcode-table involving prefixes, and + disassembly then does not work correctly, try removing the + else-clause below that fills in the prefix-table. If that + helps, you need to change the prefix_opc_table setting above, or + something related. */ + if (prefix_insn == NO_CRIS_PREFIX) + opc_table[insn] = max_matchedp; + else + prefix_opc_table[insn] = max_matchedp; + } + + return max_matchedp; +} + +/* Return -1 if the constraints of a bitwise-matched instruction say + that there is no match. Otherwise return a nonnegative number + indicating the confidence in the match (higher is better). */ + +static int +cris_constraint (const char *cs, + unsigned int insn, + unsigned int prefix_insn, + struct cris_disasm_data *disdata) +{ + int retval = 0; + int tmp; + int prefix_ok = 0; + const char *s; + + for (s = cs; *s; s++) + switch (*s) + { + case '!': + /* Do not recognize "pop" if there's a prefix and then only for + v0..v10. */ + if (prefix_insn != NO_CRIS_PREFIX + || disdata->distype != cris_dis_v0_v10) + return -1; + break; + + case 'U': + /* Not recognized at disassembly. */ + return -1; + + case 'M': + /* Size modifier for "clear", i.e. special register 0, 4 or 8. + Check that it is one of them. Only special register 12 could + be mismatched, but checking for matches is more logical than + checking for mismatches when there are only a few cases. */ + tmp = ((insn >> 12) & 0xf); + if (tmp != 0 && tmp != 4 && tmp != 8) + return -1; + break; + + case 'm': + if ((insn & 0x30) == 0x30) + return -1; + break; + + case 'S': + /* A prefix operand without side-effect. */ + if (prefix_insn != NO_CRIS_PREFIX && (insn & 0x400) == 0) + { + prefix_ok = 1; + break; + } + else + return -1; + + case 's': + case 'y': + case 'Y': + /* If this is a prefixed insn with postincrement (side-effect), + the prefix must not be DIP. */ + if (prefix_insn != NO_CRIS_PREFIX) + { + if (insn & 0x400) + { + const struct cris_opcode *prefix_opcodep + = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata); + + if (prefix_opcodep->match == DIP_OPCODE) + return -1; + } + + prefix_ok = 1; + } + break; + + case 'B': + /* If we don't fall through, then the prefix is ok. */ + prefix_ok = 1; + + /* A "push" prefix. Check for valid "push" size. + In case of special register, it may be != 4. */ + if (prefix_insn != NO_CRIS_PREFIX) + { + /* Match the prefix insn to BDAPQ. */ + const struct cris_opcode *prefix_opcodep + = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata); + + if (prefix_opcodep->match == BDAP_QUICK_OPCODE) + { + int pushsize = (prefix_insn & 255); + + if (pushsize > 127) + pushsize -= 256; + + if (s[1] == 'P') + { + unsigned int spec_reg = (insn >> 12) & 15; + const struct cris_spec_reg *sregp + = spec_reg_info (spec_reg, disdata->distype); + + /* For a special-register, the "prefix size" must + match the size of the register. */ + if (sregp && sregp->reg_size == (unsigned int) -pushsize) + break; + } + else if (s[1] == 'R') + { + if ((insn & 0x30) == 0x20 && pushsize == -4) + break; + } + /* FIXME: Should abort here; next constraint letter + *must* be 'P' or 'R'. */ + } + } + return -1; + + case 'D': + retval = (((insn >> 12) & 15) == (insn & 15)); + if (!retval) + return -1; + else + retval += 4; + break; + + case 'P': + { + const struct cris_spec_reg *sregp + = spec_reg_info ((insn >> 12) & 15, disdata->distype); + + /* Since we match four bits, we will give a value of 4-1 = 3 + in a match. If there is a corresponding exact match of a + special register in another pattern, it will get a value of + 4, which will be higher. This should be correct in that an + exact pattern would match better than a general pattern. + + Note that there is a reason for not returning zero; the + pattern for "clear" is partly matched in the bit-pattern + (the two lower bits must be zero), while the bit-pattern + for a move from a special register is matched in the + register constraint. */ + + if (sregp != NULL) + { + retval += 3; + break; + } + else + return -1; + } + } + + if (prefix_insn != NO_CRIS_PREFIX && ! prefix_ok) + return -1; + + return retval; +} + +/* Format number as hex with a leading "0x" into outbuffer. */ + +static char * +format_hex (unsigned long number, + char *outbuffer, + struct cris_disasm_data *disdata) +{ + /* Truncate negative numbers on >32-bit hosts. */ + number &= 0xffffffff; + + sprintf (outbuffer, "0x%lx", number); + + /* Save this value for the "case" support. */ + if (TRACE_CASE) + last_immediate = number; + + return outbuffer + strlen (outbuffer); +} + +/* Format number as decimal into outbuffer. Parameter signedp says + whether the number should be formatted as signed (!= 0) or + unsigned (== 0). */ + +static char * +format_dec (long number, char *outbuffer, int signedp) +{ + last_immediate = number; + sprintf (outbuffer, signedp ? "%ld" : "%lu", number); + + return outbuffer + strlen (outbuffer); +} + +/* Format the name of the general register regno into outbuffer. */ + +static char * +format_reg (struct cris_disasm_data *disdata, + int regno, + char *outbuffer_start, + bfd_boolean with_reg_prefix) +{ + char *outbuffer = outbuffer_start; + + if (with_reg_prefix) + *outbuffer++ = REGISTER_PREFIX_CHAR; + + switch (regno) + { + case 15: + /* For v32, there is no context in which we output PC. */ + if (disdata->distype == cris_dis_v32) + strcpy (outbuffer, "acr"); + else + strcpy (outbuffer, "pc"); + break; + + case 14: + strcpy (outbuffer, "sp"); + break; + + default: + sprintf (outbuffer, "r%d", regno); + break; + } + + return outbuffer_start + strlen (outbuffer_start); +} + +/* Format the name of a support register into outbuffer. */ + +static char * +format_sup_reg (unsigned int regno, + char *outbuffer_start, + bfd_boolean with_reg_prefix) +{ + char *outbuffer = outbuffer_start; + int i; + + if (with_reg_prefix) + *outbuffer++ = REGISTER_PREFIX_CHAR; + + for (i = 0; cris_support_regs[i].name != NULL; i++) + if (cris_support_regs[i].number == regno) + { + sprintf (outbuffer, "%s", cris_support_regs[i].name); + return outbuffer_start + strlen (outbuffer_start); + } + + /* There's supposed to be register names covering all numbers, though + some may be generic names. */ + sprintf (outbuffer, "format_sup_reg-BUG"); + return outbuffer_start + strlen (outbuffer_start); +} + +/* Return the length of an instruction. */ + +static unsigned +bytes_to_skip (unsigned int insn, + const struct cris_opcode *matchedp, + enum cris_disass_family distype, + const struct cris_opcode *prefix_matchedp) +{ + /* Each insn is a word plus "immediate" operands. */ + unsigned to_skip = 2; + const char *template = matchedp->args; + const char *s; + + for (s = template; *s; s++) + if ((*s == 's' || *s == 'N' || *s == 'Y') + && (insn & 0x400) && (insn & 15) == 15 + && prefix_matchedp == NULL) + { + /* Immediate via [pc+], so we have to check the size of the + operand. */ + int mode_size = 1 << ((insn >> 4) & (*template == 'z' ? 1 : 3)); + + if (matchedp->imm_oprnd_size == SIZE_FIX_32) + to_skip += 4; + else if (matchedp->imm_oprnd_size == SIZE_SPEC_REG) + { + const struct cris_spec_reg *sregp + = spec_reg_info ((insn >> 12) & 15, distype); + + /* FIXME: Improve error handling; should have been caught + earlier. */ + if (sregp == NULL) + return 2; + + /* PC is incremented by two, not one, for a byte. Except on + CRISv32, where constants are always DWORD-size for + special registers. */ + to_skip += + distype == cris_dis_v32 ? 4 : (sregp->reg_size + 1) & ~1; + } + else + to_skip += (mode_size + 1) & ~1; + } + else if (*s == 'n') + to_skip += 4; + else if (*s == 'b') + to_skip += 2; + + return to_skip; +} + +/* Print condition code flags. */ + +static char * +print_flags (struct cris_disasm_data *disdata, unsigned int insn, char *cp) +{ + /* Use the v8 (Etrax 100) flag definitions for disassembly. + The differences with v0 (Etrax 1..4) vs. Svinto are: + v0 'd' <=> v8 'm' + v0 'e' <=> v8 'b'. + FIXME: Emit v0..v3 flag names somehow. */ + static const char v8_fnames[] = "cvznxibm"; + static const char v32_fnames[] = "cvznxiup"; + const char *fnames + = disdata->distype == cris_dis_v32 ? v32_fnames : v8_fnames; + + unsigned char flagbits = (((insn >> 8) & 0xf0) | (insn & 15)); + int i; + + for (i = 0; i < 8; i++) + if (flagbits & (1 << i)) + *cp++ = fnames[i]; + + return cp; +} + +/* Print out an insn with its operands, and update the info->insn_type + fields. The prefix_opcodep and the rest hold a prefix insn that is + supposed to be output as an address mode. */ + +static void +print_with_operands (const struct cris_opcode *opcodep, + unsigned int insn, + unsigned char *buffer, + bfd_vma addr, + disassemble_info *info, + /* If a prefix insn was before this insn (and is supposed + to be output as an address), here is a description of + it. */ + const struct cris_opcode *prefix_opcodep, + unsigned int prefix_insn, + unsigned char *prefix_buffer, + bfd_boolean with_reg_prefix) +{ + /* Get a buffer of somewhat reasonable size where we store + intermediate parts of the insn. */ + char temp[sizeof (".d [$r13=$r12-2147483648],$r10") * 2]; + char *tp = temp; + static const char mode_char[] = "bwd?"; + const char *s; + const char *cs; + struct cris_disasm_data *disdata + = (struct cris_disasm_data *) info->private_data; + + /* Print out the name first thing we do. */ + (*info->fprintf_func) (info->stream, "%s", opcodep->name); + + cs = opcodep->args; + s = cs; + + /* Ignore any prefix indicator. */ + if (*s == 'p') + s++; + + if (*s == 'm' || *s == 'M' || *s == 'z') + { + *tp++ = '.'; + + /* Get the size-letter. */ + *tp++ = *s == 'M' + ? (insn & 0x8000 ? 'd' + : insn & 0x4000 ? 'w' : 'b') + : mode_char[(insn >> 4) & (*s == 'z' ? 1 : 3)]; + + /* Ignore the size and the space character that follows. */ + s += 2; + } + + /* Add a space if this isn't a long-branch, because for those will add + the condition part of the name later. */ + if (opcodep->match != (BRANCH_PC_LOW + BRANCH_INCR_HIGH * 256)) + *tp++ = ' '; + + /* Fill in the insn-type if deducible from the name (and there's no + better way). */ + if (opcodep->name[0] == 'j') + { + if (CONST_STRNEQ (opcodep->name, "jsr")) + /* It's "jsr" or "jsrc". */ + info->insn_type = dis_jsr; + else + /* Any other jump-type insn is considered a branch. */ + info->insn_type = dis_branch; + } + + /* We might know some more fields right now. */ + info->branch_delay_insns = opcodep->delayed; + + /* Handle operands. */ + for (; *s; s++) + { + switch (*s) + { + case 'T': + tp = format_sup_reg ((insn >> 12) & 15, tp, with_reg_prefix); + break; + + case 'A': + if (with_reg_prefix) + *tp++ = REGISTER_PREFIX_CHAR; + *tp++ = 'a'; + *tp++ = 'c'; + *tp++ = 'r'; + break; + + case '[': + case ']': + case ',': + *tp++ = *s; + break; + + case '!': + /* Ignore at this point; used at earlier stages to avoid + recognition if there's a prefix at something that in other + ways looks like a "pop". */ + break; + + case 'd': + /* Ignore. This is an optional ".d " on the large one of + relaxable insns. */ + break; + + case 'B': + /* This was the prefix that made this a "push". We've already + handled it by recognizing it, so signal that the prefix is + handled by setting it to NULL. */ + prefix_opcodep = NULL; + break; + + case 'D': + case 'r': + tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); + break; + + case 'R': + tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); + break; + + case 'n': + { + /* Like N but pc-relative to the start of the insn. */ + unsigned long number + = (buffer[2] + buffer[3] * 256 + buffer[4] * 65536 + + buffer[5] * 0x1000000 + addr); + + /* Finish off and output previous formatted bytes. */ + *tp = 0; + if (temp[0]) + (*info->fprintf_func) (info->stream, "%s", temp); + tp = temp; + + (*info->print_address_func) ((bfd_vma) number, info); + } + break; + + case 'u': + { + /* Like n but the offset is bits <3:0> in the instruction. */ + unsigned long number = (buffer[0] & 0xf) * 2 + addr; + + /* Finish off and output previous formatted bytes. */ + *tp = 0; + if (temp[0]) + (*info->fprintf_func) (info->stream, "%s", temp); + tp = temp; + + (*info->print_address_func) ((bfd_vma) number, info); + } + break; + + case 'N': + case 'y': + case 'Y': + case 'S': + case 's': + /* Any "normal" memory operand. */ + if ((insn & 0x400) && (insn & 15) == 15 && prefix_opcodep == NULL) + { + /* We're looking at [pc+], i.e. we need to output an immediate + number, where the size can depend on different things. */ + long number; + int signedp + = ((*cs == 'z' && (insn & 0x20)) + || opcodep->match == BDAP_QUICK_OPCODE); + int nbytes; + + if (opcodep->imm_oprnd_size == SIZE_FIX_32) + nbytes = 4; + else if (opcodep->imm_oprnd_size == SIZE_SPEC_REG) + { + const struct cris_spec_reg *sregp + = spec_reg_info ((insn >> 12) & 15, disdata->distype); + + /* A NULL return should have been as a non-match earlier, + so catch it as an internal error in the error-case + below. */ + if (sregp == NULL) + /* Whatever non-valid size. */ + nbytes = 42; + else + /* PC is always incremented by a multiple of two. + For CRISv32, immediates are always 4 bytes for + special registers. */ + nbytes = disdata->distype == cris_dis_v32 + ? 4 : (sregp->reg_size + 1) & ~1; + } + else + { + int mode_size = 1 << ((insn >> 4) & (*cs == 'z' ? 1 : 3)); + + if (mode_size == 1) + nbytes = 2; + else + nbytes = mode_size; + } + + switch (nbytes) + { + case 1: + number = buffer[2]; + if (signedp && number > 127) + number -= 256; + break; + + case 2: + number = buffer[2] + buffer[3] * 256; + if (signedp && number > 32767) + number -= 65536; + break; + + case 4: + number + = buffer[2] + buffer[3] * 256 + buffer[4] * 65536 + + buffer[5] * 0x1000000; + break; + + default: + strcpy (tp, "bug"); + tp += 3; + number = 42; + } + + if ((*cs == 'z' && (insn & 0x20)) + || (opcodep->match == BDAP_QUICK_OPCODE + && (nbytes <= 2 || buffer[1 + nbytes] == 0))) + tp = format_dec (number, tp, signedp); + else + { + unsigned int highbyte = (number >> 24) & 0xff; + + /* Either output this as an address or as a number. If it's + a dword with the same high-byte as the address of the + insn, assume it's an address, and also if it's a non-zero + non-0xff high-byte. If this is a jsr or a jump, then + it's definitely an address. */ + if (nbytes == 4 + && (highbyte == ((addr >> 24) & 0xff) + || (highbyte != 0 && highbyte != 0xff) + || info->insn_type == dis_branch + || info->insn_type == dis_jsr)) + { + /* Finish off and output previous formatted bytes. */ + *tp = 0; + tp = temp; + if (temp[0]) + (*info->fprintf_func) (info->stream, "%s", temp); + + (*info->print_address_func) ((bfd_vma) number, info); + + info->target = number; + } + else + tp = format_hex (number, tp, disdata); + } + } + else + { + /* Not an immediate number. Then this is a (possibly + prefixed) memory operand. */ + if (info->insn_type != dis_nonbranch) + { + int mode_size + = 1 << ((insn >> 4) + & (opcodep->args[0] == 'z' ? 1 : 3)); + int size; + info->insn_type = dis_dref; + info->flags |= CRIS_DIS_FLAG_MEMREF; + + if (opcodep->imm_oprnd_size == SIZE_FIX_32) + size = 4; + else if (opcodep->imm_oprnd_size == SIZE_SPEC_REG) + { + const struct cris_spec_reg *sregp + = spec_reg_info ((insn >> 12) & 15, disdata->distype); + + /* FIXME: Improve error handling; should have been caught + earlier. */ + if (sregp == NULL) + size = 4; + else + size = sregp->reg_size; + } + else + size = mode_size; + + info->data_size = size; + } + + *tp++ = '['; + + if (prefix_opcodep + /* We don't match dip with a postincremented field + as a side-effect address mode. */ + && ((insn & 0x400) == 0 + || prefix_opcodep->match != DIP_OPCODE)) + { + if (insn & 0x400) + { + tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); + *tp++ = '='; + } + + + /* We mainly ignore the prefix format string when the + address-mode syntax is output. */ + switch (prefix_opcodep->match) + { + case DIP_OPCODE: + /* It's [r], [r+] or [pc+]. */ + if ((prefix_insn & 0x400) && (prefix_insn & 15) == 15) + { + /* It's [pc+]. This cannot possibly be anything + but an address. */ + unsigned long number + = prefix_buffer[2] + prefix_buffer[3] * 256 + + prefix_buffer[4] * 65536 + + prefix_buffer[5] * 0x1000000; + + info->target = (bfd_vma) number; + + /* Finish off and output previous formatted + data. */ + *tp = 0; + tp = temp; + if (temp[0]) + (*info->fprintf_func) (info->stream, "%s", temp); + + (*info->print_address_func) ((bfd_vma) number, info); + } + else + { + /* For a memref in an address, we use target2. + In this case, target is zero. */ + info->flags + |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG + | CRIS_DIS_FLAG_MEM_TARGET2_MEM); + + info->target2 = prefix_insn & 15; + + *tp++ = '['; + tp = format_reg (disdata, prefix_insn & 15, tp, + with_reg_prefix); + if (prefix_insn & 0x400) + *tp++ = '+'; + *tp++ = ']'; + } + break; + + case BDAP_QUICK_OPCODE: + { + int number; + + number = prefix_buffer[0]; + if (number > 127) + number -= 256; + + /* Output "reg+num" or, if num < 0, "reg-num". */ + tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, + with_reg_prefix); + if (number >= 0) + *tp++ = '+'; + tp = format_dec (number, tp, 1); + + info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG; + info->target = (prefix_insn >> 12) & 15; + info->target2 = (bfd_vma) number; + break; + } + + case BIAP_OPCODE: + /* Output "r+R.m". */ + tp = format_reg (disdata, prefix_insn & 15, tp, + with_reg_prefix); + *tp++ = '+'; + tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, + with_reg_prefix); + *tp++ = '.'; + *tp++ = mode_char[(prefix_insn >> 4) & 3]; + + info->flags + |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG + | CRIS_DIS_FLAG_MEM_TARGET_IS_REG + + | ((prefix_insn & 0x8000) + ? CRIS_DIS_FLAG_MEM_TARGET2_MULT4 + : ((prefix_insn & 0x8000) + ? CRIS_DIS_FLAG_MEM_TARGET2_MULT2 : 0))); + + /* Is it the casejump? It's a "adds.w [pc+r%d.w],pc". */ + if (insn == 0xf83f && (prefix_insn & ~0xf000) == 0x55f) + /* Then start interpreting data as offsets. */ + case_offset_counter = no_of_case_offsets; + break; + + case BDAP_INDIR_OPCODE: + /* Output "r+s.m", or, if "s" is [pc+], "r+s" or + "r-s". */ + tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, + with_reg_prefix); + + if ((prefix_insn & 0x400) && (prefix_insn & 15) == 15) + { + long number; + unsigned int nbytes; + + /* It's a value. Get its size. */ + int mode_size = 1 << ((prefix_insn >> 4) & 3); + + if (mode_size == 1) + nbytes = 2; + else + nbytes = mode_size; + + switch (nbytes) + { + case 1: + number = prefix_buffer[2]; + if (number > 127) + number -= 256; + break; + + case 2: + number = prefix_buffer[2] + prefix_buffer[3] * 256; + if (number > 32767) + number -= 65536; + break; + + case 4: + number + = prefix_buffer[2] + prefix_buffer[3] * 256 + + prefix_buffer[4] * 65536 + + prefix_buffer[5] * 0x1000000; + break; + + default: + strcpy (tp, "bug"); + tp += 3; + number = 42; + } + + info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG; + info->target2 = (bfd_vma) number; + + /* If the size is dword, then assume it's an + address. */ + if (nbytes == 4) + { + /* Finish off and output previous formatted + bytes. */ + *tp++ = '+'; + *tp = 0; + tp = temp; + (*info->fprintf_func) (info->stream, "%s", temp); + + (*info->print_address_func) ((bfd_vma) number, info); + } + else + { + if (number >= 0) + *tp++ = '+'; + tp = format_dec (number, tp, 1); + } + } + else + { + /* Output "r+[R].m" or "r+[R+].m". */ + *tp++ = '+'; + *tp++ = '['; + tp = format_reg (disdata, prefix_insn & 15, tp, + with_reg_prefix); + if (prefix_insn & 0x400) + *tp++ = '+'; + *tp++ = ']'; + *tp++ = '.'; + *tp++ = mode_char[(prefix_insn >> 4) & 3]; + + info->flags + |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG + | CRIS_DIS_FLAG_MEM_TARGET2_MEM + | CRIS_DIS_FLAG_MEM_TARGET_IS_REG + + | (((prefix_insn >> 4) == 2) + ? 0 + : (((prefix_insn >> 4) & 3) == 1 + ? CRIS_DIS_FLAG_MEM_TARGET2_MEM_WORD + : CRIS_DIS_FLAG_MEM_TARGET2_MEM_BYTE))); + } + break; + + default: + (*info->fprintf_func) (info->stream, "?prefix-bug"); + } + + /* To mark that the prefix is used, reset it. */ + prefix_opcodep = NULL; + } + else + { + tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); + + info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG; + info->target = insn & 15; + + if (insn & 0x400) + *tp++ = '+'; + } + *tp++ = ']'; + } + break; + + case 'x': + tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); + *tp++ = '.'; + *tp++ = mode_char[(insn >> 4) & 3]; + break; + + case 'I': + tp = format_dec (insn & 63, tp, 0); + break; + + case 'b': + { + int where = buffer[2] + buffer[3] * 256; + + if (where > 32767) + where -= 65536; + + where += addr + ((disdata->distype == cris_dis_v32) ? 0 : 4); + + if (insn == BA_PC_INCR_OPCODE) + info->insn_type = dis_branch; + else + info->insn_type = dis_condbranch; + + info->target = (bfd_vma) where; + + *tp = 0; + tp = temp; + (*info->fprintf_func) (info->stream, "%s%s ", + temp, cris_cc_strings[insn >> 12]); + + (*info->print_address_func) ((bfd_vma) where, info); + } + break; + + case 'c': + tp = format_dec (insn & 31, tp, 0); + break; + + case 'C': + tp = format_dec (insn & 15, tp, 0); + break; + + case 'o': + { + long offset = insn & 0xfe; + bfd_vma target; + + if (insn & 1) + offset |= ~0xff; + + if (opcodep->match == BA_QUICK_OPCODE) + info->insn_type = dis_branch; + else + info->insn_type = dis_condbranch; + + target = addr + ((disdata->distype == cris_dis_v32) ? 0 : 2) + offset; + info->target = target; + *tp = 0; + tp = temp; + (*info->fprintf_func) (info->stream, "%s", temp); + (*info->print_address_func) (target, info); + } + break; + + case 'Q': + case 'O': + { + long number = buffer[0]; + + if (number > 127) + number = number - 256; + + tp = format_dec (number, tp, 1); + *tp++ = ','; + tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); + } + break; + + case 'f': + tp = print_flags (disdata, insn, tp); + break; + + case 'i': + tp = format_dec ((insn & 32) ? (insn & 31) | ~31L : insn & 31, tp, 1); + break; + + case 'P': + { + const struct cris_spec_reg *sregp + = spec_reg_info ((insn >> 12) & 15, disdata->distype); + + if (sregp->name == NULL) + /* Should have been caught as a non-match eariler. */ + *tp++ = '?'; + else + { + if (with_reg_prefix) + *tp++ = REGISTER_PREFIX_CHAR; + strcpy (tp, sregp->name); + tp += strlen (tp); + } + } + break; + + default: + strcpy (tp, "???"); + tp += 3; + } + } + + *tp = 0; + + if (prefix_opcodep) + (*info->fprintf_func) (info->stream, " (OOPS unused prefix \"%s: %s\")", + prefix_opcodep->name, prefix_opcodep->args); + + (*info->fprintf_func) (info->stream, "%s", temp); + + /* Get info for matching case-tables, if we don't have any active. + We assume that the last constant seen is used; either in the insn + itself or in a "move.d const,rN, sub.d rN,rM"-like sequence. */ + if (TRACE_CASE && case_offset_counter == 0) + { + if (CONST_STRNEQ (opcodep->name, "sub")) + case_offset = last_immediate; + + /* It could also be an "add", if there are negative case-values. */ + else if (CONST_STRNEQ (opcodep->name, "add")) + /* The first case is the negated operand to the add. */ + case_offset = -last_immediate; + + /* A bound insn will tell us the number of cases. */ + else if (CONST_STRNEQ (opcodep->name, "bound")) + no_of_case_offsets = last_immediate + 1; + + /* A jump or jsr or branch breaks the chain of insns for a + case-table, so assume default first-case again. */ + else if (info->insn_type == dis_jsr + || info->insn_type == dis_branch + || info->insn_type == dis_condbranch) + case_offset = 0; + } +} + + +/* Print the CRIS instruction at address memaddr on stream. Returns + length of the instruction, in bytes. Prefix register names with `$' if + WITH_REG_PREFIX. */ + +static int +print_insn_cris_generic (bfd_vma memaddr, + disassemble_info *info, + bfd_boolean with_reg_prefix) +{ + int nbytes; + unsigned int insn; + const struct cris_opcode *matchedp; + int advance = 0; + struct cris_disasm_data *disdata + = (struct cris_disasm_data *) info->private_data; + + /* No instruction will be disassembled as longer than this number of + bytes; stacked prefixes will not be expanded. */ + unsigned char buffer[MAX_BYTES_PER_CRIS_INSN]; + unsigned char *bufp; + int status = 0; + bfd_vma addr; + + /* There will be an "out of range" error after the last instruction. + Reading pairs of bytes in decreasing number, we hope that we will get + at least the amount that we will consume. + + If we can't get any data, or we do not get enough data, we print + the error message. */ + + nbytes = info->buffer_length; + if (nbytes > MAX_BYTES_PER_CRIS_INSN) + nbytes = MAX_BYTES_PER_CRIS_INSN; + status = (*info->read_memory_func) (memaddr, buffer, nbytes, info); + + /* If we did not get all we asked for, then clear the rest. + Hopefully this makes a reproducible result in case of errors. */ + if (nbytes != MAX_BYTES_PER_CRIS_INSN) + memset (buffer + nbytes, 0, MAX_BYTES_PER_CRIS_INSN - nbytes); + + addr = memaddr; + bufp = buffer; + + /* Set some defaults for the insn info. */ + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->flags = 0; + info->target = 0; + info->target2 = 0; + + /* If we got any data, disassemble it. */ + if (nbytes != 0) + { + matchedp = NULL; + + insn = bufp[0] + bufp[1] * 256; + + /* If we're in a case-table, don't disassemble the offsets. */ + if (TRACE_CASE && case_offset_counter != 0) + { + info->insn_type = dis_noninsn; + advance += 2; + + /* If to print data as offsets, then shortcut here. */ + (*info->fprintf_func) (info->stream, "case %ld%s: -> ", + case_offset + no_of_case_offsets + - case_offset_counter, + case_offset_counter == 1 ? "/default" : + ""); + + (*info->print_address_func) ((bfd_vma) + ((short) (insn) + + (long) (addr + - (no_of_case_offsets + - case_offset_counter) + * 2)), info); + case_offset_counter--; + + /* The default case start (without a "sub" or "add") must be + zero. */ + if (case_offset_counter == 0) + case_offset = 0; + } + else if (insn == 0) + { + /* We're often called to disassemble zeroes. While this is a + valid "bcc .+2" insn, it is also useless enough and enough + of a nuiscance that we will just output "bcc .+2" for it + and signal it as a noninsn. */ + (*info->fprintf_func) (info->stream, + disdata->distype == cris_dis_v32 + ? "bcc ." : "bcc .+2"); + info->insn_type = dis_noninsn; + advance += 2; + } + else + { + const struct cris_opcode *prefix_opcodep = NULL; + unsigned char *prefix_buffer = bufp; + unsigned int prefix_insn = insn; + int prefix_size = 0; + + matchedp = get_opcode_entry (insn, NO_CRIS_PREFIX, disdata); + + /* Check if we're supposed to write out prefixes as address + modes and if this was a prefix. */ + if (matchedp != NULL && PARSE_PREFIX && matchedp->args[0] == 'p') + { + /* If it's a prefix, put it into the prefix vars and get the + main insn. */ + prefix_size = bytes_to_skip (prefix_insn, matchedp, + disdata->distype, NULL); + prefix_opcodep = matchedp; + + insn = bufp[prefix_size] + bufp[prefix_size + 1] * 256; + matchedp = get_opcode_entry (insn, prefix_insn, disdata); + + if (matchedp != NULL) + { + addr += prefix_size; + bufp += prefix_size; + advance += prefix_size; + } + else + { + /* The "main" insn wasn't valid, at least not when + prefixed. Put back things enough to output the + prefix insn only, as a normal insn. */ + matchedp = prefix_opcodep; + insn = prefix_insn; + prefix_opcodep = NULL; + } + } + + if (matchedp == NULL) + { + (*info->fprintf_func) (info->stream, "??0x%x", insn); + advance += 2; + + info->insn_type = dis_noninsn; + } + else + { + advance + += bytes_to_skip (insn, matchedp, disdata->distype, + prefix_opcodep); + + /* The info_type and assorted fields will be set according + to the operands. */ + print_with_operands (matchedp, insn, bufp, addr, info, + prefix_opcodep, prefix_insn, + prefix_buffer, with_reg_prefix); + } + } + } + else + info->insn_type = dis_noninsn; + + /* If we read less than MAX_BYTES_PER_CRIS_INSN, i.e. we got an error + status when reading that much, and the insn decoding indicated a + length exceeding what we read, there is an error. */ + if (status != 0 && (nbytes == 0 || advance > nbytes)) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + /* Max supported insn size with one folded prefix insn. */ + info->bytes_per_line = MAX_BYTES_PER_CRIS_INSN; + + /* I would like to set this to a fixed value larger than the actual + number of bytes to print in order to avoid spaces between bytes, + but objdump.c (2.9.1) does not like that, so we print 16-bit + chunks, which is the next choice. */ + info->bytes_per_chunk = 2; + + /* Printing bytes in order of increasing addresses makes sense, + especially on a little-endian target. + This is completely the opposite of what you think; setting this to + BFD_ENDIAN_LITTLE will print bytes in order N..0 rather than the 0..N + we want. */ + info->display_endian = BFD_ENDIAN_BIG; + + return advance; +} + +/* Disassemble, prefixing register names with `$'. CRIS v0..v10. */ +static int +print_insn_cris_with_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_v0_v10)) + return -1; + return print_insn_cris_generic (vma, info, true); +} +/* Disassemble, prefixing register names with `$'. CRIS v32. */ + +static int +print_insn_crisv32_with_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_v32)) + return -1; + return print_insn_cris_generic (vma, info, true); +} + +#if 0 +/* Disassemble, prefixing register names with `$'. + Common v10 and v32 subset. */ + +static int +print_insn_crisv10_v32_with_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_common_v10_v32)) + return -1; + return print_insn_cris_generic (vma, info, true); +} + +/* Disassemble, no prefixes on register names. CRIS v0..v10. */ + +static int +print_insn_cris_without_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_v0_v10)) + return -1; + return print_insn_cris_generic (vma, info, false); +} + +/* Disassemble, no prefixes on register names. CRIS v32. */ + +static int +print_insn_crisv32_without_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_v32)) + return -1; + return print_insn_cris_generic (vma, info, false); +} + +/* Disassemble, no prefixes on register names. + Common v10 and v32 subset. */ + +static int +print_insn_crisv10_v32_without_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_common_v10_v32)) + return -1; + return print_insn_cris_generic (vma, info, false); +} +#endif + +int +print_insn_crisv10 (bfd_vma vma, + disassemble_info *info) +{ + return print_insn_cris_with_register_prefix(vma, info); +} + +int +print_insn_crisv32 (bfd_vma vma, + disassemble_info *info) +{ + return print_insn_crisv32_with_register_prefix(vma, info); +} + +/* Return a disassembler-function that prints registers with a `$' prefix, + or one that prints registers without a prefix. + FIXME: We should improve the solution to avoid the multitude of + functions seen above. */ +#if 0 +disassembler_ftype +cris_get_disassembler (bfd *abfd) +{ + /* If there's no bfd in sight, we return what is valid as input in all + contexts if fed back to the assembler: disassembly *with* register + prefix. Unfortunately this will be totally wrong for v32. */ + if (abfd == NULL) + return print_insn_cris_with_register_prefix; + + if (bfd_get_symbol_leading_char (abfd) == 0) + { + if (bfd_get_mach (abfd) == bfd_mach_cris_v32) + return print_insn_crisv32_with_register_prefix; + if (bfd_get_mach (abfd) == bfd_mach_cris_v10_v32) + return print_insn_crisv10_v32_with_register_prefix; + + /* We default to v10. This may be specifically specified in the + bfd mach, but is also the default setting. */ + return print_insn_cris_with_register_prefix; + } + + if (bfd_get_mach (abfd) == bfd_mach_cris_v32) + return print_insn_crisv32_without_register_prefix; + if (bfd_get_mach (abfd) == bfd_mach_cris_v10_v32) + return print_insn_crisv10_v32_without_register_prefix; + return print_insn_cris_without_register_prefix; +} +#endif +/* Local variables: + eval: (c-set-style "gnu") + indent-tabs-mode: t + End: */ diff --git a/disas/hppa.c b/disas/hppa.c new file mode 100644 index 0000000..c7c8be6 --- /dev/null +++ b/disas/hppa.c @@ -0,0 +1,2831 @@ +/* Disassembler for the PA-RISC. Somewhat derived from sparc-pinsn.c. + Copyright 1989, 1990, 1992, 1993, 1994, 1995, 1998, 1999, 2000, 2001, 2003, + 2005 Free Software Foundation, Inc. + + Contributed by the Center for Software Science at the + University of Utah (pa-gdb-bugs@cs.utah.edu). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include "disas/bfd.h" + +/* HP PA-RISC SOM object file format: definitions internal to BFD. + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, + 2003 Free Software Foundation, Inc. + + Contributed by the Center for Software Science at the + University of Utah (pa-gdb-bugs@cs.utah.edu). + + This file is part of BFD, the Binary File Descriptor library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#ifndef _LIBHPPA_H +#define _LIBHPPA_H + +#define BYTES_IN_WORD 4 +#define PA_PAGESIZE 0x1000 + +/* The PA instruction set variants. */ +enum pa_arch {pa10 = 10, pa11 = 11, pa20 = 20, pa20w = 25}; + +/* HP PA-RISC relocation types */ + +enum hppa_reloc_field_selector_type + { + R_HPPA_FSEL = 0x0, + R_HPPA_LSSEL = 0x1, + R_HPPA_RSSEL = 0x2, + R_HPPA_LSEL = 0x3, + R_HPPA_RSEL = 0x4, + R_HPPA_LDSEL = 0x5, + R_HPPA_RDSEL = 0x6, + R_HPPA_LRSEL = 0x7, + R_HPPA_RRSEL = 0x8, + R_HPPA_NSEL = 0x9, + R_HPPA_NLSEL = 0xa, + R_HPPA_NLRSEL = 0xb, + R_HPPA_PSEL = 0xc, + R_HPPA_LPSEL = 0xd, + R_HPPA_RPSEL = 0xe, + R_HPPA_TSEL = 0xf, + R_HPPA_LTSEL = 0x10, + R_HPPA_RTSEL = 0x11, + R_HPPA_LTPSEL = 0x12, + R_HPPA_RTPSEL = 0x13 + }; + +/* /usr/include/reloc.h defines these to constants. We want to use + them in enums, so #undef them before we start using them. We might + be able to fix this another way by simply managing not to include + /usr/include/reloc.h, but currently GDB picks up these defines + somewhere. */ +#undef e_fsel +#undef e_lssel +#undef e_rssel +#undef e_lsel +#undef e_rsel +#undef e_ldsel +#undef e_rdsel +#undef e_lrsel +#undef e_rrsel +#undef e_nsel +#undef e_nlsel +#undef e_nlrsel +#undef e_psel +#undef e_lpsel +#undef e_rpsel +#undef e_tsel +#undef e_ltsel +#undef e_rtsel +#undef e_one +#undef e_two +#undef e_pcrel +#undef e_con +#undef e_plabel +#undef e_abs + +/* for compatibility */ +enum hppa_reloc_field_selector_type_alt + { + e_fsel = R_HPPA_FSEL, + e_lssel = R_HPPA_LSSEL, + e_rssel = R_HPPA_RSSEL, + e_lsel = R_HPPA_LSEL, + e_rsel = R_HPPA_RSEL, + e_ldsel = R_HPPA_LDSEL, + e_rdsel = R_HPPA_RDSEL, + e_lrsel = R_HPPA_LRSEL, + e_rrsel = R_HPPA_RRSEL, + e_nsel = R_HPPA_NSEL, + e_nlsel = R_HPPA_NLSEL, + e_nlrsel = R_HPPA_NLRSEL, + e_psel = R_HPPA_PSEL, + e_lpsel = R_HPPA_LPSEL, + e_rpsel = R_HPPA_RPSEL, + e_tsel = R_HPPA_TSEL, + e_ltsel = R_HPPA_LTSEL, + e_rtsel = R_HPPA_RTSEL, + e_ltpsel = R_HPPA_LTPSEL, + e_rtpsel = R_HPPA_RTPSEL + }; + +enum hppa_reloc_expr_type + { + R_HPPA_E_ONE = 0, + R_HPPA_E_TWO = 1, + R_HPPA_E_PCREL = 2, + R_HPPA_E_CON = 3, + R_HPPA_E_PLABEL = 7, + R_HPPA_E_ABS = 18 + }; + +/* for compatibility */ +enum hppa_reloc_expr_type_alt + { + e_one = R_HPPA_E_ONE, + e_two = R_HPPA_E_TWO, + e_pcrel = R_HPPA_E_PCREL, + e_con = R_HPPA_E_CON, + e_plabel = R_HPPA_E_PLABEL, + e_abs = R_HPPA_E_ABS + }; + + +/* Relocations for function calls must be accompanied by parameter + relocation bits. These bits describe exactly where the caller has + placed the function's arguments and where it expects to find a return + value. + + Both ELF and SOM encode this information within the addend field + of the call relocation. (Note this could break very badly if one + was to make a call like bl foo + 0x12345678). + + The high order 10 bits contain parameter relocation information, + the low order 22 bits contain the constant offset. */ + +#define HPPA_R_ARG_RELOC(a) \ + (((a) >> 22) & 0x3ff) +#define HPPA_R_CONSTANT(a) \ + ((((bfd_signed_vma)(a)) << (BFD_ARCH_SIZE-22)) >> (BFD_ARCH_SIZE-22)) +#define HPPA_R_ADDEND(r, c) \ + (((r) << 22) + ((c) & 0x3fffff)) + + +/* Some functions to manipulate PA instructions. */ + +/* Declare the functions with the unused attribute to avoid warnings. */ +static inline int sign_extend (int, int) ATTRIBUTE_UNUSED; +static inline int low_sign_extend (int, int) ATTRIBUTE_UNUSED; +static inline int sign_unext (int, int) ATTRIBUTE_UNUSED; +static inline int low_sign_unext (int, int) ATTRIBUTE_UNUSED; +static inline int re_assemble_3 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_12 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_14 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_16 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_17 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_21 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_22 (int) ATTRIBUTE_UNUSED; +static inline bfd_signed_vma hppa_field_adjust + (bfd_vma, bfd_signed_vma, enum hppa_reloc_field_selector_type_alt) + ATTRIBUTE_UNUSED; +static inline int hppa_rebuild_insn (int, int, int) ATTRIBUTE_UNUSED; + + +/* The *sign_extend functions are used to assemble various bitfields + taken from an instruction and return the resulting immediate + value. */ + +static inline int +sign_extend (int x, int len) +{ + int signbit = (1 << (len - 1)); + int mask = (signbit << 1) - 1; + return ((x & mask) ^ signbit) - signbit; +} + +static inline int +low_sign_extend (int x, int len) +{ + return (x >> 1) - ((x & 1) << (len - 1)); +} + + +/* The re_assemble_* functions prepare an immediate value for + insertion into an opcode. pa-risc uses all sorts of weird bitfields + in the instruction to hold the value. */ + +static inline int +sign_unext (int x, int len) +{ + int len_ones; + + len_ones = (1 << len) - 1; + + return x & len_ones; +} + +static inline int +low_sign_unext (int x, int len) +{ + int temp; + int sign; + + sign = (x >> (len-1)) & 1; + + temp = sign_unext (x, len-1); + + return (temp << 1) | sign; +} + +static inline int +re_assemble_3 (int as3) +{ + return (( (as3 & 4) << (13-2)) + | ((as3 & 3) << (13+1))); +} + +static inline int +re_assemble_12 (int as12) +{ + return (( (as12 & 0x800) >> 11) + | ((as12 & 0x400) >> (10 - 2)) + | ((as12 & 0x3ff) << (1 + 2))); +} + +static inline int +re_assemble_14 (int as14) +{ + return (( (as14 & 0x1fff) << 1) + | ((as14 & 0x2000) >> 13)); +} + +static inline int +re_assemble_16 (int as16) +{ + int s, t; + + /* Unusual 16-bit encoding, for wide mode only. */ + t = (as16 << 1) & 0xffff; + s = (as16 & 0x8000); + return (t ^ s ^ (s >> 1)) | (s >> 15); +} + +static inline int +re_assemble_17 (int as17) +{ + return (( (as17 & 0x10000) >> 16) + | ((as17 & 0x0f800) << (16 - 11)) + | ((as17 & 0x00400) >> (10 - 2)) + | ((as17 & 0x003ff) << (1 + 2))); +} + +static inline int +re_assemble_21 (int as21) +{ + return (( (as21 & 0x100000) >> 20) + | ((as21 & 0x0ffe00) >> 8) + | ((as21 & 0x000180) << 7) + | ((as21 & 0x00007c) << 14) + | ((as21 & 0x000003) << 12)); +} + +static inline int +re_assemble_22 (int as22) +{ + return (( (as22 & 0x200000) >> 21) + | ((as22 & 0x1f0000) << (21 - 16)) + | ((as22 & 0x00f800) << (16 - 11)) + | ((as22 & 0x000400) >> (10 - 2)) + | ((as22 & 0x0003ff) << (1 + 2))); +} + + +/* Handle field selectors for PA instructions. + The L and R (and LS, RS etc.) selectors are used in pairs to form a + full 32 bit address. eg. + + LDIL L'start,%r1 ; put left part into r1 + LDW R'start(%r1),%r2 ; add r1 and right part to form address + + This function returns sign extended values in all cases. +*/ + +static inline bfd_signed_vma +hppa_field_adjust (bfd_vma sym_val, + bfd_signed_vma addend, + enum hppa_reloc_field_selector_type_alt r_field) +{ + bfd_signed_vma value; + + value = sym_val + addend; + switch (r_field) + { + case e_fsel: + /* F: No change. */ + break; + + case e_nsel: + /* N: null selector. I don't really understand what this is all + about, but HP's documentation says "this indicates that zero + bits are to be used for the displacement on the instruction. + This fixup is used to identify three-instruction sequences to + access data (for importing shared library data)." */ + value = 0; + break; + + case e_lsel: + case e_nlsel: + /* L: Select top 21 bits. */ + value = value >> 11; + break; + + case e_rsel: + /* R: Select bottom 11 bits. */ + value = value & 0x7ff; + break; + + case e_lssel: + /* LS: Round to nearest multiple of 2048 then select top 21 bits. */ + value = value + 0x400; + value = value >> 11; + break; + + case e_rssel: + /* RS: Select bottom 11 bits for LS. + We need to return a value such that 2048 * LS'x + RS'x == x. + ie. RS'x = x - ((x + 0x400) & -0x800) + this is just a sign extension from bit 21. */ + value = ((value & 0x7ff) ^ 0x400) - 0x400; + break; + + case e_ldsel: + /* LD: Round to next multiple of 2048 then select top 21 bits. + Yes, if we are already on a multiple of 2048, we go up to the + next one. RD in this case will be -2048. */ + value = value + 0x800; + value = value >> 11; + break; + + case e_rdsel: + /* RD: Set bits 0-20 to one. */ + value = value | -0x800; + break; + + case e_lrsel: + case e_nlrsel: + /* LR: L with rounding of the addend to nearest 8k. */ + value = sym_val + ((addend + 0x1000) & -0x2000); + value = value >> 11; + break; + + case e_rrsel: + /* RR: R with rounding of the addend to nearest 8k. + We need to return a value such that 2048 * LR'x + RR'x == x + ie. RR'x = s+a - (s + (((a + 0x1000) & -0x2000) & -0x800)) + . = s+a - ((s & -0x800) + ((a + 0x1000) & -0x2000)) + . = (s & 0x7ff) + a - ((a + 0x1000) & -0x2000) */ + value = (sym_val & 0x7ff) + (((addend & 0x1fff) ^ 0x1000) - 0x1000); + break; + + default: + abort (); + } + return value; +} + +/* PA-RISC OPCODES */ +#define get_opcode(insn) (((insn) >> 26) & 0x3f) + +enum hppa_opcode_type +{ + /* None of the opcodes in the first group generate relocs, so we + aren't too concerned about them. */ + OP_SYSOP = 0x00, + OP_MEMMNG = 0x01, + OP_ALU = 0x02, + OP_NDXMEM = 0x03, + OP_SPOP = 0x04, + OP_DIAG = 0x05, + OP_FMPYADD = 0x06, + OP_UNDEF07 = 0x07, + OP_COPRW = 0x09, + OP_COPRDW = 0x0b, + OP_COPR = 0x0c, + OP_FLOAT = 0x0e, + OP_PRDSPEC = 0x0f, + OP_UNDEF15 = 0x15, + OP_UNDEF1d = 0x1d, + OP_FMPYSUB = 0x26, + OP_FPFUSED = 0x2e, + OP_SHEXDP0 = 0x34, + OP_SHEXDP1 = 0x35, + OP_SHEXDP2 = 0x36, + OP_UNDEF37 = 0x37, + OP_SHEXDP3 = 0x3c, + OP_SHEXDP4 = 0x3d, + OP_MULTMED = 0x3e, + OP_UNDEF3f = 0x3f, + + OP_LDIL = 0x08, + OP_ADDIL = 0x0a, + + OP_LDO = 0x0d, + OP_LDB = 0x10, + OP_LDH = 0x11, + OP_LDW = 0x12, + OP_LDWM = 0x13, + OP_STB = 0x18, + OP_STH = 0x19, + OP_STW = 0x1a, + OP_STWM = 0x1b, + + OP_LDD = 0x14, + OP_STD = 0x1c, + + OP_FLDW = 0x16, + OP_LDWL = 0x17, + OP_FSTW = 0x1e, + OP_STWL = 0x1f, + + OP_COMBT = 0x20, + OP_COMIBT = 0x21, + OP_COMBF = 0x22, + OP_COMIBF = 0x23, + OP_CMPBDT = 0x27, + OP_ADDBT = 0x28, + OP_ADDIBT = 0x29, + OP_ADDBF = 0x2a, + OP_ADDIBF = 0x2b, + OP_CMPBDF = 0x2f, + OP_BVB = 0x30, + OP_BB = 0x31, + OP_MOVB = 0x32, + OP_MOVIB = 0x33, + OP_CMPIBD = 0x3b, + + OP_COMICLR = 0x24, + OP_SUBI = 0x25, + OP_ADDIT = 0x2c, + OP_ADDI = 0x2d, + + OP_BE = 0x38, + OP_BLE = 0x39, + OP_BL = 0x3a +}; + + +/* Insert VALUE into INSN using R_FORMAT to determine exactly what + bits to change. */ + +static inline int +hppa_rebuild_insn (int insn, int value, int r_format) +{ + switch (r_format) + { + case 11: + return (insn & ~ 0x7ff) | low_sign_unext (value, 11); + + case 12: + return (insn & ~ 0x1ffd) | re_assemble_12 (value); + + + case 10: + return (insn & ~ 0x3ff1) | re_assemble_14 (value & -8); + + case -11: + return (insn & ~ 0x3ff9) | re_assemble_14 (value & -4); + + case 14: + return (insn & ~ 0x3fff) | re_assemble_14 (value); + + + case -10: + return (insn & ~ 0xfff1) | re_assemble_16 (value & -8); + + case -16: + return (insn & ~ 0xfff9) | re_assemble_16 (value & -4); + + case 16: + return (insn & ~ 0xffff) | re_assemble_16 (value); + + + case 17: + return (insn & ~ 0x1f1ffd) | re_assemble_17 (value); + + case 21: + return (insn & ~ 0x1fffff) | re_assemble_21 (value); + + case 22: + return (insn & ~ 0x3ff1ffd) | re_assemble_22 (value); + + case 32: + return value; + + default: + abort (); + } + return insn; +} + +#endif /* _LIBHPPA_H */ +/* Table of opcodes for the PA-RISC. + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + + Contributed by the Center for Software Science at the + University of Utah (pa-gdb-bugs@cs.utah.edu). + +This file is part of GAS, the GNU Assembler, and GDB, the GNU disassembler. + +GAS/GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS/GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS or GDB; see the file COPYING. +If not, see . */ + +#if !defined(__STDC__) && !defined(const) +#define const +#endif + +/* + * Structure of an opcode table entry. + */ + +/* There are two kinds of delay slot nullification: normal which is + * controlled by the nullification bit, and conditional, which depends + * on the direction of the branch and its success or failure. + * + * NONE is unfortunately #defined in the hiux system include files. + * #undef it away. + */ +#undef NONE +struct pa_opcode +{ + const char *name; + unsigned long int match; /* Bits that must be set... */ + unsigned long int mask; /* ... in these bits. */ + const char *args; + enum pa_arch arch; + char flags; +}; + +/* Enables strict matching. Opcodes with match errors are skipped + when this bit is set. */ +#define FLAG_STRICT 0x1 + +/* + All hppa opcodes are 32 bits. + + The match component is a mask saying which bits must match a + particular opcode in order for an instruction to be an instance + of that opcode. + + The args component is a string containing one character for each operand of + the instruction. Characters used as a prefix allow any second character to + be used without conflicting with the main operand characters. + + Bit positions in this description follow HP usage of lsb = 31, + "at" is lsb of field. + + In the args field, the following characters must match exactly: + + '+,() ' + + In the args field, the following characters are unused: + + ' " - / 34 6789:; ' + '@ C M [\] ' + '` e g } ' + + Here are all the characters: + + ' !"#$%&'()*+-,./0123456789:;<=>?' + '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_' + '`abcdefghijklmnopqrstuvwxyz{|}~ ' + +Kinds of operands: + x integer register field at 15. + b integer register field at 10. + t integer register field at 31. + a integer register field at 10 and 15 (for PERMH) + 5 5 bit immediate at 15. + s 2 bit space specifier at 17. + S 3 bit space specifier at 18. + V 5 bit immediate value at 31 + i 11 bit immediate value at 31 + j 14 bit immediate value at 31 + k 21 bit immediate value at 31 + l 16 bit immediate value at 31 (wide mode only, unusual encoding). + n nullification for branch instructions + N nullification for spop and copr instructions + w 12 bit branch displacement + W 17 bit branch displacement (PC relative) + X 22 bit branch displacement (PC relative) + z 17 bit branch displacement (just a number, not an address) + +Also these: + + . 2 bit shift amount at 25 + * 4 bit shift amount at 25 + p 5 bit shift count at 26 (to support the SHD instruction) encoded as + 31-p + ~ 6 bit shift count at 20,22:26 encoded as 63-~. + P 5 bit bit position at 26 + q 6 bit bit position at 20,22:26 + T 5 bit field length at 31 (encoded as 32-T) + % 6 bit field length at 23,27:31 (variable extract/deposit) + | 6 bit field length at 19,27:31 (fixed extract/deposit) + A 13 bit immediate at 18 (to support the BREAK instruction) + ^ like b, but describes a control register + ! sar (cr11) register + D 26 bit immediate at 31 (to support the DIAG instruction) + $ 9 bit immediate at 28 (to support POPBTS) + + v 3 bit Special Function Unit identifier at 25 + O 20 bit Special Function Unit operation split between 15 bits at 20 + and 5 bits at 31 + o 15 bit Special Function Unit operation at 20 + 2 22 bit Special Function Unit operation split between 17 bits at 20 + and 5 bits at 31 + 1 15 bit Special Function Unit operation split between 10 bits at 20 + and 5 bits at 31 + 0 10 bit Special Function Unit operation split between 5 bits at 20 + and 5 bits at 31 + u 3 bit coprocessor unit identifier at 25 + F Source Floating Point Operand Format Completer encoded 2 bits at 20 + I Source Floating Point Operand Format Completer encoded 1 bits at 20 + (for 0xe format FP instructions) + G Destination Floating Point Operand Format Completer encoded 2 bits at 18 + H Floating Point Operand Format at 26 for 'fmpyadd' and 'fmpysub' + (very similar to 'F') + + r 5 bit immediate value at 31 (for the break instruction) + (very similar to V above, except the value is unsigned instead of + low_sign_ext) + R 5 bit immediate value at 15 (for the ssm, rsm, probei instructions) + (same as r above, except the value is in a different location) + U 10 bit immediate value at 15 (for SSM, RSM on pa2.0) + Q 5 bit immediate value at 10 (a bit position specified in + the bb instruction. It's the same as r above, except the + value is in a different location) + B 5 bit immediate value at 10 (a bit position specified in + the bb instruction. Similar to Q, but 64 bit handling is + different. + Z %r1 -- implicit target of addil instruction. + L ,%r2 completer for new syntax branch + { Source format completer for fcnv + _ Destination format completer for fcnv + h cbit for fcmp + = gfx tests for ftest + d 14 bit offset for single precision FP long load/store. + # 14 bit offset for double precision FP load long/store. + J Yet another 14 bit offset for load/store with ma,mb completers. + K Yet another 14 bit offset for load/store with ma,mb completers. + y 16 bit offset for word aligned load/store (PA2.0 wide). + & 16 bit offset for dword aligned load/store (PA2.0 wide). + < 16 bit offset for load/store with ma,mb completers (PA2.0 wide). + > 16 bit offset for load/store with ma,mb completers (PA2.0 wide). + Y %sr0,%r31 -- implicit target of be,l instruction. + @ implicit immediate value of 0 + +Completer operands all have 'c' as the prefix: + + cx indexed load and store completer. + cX indexed load and store completer. Like cx, but emits a space + after in disassembler. + cm short load and store completer. + cM short load and store completer. Like cm, but emits a space + after in disassembler. + cq long load and store completer (like cm, but inserted into a + different location in the target instruction). + cs store bytes short completer. + cA store bytes short completer. Like cs, but emits a space + after in disassembler. + ce long load/store completer for LDW/STW with a different encoding + than the others + cc load cache control hint + cd load and clear cache control hint + cC store cache control hint + co ordered access + + cp branch link and push completer + cP branch pop completer + cl branch link completer + cg branch gate completer + + cw read/write completer for PROBE + cW wide completer for MFCTL + cL local processor completer for cache control + cZ System Control Completer (to support LPA, LHA, etc.) + + ci correction completer for DCOR + ca add completer + cy 32 bit add carry completer + cY 64 bit add carry completer + cv signed overflow trap completer + ct trap on condition completer for ADDI, SUB + cT trap on condition completer for UADDCM + cb 32 bit borrow completer for SUB + cB 64 bit borrow completer for SUB + + ch left/right half completer + cH signed/unsigned saturation completer + cS signed/unsigned completer at 21 + cz zero/sign extension completer. + c* permutation completer + +Condition operands all have '?' as the prefix: + + ?f Floating point compare conditions (encoded as 5 bits at 31) + + ?a add conditions + ?A 64 bit add conditions + ?@ add branch conditions followed by nullify + ?d non-negated add branch conditions + ?D negated add branch conditions + ?w wide mode non-negated add branch conditions + ?W wide mode negated add branch conditions + + ?s compare/subtract conditions + ?S 64 bit compare/subtract conditions + ?t non-negated compare and branch conditions + ?n 32 bit compare and branch conditions followed by nullify + ?N 64 bit compare and branch conditions followed by nullify + ?Q 64 bit compare and branch conditions for CMPIB instruction + + ?l logical conditions + ?L 64 bit logical conditions + + ?b branch on bit conditions + ?B 64 bit branch on bit conditions + + ?x shift/extract/deposit conditions + ?X 64 bit shift/extract/deposit conditions + ?y shift/extract/deposit conditions followed by nullify for conditional + branches + + ?u unit conditions + ?U 64 bit unit conditions + +Floating point registers all have 'f' as a prefix: + + ft target register at 31 + fT target register with L/R halves at 31 + fa operand 1 register at 10 + fA operand 1 register with L/R halves at 10 + fX Same as fA, except prints a space before register during disasm + fb operand 2 register at 15 + fB operand 2 register with L/R halves at 15 + fC operand 3 register with L/R halves at 16:18,21:23 + fe Like fT, but encoding is different. + fE Same as fe, except prints a space before register during disasm. + fx target register at 15 (only for PA 2.0 long format FLDD/FSTD). + +Float registers for fmpyadd and fmpysub: + + fi mult operand 1 register at 10 + fj mult operand 2 register at 15 + fk mult target register at 20 + fl add/sub operand register at 25 + fm add/sub target register at 31 + +*/ + + +#if 0 +/* List of characters not to put a space after. Note that + "," is included, as the "spopN" operations use literal + commas in their completer sections. */ +static const char *const completer_chars = ",CcY<>?!@+&U~FfGHINnOoZMadu|/=0123%e$m}"; +#endif + +/* The order of the opcodes in this table is significant: + + * The assembler requires that all instances of the same mnemonic be + consecutive. If they aren't, the assembler will bomb at runtime. + + * Immediate fields use pa_get_absolute_expression to parse the + string. It will generate a "bad expression" error if passed + a register name. Thus, register index variants of an opcode + need to precede immediate variants. + + * The disassembler does not care about the order of the opcodes + except in cases where implicit addressing is used. + + Here are the rules for ordering the opcodes of a mnemonic: + + 1) Opcodes with FLAG_STRICT should precede opcodes without + FLAG_STRICT. + + 2) Opcodes with FLAG_STRICT should be ordered as follows: + register index opcodes, short immediate opcodes, and finally + long immediate opcodes. When both pa10 and pa11 variants + of the same opcode are available, the pa10 opcode should + come first for correct architectural promotion. + + 3) When implicit addressing is available for an opcode, the + implicit opcode should precede the explicit opcode. + + 4) Opcodes without FLAG_STRICT should be ordered as follows: + register index opcodes, long immediate opcodes, and finally + short immediate opcodes. */ + +static const struct pa_opcode pa_opcodes[] = +{ + +/* Pseudo-instructions. */ + +{ "ldi", 0x34000000, 0xffe00000, "l,x", pa20w, 0},/* ldo val(r0),r */ +{ "ldi", 0x34000000, 0xffe0c000, "j,x", pa10, 0},/* ldo val(r0),r */ + +{ "cmpib", 0xec000000, 0xfc000000, "?Qn5,b,w", pa20, FLAG_STRICT}, +{ "cmpib", 0x84000000, 0xf4000000, "?nn5,b,w", pa10, FLAG_STRICT}, +{ "comib", 0x84000000, 0xfc000000, "?nn5,b,w", pa10, 0}, /* comib{tf}*/ +/* This entry is for the disassembler only. It will never be used by + assembler. */ +{ "comib", 0x8c000000, 0xfc000000, "?nn5,b,w", pa10, 0}, /* comib{tf}*/ +{ "cmpb", 0x9c000000, 0xdc000000, "?Nnx,b,w", pa20, FLAG_STRICT}, +{ "cmpb", 0x80000000, 0xf4000000, "?nnx,b,w", pa10, FLAG_STRICT}, +{ "comb", 0x80000000, 0xfc000000, "?nnx,b,w", pa10, 0}, /* comb{tf} */ +/* This entry is for the disassembler only. It will never be used by + assembler. */ +{ "comb", 0x88000000, 0xfc000000, "?nnx,b,w", pa10, 0}, /* comb{tf} */ +{ "addb", 0xa0000000, 0xf4000000, "?Wnx,b,w", pa20w, FLAG_STRICT}, +{ "addb", 0xa0000000, 0xfc000000, "?@nx,b,w", pa10, 0}, /* addb{tf} */ +/* This entry is for the disassembler only. It will never be used by + assembler. */ +{ "addb", 0xa8000000, 0xfc000000, "?@nx,b,w", pa10, 0}, +{ "addib", 0xa4000000, 0xf4000000, "?Wn5,b,w", pa20w, FLAG_STRICT}, +{ "addib", 0xa4000000, 0xfc000000, "?@n5,b,w", pa10, 0}, /* addib{tf}*/ +/* This entry is for the disassembler only. It will never be used by + assembler. */ +{ "addib", 0xac000000, 0xfc000000, "?@n5,b,w", pa10, 0}, /* addib{tf}*/ +{ "nop", 0x08000240, 0xffffffff, "", pa10, 0}, /* or 0,0,0 */ +{ "copy", 0x08000240, 0xffe0ffe0, "x,t", pa10, 0}, /* or r,0,t */ +{ "mtsar", 0x01601840, 0xffe0ffff, "x", pa10, 0}, /* mtctl r,cr11 */ + +/* Loads and Stores for integer registers. */ + +{ "ldd", 0x0c0000c0, 0xfc00d3c0, "cxccx(b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0000c0, 0xfc0013c0, "cxccx(s,b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0010e0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0010e0, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0010c0, 0xfc00d3c0, "cmcc5(b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0010c0, 0xfc0013c0, "cmcc5(s,b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x50000000, 0xfc000002, "cq&(b),x", pa20w, FLAG_STRICT}, +{ "ldd", 0x50000000, 0xfc00c002, "cq#(b),x", pa20, FLAG_STRICT}, +{ "ldd", 0x50000000, 0xfc000002, "cq#(s,b),x", pa20, FLAG_STRICT}, +{ "ldw", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldw", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldw", 0x0c000080, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldw", 0x0c000080, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldw", 0x0c0010a0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldw", 0x0c0010a0, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT}, +{ "ldw", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldw", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldw", 0x0c001080, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldw", 0x0c001080, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldw", 0x4c000000, 0xfc000000, "ce<(b),x", pa20w, FLAG_STRICT}, +{ "ldw", 0x5c000004, 0xfc000006, "ce>(b),x", pa20w, FLAG_STRICT}, +{ "ldw", 0x48000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT}, +{ "ldw", 0x5c000004, 0xfc00c006, "ceK(b),x", pa20, FLAG_STRICT}, +{ "ldw", 0x5c000004, 0xfc000006, "ceK(s,b),x", pa20, FLAG_STRICT}, +{ "ldw", 0x4c000000, 0xfc00c000, "ceJ(b),x", pa10, FLAG_STRICT}, +{ "ldw", 0x4c000000, 0xfc000000, "ceJ(s,b),x", pa10, FLAG_STRICT}, +{ "ldw", 0x48000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldw", 0x48000000, 0xfc000000, "j(s,b),x", pa10, 0}, +{ "ldh", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldh", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldh", 0x0c000040, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldh", 0x0c000040, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldh", 0x0c001060, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldh", 0x0c001060, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT}, +{ "ldh", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldh", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldh", 0x0c001040, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldh", 0x0c001040, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldh", 0x44000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT}, +{ "ldh", 0x44000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldh", 0x44000000, 0xfc000000, "j(s,b),x", pa10, 0}, +{ "ldb", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldb", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldb", 0x0c000000, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldb", 0x0c000000, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldb", 0x0c001020, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldb", 0x0c001020, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT}, +{ "ldb", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldb", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldb", 0x0c001000, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldb", 0x0c001000, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldb", 0x40000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT}, +{ "ldb", 0x40000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldb", 0x40000000, 0xfc000000, "j(s,b),x", pa10, 0}, +{ "std", 0x0c0012e0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "std", 0x0c0012e0, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT}, +{ "std", 0x0c0012c0, 0xfc00d3c0, "cmcCx,V(b)", pa20, FLAG_STRICT}, +{ "std", 0x0c0012c0, 0xfc0013c0, "cmcCx,V(s,b)", pa20, FLAG_STRICT}, +{ "std", 0x70000000, 0xfc000002, "cqx,&(b)", pa20w, FLAG_STRICT}, +{ "std", 0x70000000, 0xfc00c002, "cqx,#(b)", pa20, FLAG_STRICT}, +{ "std", 0x70000000, 0xfc000002, "cqx,#(s,b)", pa20, FLAG_STRICT}, +{ "stw", 0x0c0012a0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "stw", 0x0c0012a0, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT}, +{ "stw", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stw", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "stw", 0x0c001280, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stw", 0x0c001280, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stw", 0x6c000000, 0xfc000000, "cex,<(b)", pa20w, FLAG_STRICT}, +{ "stw", 0x7c000004, 0xfc000006, "cex,>(b)", pa20w, FLAG_STRICT}, +{ "stw", 0x68000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT}, +{ "stw", 0x7c000004, 0xfc00c006, "cex,K(b)", pa20, FLAG_STRICT}, +{ "stw", 0x7c000004, 0xfc000006, "cex,K(s,b)", pa20, FLAG_STRICT}, +{ "stw", 0x6c000000, 0xfc00c000, "cex,J(b)", pa10, FLAG_STRICT}, +{ "stw", 0x6c000000, 0xfc000000, "cex,J(s,b)", pa10, FLAG_STRICT}, +{ "stw", 0x68000000, 0xfc00c000, "x,j(b)", pa10, 0}, +{ "stw", 0x68000000, 0xfc000000, "x,j(s,b)", pa10, 0}, +{ "sth", 0x0c001260, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "sth", 0x0c001260, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT}, +{ "sth", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "sth", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "sth", 0x0c001240, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "sth", 0x0c001240, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "sth", 0x64000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT}, +{ "sth", 0x64000000, 0xfc00c000, "x,j(b)", pa10, 0}, +{ "sth", 0x64000000, 0xfc000000, "x,j(s,b)", pa10, 0}, +{ "stb", 0x0c001220, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "stb", 0x0c001220, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT}, +{ "stb", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stb", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "stb", 0x0c001200, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stb", 0x0c001200, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stb", 0x60000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT}, +{ "stb", 0x60000000, 0xfc00c000, "x,j(b)", pa10, 0}, +{ "stb", 0x60000000, 0xfc000000, "x,j(s,b)", pa10, 0}, +{ "ldwm", 0x4c000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldwm", 0x4c000000, 0xfc000000, "j(s,b),x", pa10, 0}, +{ "stwm", 0x6c000000, 0xfc00c000, "x,j(b)", pa10, 0}, +{ "stwm", 0x6c000000, 0xfc000000, "x,j(s,b)", pa10, 0}, +{ "ldwx", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldwx", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldwx", 0x0c000080, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldwx", 0x0c000080, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldwx", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldwx", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, 0}, +{ "ldhx", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldhx", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldhx", 0x0c000040, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldhx", 0x0c000040, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldhx", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldhx", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, 0}, +{ "ldbx", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldbx", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldbx", 0x0c000000, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldbx", 0x0c000000, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldbx", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldbx", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, 0}, +{ "ldwa", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldwa", 0x0c000180, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldwa", 0x0c0011a0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldwa", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldwa", 0x0c001180, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldcw", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldcw", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldcw", 0x0c0001c0, 0xfc00d3c0, "cxcdx(b),t", pa11, FLAG_STRICT}, +{ "ldcw", 0x0c0001c0, 0xfc0013c0, "cxcdx(s,b),t", pa11, FLAG_STRICT}, +{ "ldcw", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldcw", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldcw", 0x0c0011c0, 0xfc00d3c0, "cmcd5(b),t", pa11, FLAG_STRICT}, +{ "ldcw", 0x0c0011c0, 0xfc0013c0, "cmcd5(s,b),t", pa11, FLAG_STRICT}, +{ "stwa", 0x0c0013a0, 0xfc00d3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "stwa", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stwa", 0x0c001380, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stby", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, FLAG_STRICT}, +{ "stby", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, FLAG_STRICT}, +{ "stby", 0x0c001300, 0xfc00d3c0, "cscCx,V(b)", pa11, FLAG_STRICT}, +{ "stby", 0x0c001300, 0xfc0013c0, "cscCx,V(s,b)", pa11, FLAG_STRICT}, +{ "ldda", 0x0c000100, 0xfc00d3c0, "cxccx(b),t", pa20, FLAG_STRICT}, +{ "ldda", 0x0c001120, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldda", 0x0c001100, 0xfc00d3c0, "cmcc5(b),t", pa20, FLAG_STRICT}, +{ "ldcd", 0x0c000140, 0xfc00d3c0, "cxcdx(b),t", pa20, FLAG_STRICT}, +{ "ldcd", 0x0c000140, 0xfc0013c0, "cxcdx(s,b),t", pa20, FLAG_STRICT}, +{ "ldcd", 0x0c001140, 0xfc00d3c0, "cmcd5(b),t", pa20, FLAG_STRICT}, +{ "ldcd", 0x0c001140, 0xfc0013c0, "cmcd5(s,b),t", pa20, FLAG_STRICT}, +{ "stda", 0x0c0013e0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "stda", 0x0c0013c0, 0xfc00d3c0, "cmcCx,V(b)", pa20, FLAG_STRICT}, +{ "ldwax", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldwax", 0x0c000180, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldwax", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldcwx", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldcwx", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldcwx", 0x0c0001c0, 0xfc00d3c0, "cxcdx(b),t", pa11, FLAG_STRICT}, +{ "ldcwx", 0x0c0001c0, 0xfc0013c0, "cxcdx(s,b),t", pa11, FLAG_STRICT}, +{ "ldcwx", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldcwx", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, 0}, +{ "ldws", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldws", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldws", 0x0c001080, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldws", 0x0c001080, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldws", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldws", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, 0}, +{ "ldhs", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldhs", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldhs", 0x0c001040, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldhs", 0x0c001040, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldhs", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldhs", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, 0}, +{ "ldbs", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldbs", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldbs", 0x0c001000, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldbs", 0x0c001000, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldbs", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldbs", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, 0}, +{ "ldwas", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldwas", 0x0c001180, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldwas", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldcws", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldcws", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldcws", 0x0c0011c0, 0xfc00d3c0, "cmcd5(b),t", pa11, FLAG_STRICT}, +{ "ldcws", 0x0c0011c0, 0xfc0013c0, "cmcd5(s,b),t", pa11, FLAG_STRICT}, +{ "ldcws", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldcws", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, 0}, +{ "stws", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stws", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "stws", 0x0c001280, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stws", 0x0c001280, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stws", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, 0}, +{ "stws", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, 0}, +{ "sths", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "sths", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "sths", 0x0c001240, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "sths", 0x0c001240, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "sths", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, 0}, +{ "sths", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, 0}, +{ "stbs", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stbs", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "stbs", 0x0c001200, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stbs", 0x0c001200, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stbs", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, 0}, +{ "stbs", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, 0}, +{ "stwas", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stwas", 0x0c001380, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stwas", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, 0}, +{ "stdby", 0x0c001340, 0xfc00d3c0, "cscCx,V(b)", pa20, FLAG_STRICT}, +{ "stdby", 0x0c001340, 0xfc0013c0, "cscCx,V(s,b)", pa20, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc00d3c0, "cscCx,V(b)", pa11, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc0013c0, "cscCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, 0}, +{ "stbys", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, 0}, + +/* Immediate instructions. */ +{ "ldo", 0x34000000, 0xfc000000, "l(b),x", pa20w, 0}, +{ "ldo", 0x34000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldil", 0x20000000, 0xfc000000, "k,b", pa10, 0}, +{ "addil", 0x28000000, 0xfc000000, "k,b,Z", pa10, 0}, +{ "addil", 0x28000000, 0xfc000000, "k,b", pa10, 0}, + +/* Branching instructions. */ +{ "b", 0xe8008000, 0xfc00e000, "cpnXL", pa20, FLAG_STRICT}, +{ "b", 0xe800a000, 0xfc00e000, "clnXL", pa20, FLAG_STRICT}, +{ "b", 0xe8000000, 0xfc00e000, "clnW,b", pa10, FLAG_STRICT}, +{ "b", 0xe8002000, 0xfc00e000, "cgnW,b", pa10, FLAG_STRICT}, +{ "b", 0xe8000000, 0xffe0e000, "nW", pa10, 0}, /* b,l foo,r0 */ +{ "bl", 0xe8000000, 0xfc00e000, "nW,b", pa10, 0}, +{ "gate", 0xe8002000, 0xfc00e000, "nW,b", pa10, 0}, +{ "blr", 0xe8004000, 0xfc00e001, "nx,b", pa10, 0}, +{ "bv", 0xe800c000, 0xfc00fffd, "nx(b)", pa10, 0}, +{ "bv", 0xe800c000, 0xfc00fffd, "n(b)", pa10, 0}, +{ "bve", 0xe800f001, 0xfc1ffffd, "cpn(b)L", pa20, FLAG_STRICT}, +{ "bve", 0xe800f000, 0xfc1ffffd, "cln(b)L", pa20, FLAG_STRICT}, +{ "bve", 0xe800d001, 0xfc1ffffd, "cPn(b)", pa20, FLAG_STRICT}, +{ "bve", 0xe800d000, 0xfc1ffffd, "n(b)", pa20, FLAG_STRICT}, +{ "be", 0xe4000000, 0xfc000000, "clnz(S,b),Y", pa10, FLAG_STRICT}, +{ "be", 0xe4000000, 0xfc000000, "clnz(b),Y", pa10, FLAG_STRICT}, +{ "be", 0xe0000000, 0xfc000000, "nz(S,b)", pa10, 0}, +{ "be", 0xe0000000, 0xfc000000, "nz(b)", pa10, 0}, +{ "ble", 0xe4000000, 0xfc000000, "nz(S,b)", pa10, 0}, +{ "movb", 0xc8000000, 0xfc000000, "?ynx,b,w", pa10, 0}, +{ "movib", 0xcc000000, 0xfc000000, "?yn5,b,w", pa10, 0}, +{ "combt", 0x80000000, 0xfc000000, "?tnx,b,w", pa10, 0}, +{ "combf", 0x88000000, 0xfc000000, "?tnx,b,w", pa10, 0}, +{ "comibt", 0x84000000, 0xfc000000, "?tn5,b,w", pa10, 0}, +{ "comibf", 0x8c000000, 0xfc000000, "?tn5,b,w", pa10, 0}, +{ "addbt", 0xa0000000, 0xfc000000, "?dnx,b,w", pa10, 0}, +{ "addbf", 0xa8000000, 0xfc000000, "?dnx,b,w", pa10, 0}, +{ "addibt", 0xa4000000, 0xfc000000, "?dn5,b,w", pa10, 0}, +{ "addibf", 0xac000000, 0xfc000000, "?dn5,b,w", pa10, 0}, +{ "bb", 0xc0004000, 0xffe06000, "?bnx,!,w", pa10, FLAG_STRICT}, +{ "bb", 0xc0006000, 0xffe06000, "?Bnx,!,w", pa20, FLAG_STRICT}, +{ "bb", 0xc4004000, 0xfc006000, "?bnx,Q,w", pa10, FLAG_STRICT}, +{ "bb", 0xc4004000, 0xfc004000, "?Bnx,B,w", pa20, FLAG_STRICT}, +{ "bvb", 0xc0004000, 0xffe04000, "?bnx,w", pa10, 0}, +{ "clrbts", 0xe8004005, 0xffffffff, "", pa20, FLAG_STRICT}, +{ "popbts", 0xe8004005, 0xfffff007, "$", pa20, FLAG_STRICT}, +{ "pushnom", 0xe8004001, 0xffffffff, "", pa20, FLAG_STRICT}, +{ "pushbts", 0xe8004001, 0xffe0ffff, "x", pa20, FLAG_STRICT}, + +/* Computation Instructions. */ + +{ "cmpclr", 0x080008a0, 0xfc000fe0, "?Sx,b,t", pa20, FLAG_STRICT}, +{ "cmpclr", 0x08000880, 0xfc000fe0, "?sx,b,t", pa10, FLAG_STRICT}, +{ "comclr", 0x08000880, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "or", 0x08000260, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT}, +{ "or", 0x08000240, 0xfc000fe0, "?lx,b,t", pa10, 0}, +{ "xor", 0x080002a0, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT}, +{ "xor", 0x08000280, 0xfc000fe0, "?lx,b,t", pa10, 0}, +{ "and", 0x08000220, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT}, +{ "and", 0x08000200, 0xfc000fe0, "?lx,b,t", pa10, 0}, +{ "andcm", 0x08000020, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT}, +{ "andcm", 0x08000000, 0xfc000fe0, "?lx,b,t", pa10, 0}, +{ "uxor", 0x080003a0, 0xfc000fe0, "?Ux,b,t", pa20, FLAG_STRICT}, +{ "uxor", 0x08000380, 0xfc000fe0, "?ux,b,t", pa10, 0}, +{ "uaddcm", 0x080009a0, 0xfc000fa0, "cT?Ux,b,t", pa20, FLAG_STRICT}, +{ "uaddcm", 0x08000980, 0xfc000fa0, "cT?ux,b,t", pa10, FLAG_STRICT}, +{ "uaddcm", 0x08000980, 0xfc000fe0, "?ux,b,t", pa10, 0}, +{ "uaddcmt", 0x080009c0, 0xfc000fe0, "?ux,b,t", pa10, 0}, +{ "dcor", 0x08000ba0, 0xfc1f0fa0, "ci?Ub,t", pa20, FLAG_STRICT}, +{ "dcor", 0x08000b80, 0xfc1f0fa0, "ci?ub,t", pa10, FLAG_STRICT}, +{ "dcor", 0x08000b80, 0xfc1f0fe0, "?ub,t", pa10, 0}, +{ "idcor", 0x08000bc0, 0xfc1f0fe0, "?ub,t", pa10, 0}, +{ "addi", 0xb0000000, 0xfc000000, "ct?ai,b,x", pa10, FLAG_STRICT}, +{ "addi", 0xb4000000, 0xfc000000, "cv?ai,b,x", pa10, FLAG_STRICT}, +{ "addi", 0xb4000000, 0xfc000800, "?ai,b,x", pa10, 0}, +{ "addio", 0xb4000800, 0xfc000800, "?ai,b,x", pa10, 0}, +{ "addit", 0xb0000000, 0xfc000800, "?ai,b,x", pa10, 0}, +{ "addito", 0xb0000800, 0xfc000800, "?ai,b,x", pa10, 0}, +{ "add", 0x08000720, 0xfc0007e0, "cY?Ax,b,t", pa20, FLAG_STRICT}, +{ "add", 0x08000700, 0xfc0007e0, "cy?ax,b,t", pa10, FLAG_STRICT}, +{ "add", 0x08000220, 0xfc0003e0, "ca?Ax,b,t", pa20, FLAG_STRICT}, +{ "add", 0x08000200, 0xfc0003e0, "ca?ax,b,t", pa10, FLAG_STRICT}, +{ "add", 0x08000600, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "addl", 0x08000a00, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "addo", 0x08000e00, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "addc", 0x08000700, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "addco", 0x08000f00, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sub", 0x080004e0, 0xfc0007e0, "ct?Sx,b,t", pa20, FLAG_STRICT}, +{ "sub", 0x080004c0, 0xfc0007e0, "ct?sx,b,t", pa10, FLAG_STRICT}, +{ "sub", 0x08000520, 0xfc0007e0, "cB?Sx,b,t", pa20, FLAG_STRICT}, +{ "sub", 0x08000500, 0xfc0007e0, "cb?sx,b,t", pa10, FLAG_STRICT}, +{ "sub", 0x08000420, 0xfc0007e0, "cv?Sx,b,t", pa20, FLAG_STRICT}, +{ "sub", 0x08000400, 0xfc0007e0, "cv?sx,b,t", pa10, FLAG_STRICT}, +{ "sub", 0x08000400, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subo", 0x08000c00, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subb", 0x08000500, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subbo", 0x08000d00, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subt", 0x080004c0, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subto", 0x08000cc0, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "ds", 0x08000440, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subi", 0x94000000, 0xfc000000, "cv?si,b,x", pa10, FLAG_STRICT}, +{ "subi", 0x94000000, 0xfc000800, "?si,b,x", pa10, 0}, +{ "subio", 0x94000800, 0xfc000800, "?si,b,x", pa10, 0}, +{ "cmpiclr", 0x90000800, 0xfc000800, "?Si,b,x", pa20, FLAG_STRICT}, +{ "cmpiclr", 0x90000000, 0xfc000800, "?si,b,x", pa10, FLAG_STRICT}, +{ "comiclr", 0x90000000, 0xfc000800, "?si,b,x", pa10, 0}, +{ "shladd", 0x08000220, 0xfc000320, "ca?Ax,.,b,t", pa20, FLAG_STRICT}, +{ "shladd", 0x08000200, 0xfc000320, "ca?ax,.,b,t", pa10, FLAG_STRICT}, +{ "sh1add", 0x08000640, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh1addl", 0x08000a40, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh1addo", 0x08000e40, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh2add", 0x08000680, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh2addl", 0x08000a80, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh2addo", 0x08000e80, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh3add", 0x080006c0, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh3addl", 0x08000ac0, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh3addo", 0x08000ec0, 0xfc000fe0, "?ax,b,t", pa10, 0}, + +/* Subword Operation Instructions. */ + +{ "hadd", 0x08000300, 0xfc00ff20, "cHx,b,t", pa20, FLAG_STRICT}, +{ "havg", 0x080002c0, 0xfc00ffe0, "x,b,t", pa20, FLAG_STRICT}, +{ "hshl", 0xf8008800, 0xffe0fc20, "x,*,t", pa20, FLAG_STRICT}, +{ "hshladd", 0x08000700, 0xfc00ff20, "x,.,b,t", pa20, FLAG_STRICT}, +{ "hshr", 0xf800c800, 0xfc1ff820, "cSb,*,t", pa20, FLAG_STRICT}, +{ "hshradd", 0x08000500, 0xfc00ff20, "x,.,b,t", pa20, FLAG_STRICT}, +{ "hsub", 0x08000100, 0xfc00ff20, "cHx,b,t", pa20, FLAG_STRICT}, +{ "mixh", 0xf8008400, 0xfc009fe0, "chx,b,t", pa20, FLAG_STRICT}, +{ "mixw", 0xf8008000, 0xfc009fe0, "chx,b,t", pa20, FLAG_STRICT}, +{ "permh", 0xf8000000, 0xfc009020, "c*a,t", pa20, FLAG_STRICT}, + + +/* Extract and Deposit Instructions. */ + +{ "shrpd", 0xd0000200, 0xfc001fe0, "?Xx,b,!,t", pa20, FLAG_STRICT}, +{ "shrpd", 0xd0000400, 0xfc001400, "?Xx,b,~,t", pa20, FLAG_STRICT}, +{ "shrpw", 0xd0000000, 0xfc001fe0, "?xx,b,!,t", pa10, FLAG_STRICT}, +{ "shrpw", 0xd0000800, 0xfc001c00, "?xx,b,p,t", pa10, FLAG_STRICT}, +{ "vshd", 0xd0000000, 0xfc001fe0, "?xx,b,t", pa10, 0}, +{ "shd", 0xd0000800, 0xfc001c00, "?xx,b,p,t", pa10, 0}, +{ "extrd", 0xd0001200, 0xfc001ae0, "cS?Xb,!,%,x", pa20, FLAG_STRICT}, +{ "extrd", 0xd8000000, 0xfc000000, "cS?Xb,q,|,x", pa20, FLAG_STRICT}, +{ "extrw", 0xd0001000, 0xfc001be0, "cS?xb,!,T,x", pa10, FLAG_STRICT}, +{ "extrw", 0xd0001800, 0xfc001800, "cS?xb,P,T,x", pa10, FLAG_STRICT}, +{ "vextru", 0xd0001000, 0xfc001fe0, "?xb,T,x", pa10, 0}, +{ "vextrs", 0xd0001400, 0xfc001fe0, "?xb,T,x", pa10, 0}, +{ "extru", 0xd0001800, 0xfc001c00, "?xb,P,T,x", pa10, 0}, +{ "extrs", 0xd0001c00, 0xfc001c00, "?xb,P,T,x", pa10, 0}, +{ "depd", 0xd4000200, 0xfc001ae0, "cz?Xx,!,%,b", pa20, FLAG_STRICT}, +{ "depd", 0xf0000000, 0xfc000000, "cz?Xx,~,|,b", pa20, FLAG_STRICT}, +{ "depdi", 0xd4001200, 0xfc001ae0, "cz?X5,!,%,b", pa20, FLAG_STRICT}, +{ "depdi", 0xf4000000, 0xfc000000, "cz?X5,~,|,b", pa20, FLAG_STRICT}, +{ "depw", 0xd4000000, 0xfc001be0, "cz?xx,!,T,b", pa10, FLAG_STRICT}, +{ "depw", 0xd4000800, 0xfc001800, "cz?xx,p,T,b", pa10, FLAG_STRICT}, +{ "depwi", 0xd4001000, 0xfc001be0, "cz?x5,!,T,b", pa10, FLAG_STRICT}, +{ "depwi", 0xd4001800, 0xfc001800, "cz?x5,p,T,b", pa10, FLAG_STRICT}, +{ "zvdep", 0xd4000000, 0xfc001fe0, "?xx,T,b", pa10, 0}, +{ "vdep", 0xd4000400, 0xfc001fe0, "?xx,T,b", pa10, 0}, +{ "zdep", 0xd4000800, 0xfc001c00, "?xx,p,T,b", pa10, 0}, +{ "dep", 0xd4000c00, 0xfc001c00, "?xx,p,T,b", pa10, 0}, +{ "zvdepi", 0xd4001000, 0xfc001fe0, "?x5,T,b", pa10, 0}, +{ "vdepi", 0xd4001400, 0xfc001fe0, "?x5,T,b", pa10, 0}, +{ "zdepi", 0xd4001800, 0xfc001c00, "?x5,p,T,b", pa10, 0}, +{ "depi", 0xd4001c00, 0xfc001c00, "?x5,p,T,b", pa10, 0}, + +/* System Control Instructions. */ + +{ "break", 0x00000000, 0xfc001fe0, "r,A", pa10, 0}, +{ "rfi", 0x00000c00, 0xffffff1f, "cr", pa10, FLAG_STRICT}, +{ "rfi", 0x00000c00, 0xffffffff, "", pa10, 0}, +{ "rfir", 0x00000ca0, 0xffffffff, "", pa11, 0}, +{ "ssm", 0x00000d60, 0xfc00ffe0, "U,t", pa20, FLAG_STRICT}, +{ "ssm", 0x00000d60, 0xffe0ffe0, "R,t", pa10, 0}, +{ "rsm", 0x00000e60, 0xfc00ffe0, "U,t", pa20, FLAG_STRICT}, +{ "rsm", 0x00000e60, 0xffe0ffe0, "R,t", pa10, 0}, +{ "mtsm", 0x00001860, 0xffe0ffff, "x", pa10, 0}, +{ "ldsid", 0x000010a0, 0xfc1fffe0, "(b),t", pa10, 0}, +{ "ldsid", 0x000010a0, 0xfc1f3fe0, "(s,b),t", pa10, 0}, +{ "mtsp", 0x00001820, 0xffe01fff, "x,S", pa10, 0}, +{ "mtctl", 0x00001840, 0xfc00ffff, "x,^", pa10, 0}, +{ "mtsarcm", 0x016018C0, 0xffe0ffff, "x", pa20, FLAG_STRICT}, +{ "mfia", 0x000014A0, 0xffffffe0, "t", pa20, FLAG_STRICT}, +{ "mfsp", 0x000004a0, 0xffff1fe0, "S,t", pa10, 0}, +{ "mfctl", 0x016048a0, 0xffffffe0, "cW!,t", pa20, FLAG_STRICT}, +{ "mfctl", 0x000008a0, 0xfc1fffe0, "^,t", pa10, 0}, +{ "sync", 0x00000400, 0xffffffff, "", pa10, 0}, +{ "syncdma", 0x00100400, 0xffffffff, "", pa10, 0}, +{ "probe", 0x04001180, 0xfc00ffa0, "cw(b),x,t", pa10, FLAG_STRICT}, +{ "probe", 0x04001180, 0xfc003fa0, "cw(s,b),x,t", pa10, FLAG_STRICT}, +{ "probei", 0x04003180, 0xfc00ffa0, "cw(b),R,t", pa10, FLAG_STRICT}, +{ "probei", 0x04003180, 0xfc003fa0, "cw(s,b),R,t", pa10, FLAG_STRICT}, +{ "prober", 0x04001180, 0xfc00ffe0, "(b),x,t", pa10, 0}, +{ "prober", 0x04001180, 0xfc003fe0, "(s,b),x,t", pa10, 0}, +{ "proberi", 0x04003180, 0xfc00ffe0, "(b),R,t", pa10, 0}, +{ "proberi", 0x04003180, 0xfc003fe0, "(s,b),R,t", pa10, 0}, +{ "probew", 0x040011c0, 0xfc00ffe0, "(b),x,t", pa10, 0}, +{ "probew", 0x040011c0, 0xfc003fe0, "(s,b),x,t", pa10, 0}, +{ "probewi", 0x040031c0, 0xfc00ffe0, "(b),R,t", pa10, 0}, +{ "probewi", 0x040031c0, 0xfc003fe0, "(s,b),R,t", pa10, 0}, +{ "lpa", 0x04001340, 0xfc00ffc0, "cZx(b),t", pa10, 0}, +{ "lpa", 0x04001340, 0xfc003fc0, "cZx(s,b),t", pa10, 0}, +{ "lci", 0x04001300, 0xfc00ffe0, "x(b),t", pa11, 0}, +{ "lci", 0x04001300, 0xfc003fe0, "x(s,b),t", pa11, 0}, +{ "pdtlb", 0x04001600, 0xfc00ffdf, "cLcZx(b)", pa20, FLAG_STRICT}, +{ "pdtlb", 0x04001600, 0xfc003fdf, "cLcZx(s,b)", pa20, FLAG_STRICT}, +{ "pdtlb", 0x04001600, 0xfc1fffdf, "cLcZ@(b)", pa20, FLAG_STRICT}, +{ "pdtlb", 0x04001600, 0xfc1f3fdf, "cLcZ@(s,b)", pa20, FLAG_STRICT}, +{ "pdtlb", 0x04001200, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "pdtlb", 0x04001200, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "pitlb", 0x04000600, 0xfc001fdf, "cLcZx(S,b)", pa20, FLAG_STRICT}, +{ "pitlb", 0x04000600, 0xfc1f1fdf, "cLcZ@(S,b)", pa20, FLAG_STRICT}, +{ "pitlb", 0x04000200, 0xfc001fdf, "cZx(S,b)", pa10, 0}, +{ "pdtlbe", 0x04001240, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "pdtlbe", 0x04001240, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "pitlbe", 0x04000240, 0xfc001fdf, "cZx(S,b)", pa10, 0}, +{ "idtlba", 0x04001040, 0xfc00ffff, "x,(b)", pa10, 0}, +{ "idtlba", 0x04001040, 0xfc003fff, "x,(s,b)", pa10, 0}, +{ "iitlba", 0x04000040, 0xfc001fff, "x,(S,b)", pa10, 0}, +{ "idtlbp", 0x04001000, 0xfc00ffff, "x,(b)", pa10, 0}, +{ "idtlbp", 0x04001000, 0xfc003fff, "x,(s,b)", pa10, 0}, +{ "iitlbp", 0x04000000, 0xfc001fff, "x,(S,b)", pa10, 0}, +{ "pdc", 0x04001380, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "pdc", 0x04001380, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "fdc", 0x04001280, 0xfc00ffdf, "cZx(b)", pa10, FLAG_STRICT}, +{ "fdc", 0x04001280, 0xfc003fdf, "cZx(s,b)", pa10, FLAG_STRICT}, +{ "fdc", 0x04003280, 0xfc00ffff, "5(b)", pa20, FLAG_STRICT}, +{ "fdc", 0x04003280, 0xfc003fff, "5(s,b)", pa20, FLAG_STRICT}, +{ "fdc", 0x04001280, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "fdc", 0x04001280, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "fic", 0x040013c0, 0xfc00dfdf, "cZx(b)", pa20, FLAG_STRICT}, +{ "fic", 0x04000280, 0xfc001fdf, "cZx(S,b)", pa10, 0}, +{ "fdce", 0x040012c0, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "fdce", 0x040012c0, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "fice", 0x040002c0, 0xfc001fdf, "cZx(S,b)", pa10, 0}, +{ "diag", 0x14000000, 0xfc000000, "D", pa10, 0}, +{ "idtlbt", 0x04001800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT}, +{ "iitlbt", 0x04000800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT}, + +/* These may be specific to certain versions of the PA. Joel claimed + they were 72000 (7200?) specific. However, I'm almost certain the + mtcpu/mfcpu were undocumented, but available in the older 700 machines. */ +{ "mtcpu", 0x14001600, 0xfc00ffff, "x,^", pa10, 0}, +{ "mfcpu", 0x14001A00, 0xfc00ffff, "^,x", pa10, 0}, +{ "tocen", 0x14403600, 0xffffffff, "", pa10, 0}, +{ "tocdis", 0x14401620, 0xffffffff, "", pa10, 0}, +{ "shdwgr", 0x14402600, 0xffffffff, "", pa10, 0}, +{ "grshdw", 0x14400620, 0xffffffff, "", pa10, 0}, + +/* gfw and gfr are not in the HP PA 1.1 manual, but they are in either + the Timex FPU or the Mustang ERS (not sure which) manual. */ +{ "gfw", 0x04001680, 0xfc00ffdf, "cZx(b)", pa11, 0}, +{ "gfw", 0x04001680, 0xfc003fdf, "cZx(s,b)", pa11, 0}, +{ "gfr", 0x04001a80, 0xfc00ffdf, "cZx(b)", pa11, 0}, +{ "gfr", 0x04001a80, 0xfc003fdf, "cZx(s,b)", pa11, 0}, + +/* Floating Point Coprocessor Instructions. */ + +{ "fldw", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, FLAG_STRICT}, +{ "fldw", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, FLAG_STRICT}, +{ "fldw", 0x24000000, 0xfc00d380, "cxccx(b),fT", pa11, FLAG_STRICT}, +{ "fldw", 0x24000000, 0xfc001380, "cxccx(s,b),fT", pa11, FLAG_STRICT}, +{ "fldw", 0x24001020, 0xfc1ff3a0, "cocc@(b),fT", pa20, FLAG_STRICT}, +{ "fldw", 0x24001020, 0xfc1f33a0, "cocc@(s,b),fT", pa20, FLAG_STRICT}, +{ "fldw", 0x24001000, 0xfc00df80, "cM5(b),fT", pa10, FLAG_STRICT}, +{ "fldw", 0x24001000, 0xfc001f80, "cM5(s,b),fT", pa10, FLAG_STRICT}, +{ "fldw", 0x24001000, 0xfc00d380, "cmcc5(b),fT", pa11, FLAG_STRICT}, +{ "fldw", 0x24001000, 0xfc001380, "cmcc5(s,b),fT", pa11, FLAG_STRICT}, +{ "fldw", 0x5c000000, 0xfc000004, "y(b),fe", pa20w, FLAG_STRICT}, +{ "fldw", 0x58000000, 0xfc000000, "cJy(b),fe", pa20w, FLAG_STRICT}, +{ "fldw", 0x5c000000, 0xfc00c004, "d(b),fe", pa20, FLAG_STRICT}, +{ "fldw", 0x5c000000, 0xfc000004, "d(s,b),fe", pa20, FLAG_STRICT}, +{ "fldw", 0x58000000, 0xfc00c000, "cJd(b),fe", pa20, FLAG_STRICT}, +{ "fldw", 0x58000000, 0xfc000000, "cJd(s,b),fe", pa20, FLAG_STRICT}, +{ "fldd", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, FLAG_STRICT}, +{ "fldd", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, FLAG_STRICT}, +{ "fldd", 0x2c000000, 0xfc00d3c0, "cxccx(b),ft", pa11, FLAG_STRICT}, +{ "fldd", 0x2c000000, 0xfc0013c0, "cxccx(s,b),ft", pa11, FLAG_STRICT}, +{ "fldd", 0x2c001020, 0xfc1ff3e0, "cocc@(b),ft", pa20, FLAG_STRICT}, +{ "fldd", 0x2c001020, 0xfc1f33e0, "cocc@(s,b),ft", pa20, FLAG_STRICT}, +{ "fldd", 0x2c001000, 0xfc00dfc0, "cM5(b),ft", pa10, FLAG_STRICT}, +{ "fldd", 0x2c001000, 0xfc001fc0, "cM5(s,b),ft", pa10, FLAG_STRICT}, +{ "fldd", 0x2c001000, 0xfc00d3c0, "cmcc5(b),ft", pa11, FLAG_STRICT}, +{ "fldd", 0x2c001000, 0xfc0013c0, "cmcc5(s,b),ft", pa11, FLAG_STRICT}, +{ "fldd", 0x50000002, 0xfc000002, "cq&(b),fx", pa20w, FLAG_STRICT}, +{ "fldd", 0x50000002, 0xfc00c002, "cq#(b),fx", pa20, FLAG_STRICT}, +{ "fldd", 0x50000002, 0xfc000002, "cq#(s,b),fx", pa20, FLAG_STRICT}, +{ "fstw", 0x24000200, 0xfc00df80, "cXfT,x(b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24000200, 0xfc001f80, "cXfT,x(s,b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24000200, 0xfc00d380, "cxcCfT,x(b)", pa11, FLAG_STRICT}, +{ "fstw", 0x24000200, 0xfc001380, "cxcCfT,x(s,b)", pa11, FLAG_STRICT}, +{ "fstw", 0x24001220, 0xfc1ff3a0, "cocCfT,@(b)", pa20, FLAG_STRICT}, +{ "fstw", 0x24001220, 0xfc1f33a0, "cocCfT,@(s,b)", pa20, FLAG_STRICT}, +{ "fstw", 0x24001200, 0xfc00df80, "cMfT,5(b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24001200, 0xfc001f80, "cMfT,5(s,b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24001200, 0xfc00df80, "cMfT,5(b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24001200, 0xfc001f80, "cMfT,5(s,b)", pa10, FLAG_STRICT}, +{ "fstw", 0x7c000000, 0xfc000004, "fE,y(b)", pa20w, FLAG_STRICT}, +{ "fstw", 0x78000000, 0xfc000000, "cJfE,y(b)", pa20w, FLAG_STRICT}, +{ "fstw", 0x7c000000, 0xfc00c004, "fE,d(b)", pa20, FLAG_STRICT}, +{ "fstw", 0x7c000000, 0xfc000004, "fE,d(s,b)", pa20, FLAG_STRICT}, +{ "fstw", 0x78000000, 0xfc00c000, "cJfE,d(b)", pa20, FLAG_STRICT}, +{ "fstw", 0x78000000, 0xfc000000, "cJfE,d(s,b)", pa20, FLAG_STRICT}, +{ "fstd", 0x2c000200, 0xfc00dfc0, "cXft,x(b)", pa10, FLAG_STRICT}, +{ "fstd", 0x2c000200, 0xfc001fc0, "cXft,x(s,b)", pa10, FLAG_STRICT}, +{ "fstd", 0x2c000200, 0xfc00d3c0, "cxcCft,x(b)", pa11, FLAG_STRICT}, +{ "fstd", 0x2c000200, 0xfc0013c0, "cxcCft,x(s,b)", pa11, FLAG_STRICT}, +{ "fstd", 0x2c001220, 0xfc1ff3e0, "cocCft,@(b)", pa20, FLAG_STRICT}, +{ "fstd", 0x2c001220, 0xfc1f33e0, "cocCft,@(s,b)", pa20, FLAG_STRICT}, +{ "fstd", 0x2c001200, 0xfc00dfc0, "cMft,5(b)", pa10, FLAG_STRICT}, +{ "fstd", 0x2c001200, 0xfc001fc0, "cMft,5(s,b)", pa10, FLAG_STRICT}, +{ "fstd", 0x2c001200, 0xfc00d3c0, "cmcCft,5(b)", pa11, FLAG_STRICT}, +{ "fstd", 0x2c001200, 0xfc0013c0, "cmcCft,5(s,b)", pa11, FLAG_STRICT}, +{ "fstd", 0x70000002, 0xfc000002, "cqfx,&(b)", pa20w, FLAG_STRICT}, +{ "fstd", 0x70000002, 0xfc00c002, "cqfx,#(b)", pa20, FLAG_STRICT}, +{ "fstd", 0x70000002, 0xfc000002, "cqfx,#(s,b)", pa20, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc00d380, "cxccx(b),fT", pa11, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc001380, "cxccx(s,b),fT", pa11, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, 0}, +{ "fldwx", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, 0}, +{ "flddx", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, FLAG_STRICT}, +{ "flddx", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, FLAG_STRICT}, +{ "flddx", 0x2c000000, 0xfc00d3c0, "cxccx(b),ft", pa11, FLAG_STRICT}, +{ "flddx", 0x2c000000, 0xfc0013c0, "cxccx(s,b),ft", pa11, FLAG_STRICT}, +{ "flddx", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, 0}, +{ "flddx", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, 0}, +{ "fstwx", 0x24000200, 0xfc00df80, "cxfT,x(b)", pa10, FLAG_STRICT}, +{ "fstwx", 0x24000200, 0xfc001f80, "cxfT,x(s,b)", pa10, FLAG_STRICT}, +{ "fstwx", 0x24000200, 0xfc00d380, "cxcCfT,x(b)", pa11, FLAG_STRICT}, +{ "fstwx", 0x24000200, 0xfc001380, "cxcCfT,x(s,b)", pa11, FLAG_STRICT}, +{ "fstwx", 0x24000200, 0xfc00df80, "cxfT,x(b)", pa10, 0}, +{ "fstwx", 0x24000200, 0xfc001f80, "cxfT,x(s,b)", pa10, 0}, +{ "fstdx", 0x2c000200, 0xfc00dfc0, "cxft,x(b)", pa10, FLAG_STRICT}, +{ "fstdx", 0x2c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, FLAG_STRICT}, +{ "fstdx", 0x2c000200, 0xfc00d3c0, "cxcCft,x(b)", pa11, FLAG_STRICT}, +{ "fstdx", 0x2c000200, 0xfc0013c0, "cxcCft,x(s,b)", pa11, FLAG_STRICT}, +{ "fstdx", 0x2c000200, 0xfc00dfc0, "cxft,x(b)", pa10, 0}, +{ "fstdx", 0x2c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, 0}, +{ "fstqx", 0x3c000200, 0xfc00dfc0, "cxft,x(b)", pa10, 0}, +{ "fstqx", 0x3c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, 0}, +{ "fldws", 0x24001000, 0xfc00df80, "cm5(b),fT", pa10, FLAG_STRICT}, +{ "fldws", 0x24001000, 0xfc001f80, "cm5(s,b),fT", pa10, FLAG_STRICT}, +{ "fldws", 0x24001000, 0xfc00d380, "cmcc5(b),fT", pa11, FLAG_STRICT}, +{ "fldws", 0x24001000, 0xfc001380, "cmcc5(s,b),fT", pa11, FLAG_STRICT}, +{ "fldws", 0x24001000, 0xfc00df80, "cm5(b),fT", pa10, 0}, +{ "fldws", 0x24001000, 0xfc001f80, "cm5(s,b),fT", pa10, 0}, +{ "fldds", 0x2c001000, 0xfc00dfc0, "cm5(b),ft", pa10, FLAG_STRICT}, +{ "fldds", 0x2c001000, 0xfc001fc0, "cm5(s,b),ft", pa10, FLAG_STRICT}, +{ "fldds", 0x2c001000, 0xfc00d3c0, "cmcc5(b),ft", pa11, FLAG_STRICT}, +{ "fldds", 0x2c001000, 0xfc0013c0, "cmcc5(s,b),ft", pa11, FLAG_STRICT}, +{ "fldds", 0x2c001000, 0xfc00dfc0, "cm5(b),ft", pa10, 0}, +{ "fldds", 0x2c001000, 0xfc001fc0, "cm5(s,b),ft", pa10, 0}, +{ "fstws", 0x24001200, 0xfc00df80, "cmfT,5(b)", pa10, FLAG_STRICT}, +{ "fstws", 0x24001200, 0xfc001f80, "cmfT,5(s,b)", pa10, FLAG_STRICT}, +{ "fstws", 0x24001200, 0xfc00d380, "cmcCfT,5(b)", pa11, FLAG_STRICT}, +{ "fstws", 0x24001200, 0xfc001380, "cmcCfT,5(s,b)", pa11, FLAG_STRICT}, +{ "fstws", 0x24001200, 0xfc00df80, "cmfT,5(b)", pa10, 0}, +{ "fstws", 0x24001200, 0xfc001f80, "cmfT,5(s,b)", pa10, 0}, +{ "fstds", 0x2c001200, 0xfc00dfc0, "cmft,5(b)", pa10, FLAG_STRICT}, +{ "fstds", 0x2c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, FLAG_STRICT}, +{ "fstds", 0x2c001200, 0xfc00d3c0, "cmcCft,5(b)", pa11, FLAG_STRICT}, +{ "fstds", 0x2c001200, 0xfc0013c0, "cmcCft,5(s,b)", pa11, FLAG_STRICT}, +{ "fstds", 0x2c001200, 0xfc00dfc0, "cmft,5(b)", pa10, 0}, +{ "fstds", 0x2c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, 0}, +{ "fstqs", 0x3c001200, 0xfc00dfc0, "cmft,5(b)", pa10, 0}, +{ "fstqs", 0x3c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, 0}, +{ "fadd", 0x30000600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "fadd", 0x38000600, 0xfc00e720, "IfA,fB,fT", pa10, 0}, +{ "fsub", 0x30002600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "fsub", 0x38002600, 0xfc00e720, "IfA,fB,fT", pa10, 0}, +{ "fmpy", 0x30004600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "fmpy", 0x38004600, 0xfc00e720, "IfA,fB,fT", pa10, 0}, +{ "fdiv", 0x30006600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "fdiv", 0x38006600, 0xfc00e720, "IfA,fB,fT", pa10, 0}, +{ "fsqrt", 0x30008000, 0xfc1fe7e0, "Ffa,fT", pa10, 0}, +{ "fsqrt", 0x38008000, 0xfc1fe720, "FfA,fT", pa10, 0}, +{ "fabs", 0x30006000, 0xfc1fe7e0, "Ffa,fT", pa10, 0}, +{ "fabs", 0x38006000, 0xfc1fe720, "FfA,fT", pa10, 0}, +{ "frem", 0x30008600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "frem", 0x38008600, 0xfc00e720, "FfA,fB,fT", pa10, 0}, +{ "frnd", 0x3000a000, 0xfc1fe7e0, "Ffa,fT", pa10, 0}, +{ "frnd", 0x3800a000, 0xfc1fe720, "FfA,fT", pa10, 0}, +{ "fcpy", 0x30004000, 0xfc1fe7e0, "Ffa,fT", pa10, 0}, +{ "fcpy", 0x38004000, 0xfc1fe720, "FfA,fT", pa10, 0}, +{ "fcnvff", 0x30000200, 0xfc1f87e0, "FGfa,fT", pa10, 0}, +{ "fcnvff", 0x38000200, 0xfc1f8720, "FGfA,fT", pa10, 0}, +{ "fcnvxf", 0x30008200, 0xfc1f87e0, "FGfa,fT", pa10, 0}, +{ "fcnvxf", 0x38008200, 0xfc1f8720, "FGfA,fT", pa10, 0}, +{ "fcnvfx", 0x30010200, 0xfc1f87e0, "FGfa,fT", pa10, 0}, +{ "fcnvfx", 0x38010200, 0xfc1f8720, "FGfA,fT", pa10, 0}, +{ "fcnvfxt", 0x30018200, 0xfc1f87e0, "FGfa,fT", pa10, 0}, +{ "fcnvfxt", 0x38018200, 0xfc1f8720, "FGfA,fT", pa10, 0}, +{ "fmpyfadd", 0xb8000000, 0xfc000020, "IfA,fB,fC,fT", pa20, FLAG_STRICT}, +{ "fmpynfadd", 0xb8000020, 0xfc000020, "IfA,fB,fC,fT", pa20, FLAG_STRICT}, +{ "fneg", 0x3000c000, 0xfc1fe7e0, "Ffa,fT", pa20, FLAG_STRICT}, +{ "fneg", 0x3800c000, 0xfc1fe720, "IfA,fT", pa20, FLAG_STRICT}, +{ "fnegabs", 0x3000e000, 0xfc1fe7e0, "Ffa,fT", pa20, FLAG_STRICT}, +{ "fnegabs", 0x3800e000, 0xfc1fe720, "IfA,fT", pa20, FLAG_STRICT}, +{ "fcnv", 0x30000200, 0xfc1c0720, "{_fa,fT", pa20, FLAG_STRICT}, +{ "fcnv", 0x38000200, 0xfc1c0720, "FGfA,fT", pa20, FLAG_STRICT}, +{ "fcmp", 0x30000400, 0xfc00e7e0, "F?ffa,fb", pa10, FLAG_STRICT}, +{ "fcmp", 0x38000400, 0xfc00e720, "I?ffA,fB", pa10, FLAG_STRICT}, +{ "fcmp", 0x30000400, 0xfc0007e0, "F?ffa,fb,h", pa20, FLAG_STRICT}, +{ "fcmp", 0x38000400, 0xfc000720, "I?ffA,fB,h", pa20, FLAG_STRICT}, +{ "fcmp", 0x30000400, 0xfc00e7e0, "F?ffa,fb", pa10, 0}, +{ "fcmp", 0x38000400, 0xfc00e720, "I?ffA,fB", pa10, 0}, +{ "xmpyu", 0x38004700, 0xfc00e720, "fX,fB,fT", pa11, 0}, +{ "fmpyadd", 0x18000000, 0xfc000000, "Hfi,fj,fk,fl,fm", pa11, 0}, +{ "fmpysub", 0x98000000, 0xfc000000, "Hfi,fj,fk,fl,fm", pa11, 0}, +{ "ftest", 0x30002420, 0xffffffff, "", pa10, FLAG_STRICT}, +{ "ftest", 0x30002420, 0xffffffe0, ",=", pa20, FLAG_STRICT}, +{ "ftest", 0x30000420, 0xffff1fff, "m", pa20, FLAG_STRICT}, +{ "fid", 0x30000000, 0xffffffff, "", pa11, 0}, + +/* Performance Monitor Instructions. */ + +{ "pmdis", 0x30000280, 0xffffffdf, "N", pa20, FLAG_STRICT}, +{ "pmenb", 0x30000680, 0xffffffff, "", pa20, FLAG_STRICT}, + +/* Assist Instructions. */ + +{ "spop0", 0x10000000, 0xfc000600, "v,ON", pa10, 0}, +{ "spop1", 0x10000200, 0xfc000600, "v,oNt", pa10, 0}, +{ "spop2", 0x10000400, 0xfc000600, "v,1Nb", pa10, 0}, +{ "spop3", 0x10000600, 0xfc000600, "v,0Nx,b", pa10, 0}, +{ "copr", 0x30000000, 0xfc000000, "u,2N", pa10, 0}, +{ "cldw", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT}, +{ "cldw", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT}, +{ "cldw", 0x24000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT}, +{ "cldw", 0x24000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc00d200, "ucocc@(b),t", pa20, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc001200, "ucocc@(s,b),t", pa20, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "cldd", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT}, +{ "cldd", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT}, +{ "cldd", 0x2c000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT}, +{ "cldd", 0x2c000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc00d200, "ucocc@(b),t", pa20, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc001200, "ucocc@(s,b),t", pa20, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "cstw", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT}, +{ "cstw", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT}, +{ "cstw", 0x24000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT}, +{ "cstw", 0x24000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc00d200, "ucocCt,@(b)", pa20, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc001200, "ucocCt,@(s,b)", pa20, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT}, +{ "cstd", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT}, +{ "cstd", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT}, +{ "cstd", 0x2c000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT}, +{ "cstd", 0x2c000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc00d200, "ucocCt,@(b)", pa20, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc001200, "ucocCt,@(s,b)", pa20, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, 0}, +{ "cldwx", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, 0}, +{ "clddx", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT}, +{ "clddx", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT}, +{ "clddx", 0x2c000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT}, +{ "clddx", 0x2c000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT}, +{ "clddx", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, 0}, +{ "clddx", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, 0}, +{ "cstwx", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT}, +{ "cstwx", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT}, +{ "cstwx", 0x24000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT}, +{ "cstwx", 0x24000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT}, +{ "cstwx", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, 0}, +{ "cstwx", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, 0}, +{ "cstdx", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT}, +{ "cstdx", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT}, +{ "cstdx", 0x2c000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT}, +{ "cstdx", 0x2c000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT}, +{ "cstdx", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, 0}, +{ "cstdx", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, 0}, +{ "cldws", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT}, +{ "cldws", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT}, +{ "cldws", 0x24001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT}, +{ "cldws", 0x24001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "cldws", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, 0}, +{ "cldws", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, 0}, +{ "cldds", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT}, +{ "cldds", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT}, +{ "cldds", 0x2c001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT}, +{ "cldds", 0x2c001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "cldds", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, 0}, +{ "cldds", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, 0}, +{ "cstws", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT}, +{ "cstws", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT}, +{ "cstws", 0x24001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT}, +{ "cstws", 0x24001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT}, +{ "cstws", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, 0}, +{ "cstws", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, 0}, +{ "cstds", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT}, +{ "cstds", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT}, +{ "cstds", 0x2c001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT}, +{ "cstds", 0x2c001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT}, +{ "cstds", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, 0}, +{ "cstds", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, 0}, + +/* More pseudo instructions which must follow the main table. */ +{ "call", 0xe800f000, 0xfc1ffffd, "n(b)", pa20, FLAG_STRICT}, +{ "call", 0xe800a000, 0xffe0e000, "nW", pa10, FLAG_STRICT}, +{ "ret", 0xe840d000, 0xfffffffd, "n", pa20, FLAG_STRICT}, + +}; + +#define NUMOPCODES ((sizeof pa_opcodes)/(sizeof pa_opcodes[0])) + +/* SKV 12/18/92. Added some denotations for various operands. */ + +#define PA_IMM11_AT_31 'i' +#define PA_IMM14_AT_31 'j' +#define PA_IMM21_AT_31 'k' +#define PA_DISP12 'w' +#define PA_DISP17 'W' + +#define N_HPPA_OPERAND_FORMATS 5 + +/* Integer register names, indexed by the numbers which appear in the + opcodes. */ +static const char *const reg_names[] = +{ + "flags", "r1", "rp", "r3", "r4", "r5", "r6", "r7", "r8", "r9", + "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", + "r20", "r21", "r22", "r23", "r24", "r25", "r26", "dp", "ret0", "ret1", + "sp", "r31" +}; + +/* Floating point register names, indexed by the numbers which appear in the + opcodes. */ +static const char *const fp_reg_names[] = +{ + "fpsr", "fpe2", "fpe4", "fpe6", + "fr4", "fr5", "fr6", "fr7", "fr8", + "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15", + "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23", + "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31" +}; + +typedef unsigned int CORE_ADDR; + +/* Get at various relevant fields of an instruction word. */ + +#define MASK_5 0x1f +#define MASK_10 0x3ff +#define MASK_11 0x7ff +#define MASK_14 0x3fff +#define MASK_16 0xffff +#define MASK_21 0x1fffff + +/* These macros get bit fields using HP's numbering (MSB = 0). */ + +#define GET_FIELD(X, FROM, TO) \ + ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1)) + +#define GET_BIT(X, WHICH) \ + GET_FIELD (X, WHICH, WHICH) + +/* Some of these have been converted to 2-d arrays because they + consume less storage this way. If the maintenance becomes a + problem, convert them back to const 1-d pointer arrays. */ +static const char *const control_reg[] = +{ + "rctr", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", + "pidr1", "pidr2", "ccr", "sar", "pidr3", "pidr4", + "iva", "eiem", "itmr", "pcsq", "pcoq", "iir", "isr", + "ior", "ipsw", "eirr", "tr0", "tr1", "tr2", "tr3", + "tr4", "tr5", "tr6", "tr7" +}; + +static const char *const compare_cond_names[] = +{ + "", ",=", ",<", ",<=", ",<<", ",<<=", ",sv", ",od", + ",tr", ",<>", ",>=", ",>", ",>>=", ",>>", ",nsv", ",ev" +}; +static const char *const compare_cond_64_names[] = +{ + "", ",*=", ",*<", ",*<=", ",*<<", ",*<<=", ",*sv", ",*od", + ",*tr", ",*<>", ",*>=", ",*>", ",*>>=", ",*>>", ",*nsv", ",*ev" +}; +static const char *const cmpib_cond_64_names[] = +{ + ",*<<", ",*=", ",*<", ",*<=", ",*>>=", ",*<>", ",*>=", ",*>" +}; +static const char *const add_cond_names[] = +{ + "", ",=", ",<", ",<=", ",nuv", ",znv", ",sv", ",od", + ",tr", ",<>", ",>=", ",>", ",uv", ",vnz", ",nsv", ",ev" +}; +static const char *const add_cond_64_names[] = +{ + "", ",*=", ",*<", ",*<=", ",*nuv", ",*znv", ",*sv", ",*od", + ",*tr", ",*<>", ",*>=", ",*>", ",*uv", ",*vnz", ",*nsv", ",*ev" +}; +static const char *const wide_add_cond_names[] = +{ + "", ",=", ",<", ",<=", ",nuv", ",*=", ",*<", ",*<=", + ",tr", ",<>", ",>=", ",>", ",uv", ",*<>", ",*>=", ",*>" +}; +static const char *const logical_cond_names[] = +{ + "", ",=", ",<", ",<=", 0, 0, 0, ",od", + ",tr", ",<>", ",>=", ",>", 0, 0, 0, ",ev"}; +static const char *const logical_cond_64_names[] = +{ + "", ",*=", ",*<", ",*<=", 0, 0, 0, ",*od", + ",*tr", ",*<>", ",*>=", ",*>", 0, 0, 0, ",*ev"}; +static const char *const unit_cond_names[] = +{ + "", ",swz", ",sbz", ",shz", ",sdc", ",swc", ",sbc", ",shc", + ",tr", ",nwz", ",nbz", ",nhz", ",ndc", ",nwc", ",nbc", ",nhc" +}; +static const char *const unit_cond_64_names[] = +{ + "", ",*swz", ",*sbz", ",*shz", ",*sdc", ",*swc", ",*sbc", ",*shc", + ",*tr", ",*nwz", ",*nbz", ",*nhz", ",*ndc", ",*nwc", ",*nbc", ",*nhc" +}; +static const char *const shift_cond_names[] = +{ + "", ",=", ",<", ",od", ",tr", ",<>", ",>=", ",ev" +}; +static const char *const shift_cond_64_names[] = +{ + "", ",*=", ",*<", ",*od", ",*tr", ",*<>", ",*>=", ",*ev" +}; +static const char *const bb_cond_64_names[] = +{ + ",*<", ",*>=" +}; +static const char *const index_compl_names[] = {"", ",m", ",s", ",sm"}; +static const char *const short_ldst_compl_names[] = {"", ",ma", "", ",mb"}; +static const char *const short_bytes_compl_names[] = +{ + "", ",b,m", ",e", ",e,m" +}; +static const char *const float_format_names[] = {",sgl", ",dbl", "", ",quad"}; +static const char *const fcnv_fixed_names[] = {",w", ",dw", "", ",qw"}; +static const char *const fcnv_ufixed_names[] = {",uw", ",udw", "", ",uqw"}; +static const char *const float_comp_names[] = +{ + ",false?", ",false", ",?", ",!<=>", ",=", ",=t", ",?=", ",!<>", + ",!?>=", ",<", ",?<", ",!>=", ",!?>", ",<=", ",?<=", ",!>", + ",!?<=", ",>", ",?>", ",!<=", ",!?<", ",>=", ",?>=", ",!<", + ",!?=", ",<>", ",!=", ",!=t", ",!?", ",<=>", ",true?", ",true" +}; +static const char *const signed_unsigned_names[] = {",u", ",s"}; +static const char *const mix_half_names[] = {",l", ",r"}; +static const char *const saturation_names[] = {",us", ",ss", 0, ""}; +static const char *const read_write_names[] = {",r", ",w"}; +static const char *const add_compl_names[] = { 0, "", ",l", ",tsv" }; + +/* For a bunch of different instructions form an index into a + completer name table. */ +#define GET_COMPL(insn) (GET_FIELD (insn, 26, 26) | \ + GET_FIELD (insn, 18, 18) << 1) + +#define GET_COND(insn) (GET_FIELD ((insn), 16, 18) + \ + (GET_FIELD ((insn), 19, 19) ? 8 : 0)) + +/* Utility function to print registers. Put these first, so gcc's function + inlining can do its stuff. */ + +#define fputs_filtered(STR,F) (*info->fprintf_func) (info->stream, "%s", STR) + +static void +fput_reg (unsigned reg, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "%s", reg ? reg_names[reg] : "r0"); +} + +static void +fput_fp_reg (unsigned reg, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "%s", reg ? fp_reg_names[reg] : "fr0"); +} + +static void +fput_fp_reg_r (unsigned reg, disassemble_info *info) +{ + /* Special case floating point exception registers. */ + if (reg < 4) + (*info->fprintf_func) (info->stream, "fpe%d", reg * 2 + 1); + else + (*info->fprintf_func) (info->stream, "%sR", + reg ? fp_reg_names[reg] : "fr0"); +} + +static void +fput_creg (unsigned reg, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "%s", control_reg[reg]); +} + +/* Print constants with sign. */ + +static void +fput_const (unsigned num, disassemble_info *info) +{ + if ((int) num < 0) + (*info->fprintf_func) (info->stream, "-%x", - (int) num); + else + (*info->fprintf_func) (info->stream, "%x", num); +} + +/* Routines to extract various sized constants out of hppa + instructions. */ + +/* Extract a 3-bit space register number from a be, ble, mtsp or mfsp. */ +static int +extract_3 (unsigned word) +{ + return GET_FIELD (word, 18, 18) << 2 | GET_FIELD (word, 16, 17); +} + +static int +extract_5_load (unsigned word) +{ + return low_sign_extend (word >> 16 & MASK_5, 5); +} + +/* Extract the immediate field from a st{bhw}s instruction. */ + +static int +extract_5_store (unsigned word) +{ + return low_sign_extend (word & MASK_5, 5); +} + +/* Extract the immediate field from a break instruction. */ + +static unsigned +extract_5r_store (unsigned word) +{ + return (word & MASK_5); +} + +/* Extract the immediate field from a {sr}sm instruction. */ + +static unsigned +extract_5R_store (unsigned word) +{ + return (word >> 16 & MASK_5); +} + +/* Extract the 10 bit immediate field from a {sr}sm instruction. */ + +static unsigned +extract_10U_store (unsigned word) +{ + return (word >> 16 & MASK_10); +} + +/* Extract the immediate field from a bb instruction. */ + +static unsigned +extract_5Q_store (unsigned word) +{ + return (word >> 21 & MASK_5); +} + +/* Extract an 11 bit immediate field. */ + +static int +extract_11 (unsigned word) +{ + return low_sign_extend (word & MASK_11, 11); +} + +/* Extract a 14 bit immediate field. */ + +static int +extract_14 (unsigned word) +{ + return low_sign_extend (word & MASK_14, 14); +} + +/* Extract a 16 bit immediate field (PA2.0 wide only). */ + +static int +extract_16 (unsigned word) +{ + int m15, m0, m1; + + m0 = GET_BIT (word, 16); + m1 = GET_BIT (word, 17); + m15 = GET_BIT (word, 31); + word = (word >> 1) & 0x1fff; + word = word | (m15 << 15) | ((m15 ^ m0) << 14) | ((m15 ^ m1) << 13); + return sign_extend (word, 16); +} + +/* Extract a 21 bit constant. */ + +static int +extract_21 (unsigned word) +{ + int val; + + word &= MASK_21; + word <<= 11; + val = GET_FIELD (word, 20, 20); + val <<= 11; + val |= GET_FIELD (word, 9, 19); + val <<= 2; + val |= GET_FIELD (word, 5, 6); + val <<= 5; + val |= GET_FIELD (word, 0, 4); + val <<= 2; + val |= GET_FIELD (word, 7, 8); + return sign_extend (val, 21) << 11; +} + +/* Extract a 12 bit constant from branch instructions. */ + +static int +extract_12 (unsigned word) +{ + return sign_extend (GET_FIELD (word, 19, 28) + | GET_FIELD (word, 29, 29) << 10 + | (word & 0x1) << 11, 12) << 2; +} + +/* Extract a 17 bit constant from branch instructions, returning the + 19 bit signed value. */ + +static int +extract_17 (unsigned word) +{ + return sign_extend (GET_FIELD (word, 19, 28) + | GET_FIELD (word, 29, 29) << 10 + | GET_FIELD (word, 11, 15) << 11 + | (word & 0x1) << 16, 17) << 2; +} + +static int +extract_22 (unsigned word) +{ + return sign_extend (GET_FIELD (word, 19, 28) + | GET_FIELD (word, 29, 29) << 10 + | GET_FIELD (word, 11, 15) << 11 + | GET_FIELD (word, 6, 10) << 16 + | (word & 0x1) << 21, 22) << 2; +} + +/* Print one instruction. */ + +int +print_insn_hppa (bfd_vma memaddr, disassemble_info *info) +{ + bfd_byte buffer[4]; + unsigned int insn, i; + + { + int status = + (*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + } + + insn = bfd_getb32 (buffer); + + for (i = 0; i < NUMOPCODES; ++i) + { + const struct pa_opcode *opcode = &pa_opcodes[i]; + + if ((insn & opcode->mask) == opcode->match) + { + const char *s; +#ifndef BFD64 + if (opcode->arch == pa20w) + continue; +#endif + (*info->fprintf_func) (info->stream, "%s", opcode->name); + + if (!strchr ("cfCY?-+nHNZFIuv{", opcode->args[0])) + (*info->fprintf_func) (info->stream, " "); + for (s = opcode->args; *s != '\0'; ++s) + { + switch (*s) + { + case 'x': + fput_reg (GET_FIELD (insn, 11, 15), info); + break; + case 'a': + case 'b': + fput_reg (GET_FIELD (insn, 6, 10), info); + break; + case '^': + fput_creg (GET_FIELD (insn, 6, 10), info); + break; + case 't': + fput_reg (GET_FIELD (insn, 27, 31), info); + break; + + /* Handle floating point registers. */ + case 'f': + switch (*++s) + { + case 't': + fput_fp_reg (GET_FIELD (insn, 27, 31), info); + break; + case 'T': + if (GET_FIELD (insn, 25, 25)) + fput_fp_reg_r (GET_FIELD (insn, 27, 31), info); + else + fput_fp_reg (GET_FIELD (insn, 27, 31), info); + break; + case 'a': + if (GET_FIELD (insn, 25, 25)) + fput_fp_reg_r (GET_FIELD (insn, 6, 10), info); + else + fput_fp_reg (GET_FIELD (insn, 6, 10), info); + break; + + /* 'fA' will not generate a space before the regsiter + name. Normally that is fine. Except that it + causes problems with xmpyu which has no FP format + completer. */ + case 'X': + fputs_filtered (" ", info); + /* FALLTHRU */ + + case 'A': + if (GET_FIELD (insn, 24, 24)) + fput_fp_reg_r (GET_FIELD (insn, 6, 10), info); + else + fput_fp_reg (GET_FIELD (insn, 6, 10), info); + break; + case 'b': + if (GET_FIELD (insn, 25, 25)) + fput_fp_reg_r (GET_FIELD (insn, 11, 15), info); + else + fput_fp_reg (GET_FIELD (insn, 11, 15), info); + break; + case 'B': + if (GET_FIELD (insn, 19, 19)) + fput_fp_reg_r (GET_FIELD (insn, 11, 15), info); + else + fput_fp_reg (GET_FIELD (insn, 11, 15), info); + break; + case 'C': + { + int reg = GET_FIELD (insn, 21, 22); + reg |= GET_FIELD (insn, 16, 18) << 2; + if (GET_FIELD (insn, 23, 23) != 0) + fput_fp_reg_r (reg, info); + else + fput_fp_reg (reg, info); + break; + } + case 'i': + { + int reg = GET_FIELD (insn, 6, 10); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + case 'j': + { + int reg = GET_FIELD (insn, 11, 15); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + case 'k': + { + int reg = GET_FIELD (insn, 27, 31); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + case 'l': + { + int reg = GET_FIELD (insn, 21, 25); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + case 'm': + { + int reg = GET_FIELD (insn, 16, 20); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + + /* 'fe' will not generate a space before the register + name. Normally that is fine. Except that it + causes problems with fstw fe,y(b) which has no FP + format completer. */ + case 'E': + fputs_filtered (" ", info); + /* FALLTHRU */ + + case 'e': + if (GET_FIELD (insn, 30, 30)) + fput_fp_reg_r (GET_FIELD (insn, 11, 15), info); + else + fput_fp_reg (GET_FIELD (insn, 11, 15), info); + break; + case 'x': + fput_fp_reg (GET_FIELD (insn, 11, 15), info); + break; + } + break; + + case '5': + fput_const (extract_5_load (insn), info); + break; + case 's': + { + int space = GET_FIELD (insn, 16, 17); + /* Zero means implicit addressing, not use of sr0. */ + if (space != 0) + (*info->fprintf_func) (info->stream, "sr%d", space); + } + break; + + case 'S': + (*info->fprintf_func) (info->stream, "sr%d", + extract_3 (insn)); + break; + + /* Handle completers. */ + case 'c': + switch (*++s) + { + case 'x': + (*info->fprintf_func) + (info->stream, "%s", + index_compl_names[GET_COMPL (insn)]); + break; + case 'X': + (*info->fprintf_func) + (info->stream, "%s ", + index_compl_names[GET_COMPL (insn)]); + break; + case 'm': + (*info->fprintf_func) + (info->stream, "%s", + short_ldst_compl_names[GET_COMPL (insn)]); + break; + case 'M': + (*info->fprintf_func) + (info->stream, "%s ", + short_ldst_compl_names[GET_COMPL (insn)]); + break; + case 'A': + (*info->fprintf_func) + (info->stream, "%s ", + short_bytes_compl_names[GET_COMPL (insn)]); + break; + case 's': + (*info->fprintf_func) + (info->stream, "%s", + short_bytes_compl_names[GET_COMPL (insn)]); + break; + case 'c': + case 'C': + switch (GET_FIELD (insn, 20, 21)) + { + case 1: + (*info->fprintf_func) (info->stream, ",bc "); + break; + case 2: + (*info->fprintf_func) (info->stream, ",sl "); + break; + default: + (*info->fprintf_func) (info->stream, " "); + } + break; + case 'd': + switch (GET_FIELD (insn, 20, 21)) + { + case 1: + (*info->fprintf_func) (info->stream, ",co "); + break; + default: + (*info->fprintf_func) (info->stream, " "); + } + break; + case 'o': + (*info->fprintf_func) (info->stream, ",o"); + break; + case 'g': + (*info->fprintf_func) (info->stream, ",gate"); + break; + case 'p': + (*info->fprintf_func) (info->stream, ",l,push"); + break; + case 'P': + (*info->fprintf_func) (info->stream, ",pop"); + break; + case 'l': + case 'L': + (*info->fprintf_func) (info->stream, ",l"); + break; + case 'w': + (*info->fprintf_func) + (info->stream, "%s ", + read_write_names[GET_FIELD (insn, 25, 25)]); + break; + case 'W': + (*info->fprintf_func) (info->stream, ",w "); + break; + case 'r': + if (GET_FIELD (insn, 23, 26) == 5) + (*info->fprintf_func) (info->stream, ",r"); + break; + case 'Z': + if (GET_FIELD (insn, 26, 26)) + (*info->fprintf_func) (info->stream, ",m "); + else + (*info->fprintf_func) (info->stream, " "); + break; + case 'i': + if (GET_FIELD (insn, 25, 25)) + (*info->fprintf_func) (info->stream, ",i"); + break; + case 'z': + if (!GET_FIELD (insn, 21, 21)) + (*info->fprintf_func) (info->stream, ",z"); + break; + case 'a': + (*info->fprintf_func) + (info->stream, "%s", + add_compl_names[GET_FIELD (insn, 20, 21)]); + break; + case 'Y': + (*info->fprintf_func) + (info->stream, ",dc%s", + add_compl_names[GET_FIELD (insn, 20, 21)]); + break; + case 'y': + (*info->fprintf_func) + (info->stream, ",c%s", + add_compl_names[GET_FIELD (insn, 20, 21)]); + break; + case 'v': + if (GET_FIELD (insn, 20, 20)) + (*info->fprintf_func) (info->stream, ",tsv"); + break; + case 't': + (*info->fprintf_func) (info->stream, ",tc"); + if (GET_FIELD (insn, 20, 20)) + (*info->fprintf_func) (info->stream, ",tsv"); + break; + case 'B': + (*info->fprintf_func) (info->stream, ",db"); + if (GET_FIELD (insn, 20, 20)) + (*info->fprintf_func) (info->stream, ",tsv"); + break; + case 'b': + (*info->fprintf_func) (info->stream, ",b"); + if (GET_FIELD (insn, 20, 20)) + (*info->fprintf_func) (info->stream, ",tsv"); + break; + case 'T': + if (GET_FIELD (insn, 25, 25)) + (*info->fprintf_func) (info->stream, ",tc"); + break; + case 'S': + /* EXTRD/W has a following condition. */ + if (*(s + 1) == '?') + (*info->fprintf_func) + (info->stream, "%s", + signed_unsigned_names[GET_FIELD (insn, 21, 21)]); + else + (*info->fprintf_func) + (info->stream, "%s ", + signed_unsigned_names[GET_FIELD (insn, 21, 21)]); + break; + case 'h': + (*info->fprintf_func) + (info->stream, "%s", + mix_half_names[GET_FIELD (insn, 17, 17)]); + break; + case 'H': + (*info->fprintf_func) + (info->stream, "%s ", + saturation_names[GET_FIELD (insn, 24, 25)]); + break; + case '*': + (*info->fprintf_func) + (info->stream, ",%d%d%d%d ", + GET_FIELD (insn, 17, 18), GET_FIELD (insn, 20, 21), + GET_FIELD (insn, 22, 23), GET_FIELD (insn, 24, 25)); + break; + + case 'q': + { + int m, a; + + m = GET_FIELD (insn, 28, 28); + a = GET_FIELD (insn, 29, 29); + + if (m && !a) + fputs_filtered (",ma ", info); + else if (m && a) + fputs_filtered (",mb ", info); + else + fputs_filtered (" ", info); + break; + } + + case 'J': + { + int opc = GET_FIELD (insn, 0, 5); + + if (opc == 0x16 || opc == 0x1e) + { + if (GET_FIELD (insn, 29, 29) == 0) + fputs_filtered (",ma ", info); + else + fputs_filtered (",mb ", info); + } + else + fputs_filtered (" ", info); + break; + } + + case 'e': + { + int opc = GET_FIELD (insn, 0, 5); + + if (opc == 0x13 || opc == 0x1b) + { + if (GET_FIELD (insn, 18, 18) == 1) + fputs_filtered (",mb ", info); + else + fputs_filtered (",ma ", info); + } + else if (opc == 0x17 || opc == 0x1f) + { + if (GET_FIELD (insn, 31, 31) == 1) + fputs_filtered (",ma ", info); + else + fputs_filtered (",mb ", info); + } + else + fputs_filtered (" ", info); + + break; + } + } + break; + + /* Handle conditions. */ + case '?': + { + s++; + switch (*s) + { + case 'f': + (*info->fprintf_func) + (info->stream, "%s ", + float_comp_names[GET_FIELD (insn, 27, 31)]); + break; + + /* These four conditions are for the set of instructions + which distinguish true/false conditions by opcode + rather than by the 'f' bit (sigh): comb, comib, + addb, addib. */ + case 't': + fputs_filtered + (compare_cond_names[GET_FIELD (insn, 16, 18)], info); + break; + case 'n': + fputs_filtered + (compare_cond_names[GET_FIELD (insn, 16, 18) + + GET_FIELD (insn, 4, 4) * 8], + info); + break; + case 'N': + fputs_filtered + (compare_cond_64_names[GET_FIELD (insn, 16, 18) + + GET_FIELD (insn, 2, 2) * 8], + info); + break; + case 'Q': + fputs_filtered + (cmpib_cond_64_names[GET_FIELD (insn, 16, 18)], + info); + break; + case '@': + fputs_filtered + (add_cond_names[GET_FIELD (insn, 16, 18) + + GET_FIELD (insn, 4, 4) * 8], + info); + break; + case 's': + (*info->fprintf_func) + (info->stream, "%s ", + compare_cond_names[GET_COND (insn)]); + break; + case 'S': + (*info->fprintf_func) + (info->stream, "%s ", + compare_cond_64_names[GET_COND (insn)]); + break; + case 'a': + (*info->fprintf_func) + (info->stream, "%s ", + add_cond_names[GET_COND (insn)]); + break; + case 'A': + (*info->fprintf_func) + (info->stream, "%s ", + add_cond_64_names[GET_COND (insn)]); + break; + case 'd': + (*info->fprintf_func) + (info->stream, "%s", + add_cond_names[GET_FIELD (insn, 16, 18)]); + break; + + case 'W': + (*info->fprintf_func) + (info->stream, "%s", + wide_add_cond_names[GET_FIELD (insn, 16, 18) + + GET_FIELD (insn, 4, 4) * 8]); + break; + + case 'l': + (*info->fprintf_func) + (info->stream, "%s ", + logical_cond_names[GET_COND (insn)]); + break; + case 'L': + (*info->fprintf_func) + (info->stream, "%s ", + logical_cond_64_names[GET_COND (insn)]); + break; + case 'u': + (*info->fprintf_func) + (info->stream, "%s ", + unit_cond_names[GET_COND (insn)]); + break; + case 'U': + (*info->fprintf_func) + (info->stream, "%s ", + unit_cond_64_names[GET_COND (insn)]); + break; + case 'y': + case 'x': + case 'b': + (*info->fprintf_func) + (info->stream, "%s", + shift_cond_names[GET_FIELD (insn, 16, 18)]); + + /* If the next character in args is 'n', it will handle + putting out the space. */ + if (s[1] != 'n') + (*info->fprintf_func) (info->stream, " "); + break; + case 'X': + (*info->fprintf_func) + (info->stream, "%s ", + shift_cond_64_names[GET_FIELD (insn, 16, 18)]); + break; + case 'B': + (*info->fprintf_func) + (info->stream, "%s", + bb_cond_64_names[GET_FIELD (insn, 16, 16)]); + + /* If the next character in args is 'n', it will handle + putting out the space. */ + if (s[1] != 'n') + (*info->fprintf_func) (info->stream, " "); + break; + } + break; + } + + case 'V': + fput_const (extract_5_store (insn), info); + break; + case 'r': + fput_const (extract_5r_store (insn), info); + break; + case 'R': + fput_const (extract_5R_store (insn), info); + break; + case 'U': + fput_const (extract_10U_store (insn), info); + break; + case 'B': + case 'Q': + fput_const (extract_5Q_store (insn), info); + break; + case 'i': + fput_const (extract_11 (insn), info); + break; + case 'j': + fput_const (extract_14 (insn), info); + break; + case 'k': + fputs_filtered ("L%", info); + fput_const (extract_21 (insn), info); + break; + case '<': + case 'l': + /* 16-bit long disp., PA2.0 wide only. */ + fput_const (extract_16 (insn), info); + break; + case 'n': + if (insn & 0x2) + (*info->fprintf_func) (info->stream, ",n "); + else + (*info->fprintf_func) (info->stream, " "); + break; + case 'N': + if ((insn & 0x20) && s[1]) + (*info->fprintf_func) (info->stream, ",n "); + else if (insn & 0x20) + (*info->fprintf_func) (info->stream, ",n"); + else if (s[1]) + (*info->fprintf_func) (info->stream, " "); + break; + case 'w': + (*info->print_address_func) + (memaddr + 8 + extract_12 (insn), info); + break; + case 'W': + /* 17 bit PC-relative branch. */ + (*info->print_address_func) + ((memaddr + 8 + extract_17 (insn)), info); + break; + case 'z': + /* 17 bit displacement. This is an offset from a register + so it gets disasssembled as just a number, not any sort + of address. */ + fput_const (extract_17 (insn), info); + break; + + case 'Z': + /* addil %r1 implicit output. */ + fputs_filtered ("r1", info); + break; + + case 'Y': + /* be,l %sr0,%r31 implicit output. */ + fputs_filtered ("sr0,r31", info); + break; + + case '@': + (*info->fprintf_func) (info->stream, "0"); + break; + + case '.': + (*info->fprintf_func) (info->stream, "%d", + GET_FIELD (insn, 24, 25)); + break; + case '*': + (*info->fprintf_func) (info->stream, "%d", + GET_FIELD (insn, 22, 25)); + break; + case '!': + fputs_filtered ("sar", info); + break; + case 'p': + (*info->fprintf_func) (info->stream, "%d", + 31 - GET_FIELD (insn, 22, 26)); + break; + case '~': + { + int num; + num = GET_FIELD (insn, 20, 20) << 5; + num |= GET_FIELD (insn, 22, 26); + (*info->fprintf_func) (info->stream, "%d", 63 - num); + break; + } + case 'P': + (*info->fprintf_func) (info->stream, "%d", + GET_FIELD (insn, 22, 26)); + break; + case 'q': + { + int num; + num = GET_FIELD (insn, 20, 20) << 5; + num |= GET_FIELD (insn, 22, 26); + (*info->fprintf_func) (info->stream, "%d", num); + break; + } + case 'T': + (*info->fprintf_func) (info->stream, "%d", + 32 - GET_FIELD (insn, 27, 31)); + break; + case '%': + { + int num; + num = (GET_FIELD (insn, 23, 23) + 1) * 32; + num -= GET_FIELD (insn, 27, 31); + (*info->fprintf_func) (info->stream, "%d", num); + break; + } + case '|': + { + int num; + num = (GET_FIELD (insn, 19, 19) + 1) * 32; + num -= GET_FIELD (insn, 27, 31); + (*info->fprintf_func) (info->stream, "%d", num); + break; + } + case '$': + fput_const (GET_FIELD (insn, 20, 28), info); + break; + case 'A': + fput_const (GET_FIELD (insn, 6, 18), info); + break; + case 'D': + fput_const (GET_FIELD (insn, 6, 31), info); + break; + case 'v': + (*info->fprintf_func) (info->stream, ",%d", + GET_FIELD (insn, 23, 25)); + break; + case 'O': + fput_const ((GET_FIELD (insn, 6,20) << 5 | + GET_FIELD (insn, 27, 31)), info); + break; + case 'o': + fput_const (GET_FIELD (insn, 6, 20), info); + break; + case '2': + fput_const ((GET_FIELD (insn, 6, 22) << 5 | + GET_FIELD (insn, 27, 31)), info); + break; + case '1': + fput_const ((GET_FIELD (insn, 11, 20) << 5 | + GET_FIELD (insn, 27, 31)), info); + break; + case '0': + fput_const ((GET_FIELD (insn, 16, 20) << 5 | + GET_FIELD (insn, 27, 31)), info); + break; + case 'u': + (*info->fprintf_func) (info->stream, ",%d", + GET_FIELD (insn, 23, 25)); + break; + case 'F': + /* If no destination completer and not before a completer + for fcmp, need a space here. */ + if (s[1] == 'G' || s[1] == '?') + fputs_filtered + (float_format_names[GET_FIELD (insn, 19, 20)], info); + else + (*info->fprintf_func) + (info->stream, "%s ", + float_format_names[GET_FIELD (insn, 19, 20)]); + break; + case 'G': + (*info->fprintf_func) + (info->stream, "%s ", + float_format_names[GET_FIELD (insn, 17, 18)]); + break; + case 'H': + if (GET_FIELD (insn, 26, 26) == 1) + (*info->fprintf_func) (info->stream, "%s ", + float_format_names[0]); + else + (*info->fprintf_func) (info->stream, "%s ", + float_format_names[1]); + break; + case 'I': + /* If no destination completer and not before a completer + for fcmp, need a space here. */ + if (s[1] == '?') + fputs_filtered + (float_format_names[GET_FIELD (insn, 20, 20)], info); + else + (*info->fprintf_func) + (info->stream, "%s ", + float_format_names[GET_FIELD (insn, 20, 20)]); + break; + + case 'J': + fput_const (extract_14 (insn), info); + break; + + case '#': + { + int sign = GET_FIELD (insn, 31, 31); + int imm10 = GET_FIELD (insn, 18, 27); + int disp; + + if (sign) + disp = (-1 << 10) | imm10; + else + disp = imm10; + + disp <<= 3; + fput_const (disp, info); + break; + } + case 'K': + case 'd': + { + int sign = GET_FIELD (insn, 31, 31); + int imm11 = GET_FIELD (insn, 18, 28); + int disp; + + if (sign) + disp = (-1 << 11) | imm11; + else + disp = imm11; + + disp <<= 2; + fput_const (disp, info); + break; + } + + case '>': + case 'y': + { + /* 16-bit long disp., PA2.0 wide only. */ + int disp = extract_16 (insn); + disp &= ~3; + fput_const (disp, info); + break; + } + + case '&': + { + /* 16-bit long disp., PA2.0 wide only. */ + int disp = extract_16 (insn); + disp &= ~7; + fput_const (disp, info); + break; + } + + case '_': + break; /* Dealt with by '{' */ + + case '{': + { + int sub = GET_FIELD (insn, 14, 16); + int df = GET_FIELD (insn, 17, 18); + int sf = GET_FIELD (insn, 19, 20); + const char * const * source = float_format_names; + const char * const * dest = float_format_names; + const char *t = ""; + + if (sub == 4) + { + fputs_filtered (",UND ", info); + break; + } + if ((sub & 3) == 3) + t = ",t"; + if ((sub & 3) == 1) + source = sub & 4 ? fcnv_ufixed_names : fcnv_fixed_names; + if (sub & 2) + dest = sub & 4 ? fcnv_ufixed_names : fcnv_fixed_names; + + (*info->fprintf_func) (info->stream, "%s%s%s ", + t, source[sf], dest[df]); + break; + } + + case 'm': + { + int y = GET_FIELD (insn, 16, 18); + + if (y != 1) + fput_const ((y ^ 1) - 1, info); + } + break; + + case 'h': + { + int cbit; + + cbit = GET_FIELD (insn, 16, 18); + + if (cbit > 0) + (*info->fprintf_func) (info->stream, ",%d", cbit - 1); + break; + } + + case '=': + { + int cond = GET_FIELD (insn, 27, 31); + + switch (cond) + { + case 0: fputs_filtered (" ", info); break; + case 1: fputs_filtered ("acc ", info); break; + case 2: fputs_filtered ("rej ", info); break; + case 5: fputs_filtered ("acc8 ", info); break; + case 6: fputs_filtered ("rej8 ", info); break; + case 9: fputs_filtered ("acc6 ", info); break; + case 13: fputs_filtered ("acc4 ", info); break; + case 17: fputs_filtered ("acc2 ", info); break; + default: break; + } + break; + } + + case 'X': + (*info->print_address_func) + (memaddr + 8 + extract_22 (insn), info); + break; + case 'L': + fputs_filtered (",rp", info); + break; + default: + (*info->fprintf_func) (info->stream, "%c", *s); + break; + } + } + return sizeof (insn); + } + } + (*info->fprintf_func) (info->stream, "#%8x", insn); + return sizeof (insn); +} diff --git a/disas/i386.c b/disas/i386.c new file mode 100644 index 0000000..00ceca9 --- /dev/null +++ b/disas/i386.c @@ -0,0 +1,6745 @@ +/* opcodes/i386-dis.c r1.126 */ +/* Print i386 instructions for GDB, the GNU debugger. + Copyright 1988, 1989, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +/* 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu) + July 1988 + modified by John Hassey (hassey@dg-rtp.dg.com) + x86-64 support added by Jan Hubicka (jh@suse.cz) + VIA PadLock support by Michal Ludvig (mludvig@suse.cz). */ + +/* The main tables describing the instructions is essentially a copy + of the "Opcode Map" chapter (Appendix A) of the Intel 80386 + Programmers Manual. Usually, there is a capital letter, followed + by a small letter. The capital letter tell the addressing mode, + and the small letter tells about the operand size. Refer to + the Intel manual for details. */ + +#include +#include "disas/bfd.h" +/* include/opcode/i386.h r1.78 */ + +/* opcode/i386.h -- Intel 80386 opcode macros + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler, and GDB, the GNU Debugger. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +/* The SystemV/386 SVR3.2 assembler, and probably all AT&T derived + ix86 Unix assemblers, generate floating point instructions with + reversed source and destination registers in certain cases. + Unfortunately, gcc and possibly many other programs use this + reversed syntax, so we're stuck with it. + + eg. `fsub %st(3),%st' results in st = st - st(3) as expected, but + `fsub %st,%st(3)' results in st(3) = st - st(3), rather than + the expected st(3) = st(3) - st + + This happens with all the non-commutative arithmetic floating point + operations with two register operands, where the source register is + %st, and destination register is %st(i). + + The affected opcode map is dceX, dcfX, deeX, defX. */ + +#ifndef SYSV386_COMPAT +/* Set non-zero for broken, compatible instructions. Set to zero for + non-broken opcodes at your peril. gcc generates SystemV/386 + compatible instructions. */ +#define SYSV386_COMPAT 1 +#endif +#ifndef OLDGCC_COMPAT +/* Set non-zero to cater for old (<= 2.8.1) versions of gcc that could + generate nonsense fsubp, fsubrp, fdivp and fdivrp with operands + reversed. */ +#define OLDGCC_COMPAT SYSV386_COMPAT +#endif + +#define MOV_AX_DISP32 0xa0 +#define POP_SEG_SHORT 0x07 +#define JUMP_PC_RELATIVE 0xeb +#define INT_OPCODE 0xcd +#define INT3_OPCODE 0xcc +/* The opcode for the fwait instruction, which disassembler treats as a + prefix when it can. */ +#define FWAIT_OPCODE 0x9b +#define ADDR_PREFIX_OPCODE 0x67 +#define DATA_PREFIX_OPCODE 0x66 +#define LOCK_PREFIX_OPCODE 0xf0 +#define CS_PREFIX_OPCODE 0x2e +#define DS_PREFIX_OPCODE 0x3e +#define ES_PREFIX_OPCODE 0x26 +#define FS_PREFIX_OPCODE 0x64 +#define GS_PREFIX_OPCODE 0x65 +#define SS_PREFIX_OPCODE 0x36 +#define REPNE_PREFIX_OPCODE 0xf2 +#define REPE_PREFIX_OPCODE 0xf3 + +#define TWO_BYTE_OPCODE_ESCAPE 0x0f +#define NOP_OPCODE (char) 0x90 + +/* register numbers */ +#define EBP_REG_NUM 5 +#define ESP_REG_NUM 4 + +/* modrm_byte.regmem for twobyte escape */ +#define ESCAPE_TO_TWO_BYTE_ADDRESSING ESP_REG_NUM +/* index_base_byte.index for no index register addressing */ +#define NO_INDEX_REGISTER ESP_REG_NUM +/* index_base_byte.base for no base register addressing */ +#define NO_BASE_REGISTER EBP_REG_NUM +#define NO_BASE_REGISTER_16 6 + +/* modrm.mode = REGMEM_FIELD_HAS_REG when a register is in there */ +#define REGMEM_FIELD_HAS_REG 0x3/* always = 0x3 */ +#define REGMEM_FIELD_HAS_MEM (~REGMEM_FIELD_HAS_REG) + +/* x86-64 extension prefix. */ +#define REX_OPCODE 0x40 + +/* Indicates 64 bit operand size. */ +#define REX_W 8 +/* High extension to reg field of modrm byte. */ +#define REX_R 4 +/* High extension to SIB index field. */ +#define REX_X 2 +/* High extension to base field of modrm or SIB, or reg field of opcode. */ +#define REX_B 1 + +/* max operands per insn */ +#define MAX_OPERANDS 4 + +/* max immediates per insn (lcall, ljmp, insertq, extrq) */ +#define MAX_IMMEDIATE_OPERANDS 2 + +/* max memory refs per insn (string ops) */ +#define MAX_MEMORY_OPERANDS 2 + +/* max size of insn mnemonics. */ +#define MAX_MNEM_SIZE 16 + +/* max size of register name in insn mnemonics. */ +#define MAX_REG_NAME_SIZE 8 + +/* opcodes/i386-dis.c r1.126 */ +#include "qemu-common.h" + +#include + +static int fetch_data2(struct disassemble_info *, bfd_byte *); +static int fetch_data(struct disassemble_info *, bfd_byte *); +static void ckprefix (void); +static const char *prefix_name (int, int); +static int print_insn (bfd_vma, disassemble_info *); +static void dofloat (int); +static void OP_ST (int, int); +static void OP_STi (int, int); +static int putop (const char *, int); +static void oappend (const char *); +static void append_seg (void); +static void OP_indirE (int, int); +static void print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma disp); +static void print_displacement (char *, bfd_vma); +static void OP_E (int, int); +static void OP_G (int, int); +static void OP_vvvv (int, int); +static bfd_vma get64 (void); +static bfd_signed_vma get32 (void); +static bfd_signed_vma get32s (void); +static int get16 (void); +static void set_op (bfd_vma, int); +static void OP_REG (int, int); +static void OP_IMREG (int, int); +static void OP_I (int, int); +static void OP_I64 (int, int); +static void OP_sI (int, int); +static void OP_J (int, int); +static void OP_SEG (int, int); +static void OP_DIR (int, int); +static void OP_OFF (int, int); +static void OP_OFF64 (int, int); +static void ptr_reg (int, int); +static void OP_ESreg (int, int); +static void OP_DSreg (int, int); +static void OP_C (int, int); +static void OP_D (int, int); +static void OP_T (int, int); +static void OP_R (int, int); +static void OP_MMX (int, int); +static void OP_XMM (int, int); +static void OP_EM (int, int); +static void OP_EX (int, int); +static void OP_EMC (int,int); +static void OP_MXC (int,int); +static void OP_MS (int, int); +static void OP_XS (int, int); +static void OP_M (int, int); +static void OP_VMX (int, int); +static void OP_0fae (int, int); +static void OP_0f07 (int, int); +static void NOP_Fixup1 (int, int); +static void NOP_Fixup2 (int, int); +static void OP_3DNowSuffix (int, int); +static void OP_SIMD_Suffix (int, int); +static void SIMD_Fixup (int, int); +static void PNI_Fixup (int, int); +static void SVME_Fixup (int, int); +static void INVLPG_Fixup (int, int); +static void BadOp (void); +static void VMX_Fixup (int, int); +static void REP_Fixup (int, int); +static void CMPXCHG8B_Fixup (int, int); +static void XMM_Fixup (int, int); +static void CRC32_Fixup (int, int); + +struct dis_private { + /* Points to first byte not fetched. */ + bfd_byte *max_fetched; + bfd_byte the_buffer[MAX_MNEM_SIZE]; + bfd_vma insn_start; + int orig_sizeflag; + sigjmp_buf bailout; +}; + +enum address_mode +{ + mode_16bit, + mode_32bit, + mode_64bit +}; + +static enum address_mode address_mode; + +/* Flags for the prefixes for the current instruction. See below. */ +static int prefixes; + +/* REX prefix the current instruction. See below. */ +static int rex; +/* Bits of REX we've already used. */ +static int rex_used; +/* Mark parts used in the REX prefix. When we are testing for + empty prefix (for 8bit register REX extension), just mask it + out. Otherwise test for REX bit is excuse for existence of REX + only in case value is nonzero. */ +#define USED_REX(value) \ + { \ + if (value) \ + { \ + if ((rex & value)) \ + rex_used |= (value) | REX_OPCODE; \ + } \ + else \ + rex_used |= REX_OPCODE; \ + } + +/* Flags for prefixes which we somehow handled when printing the + current instruction. */ +static int used_prefixes; + +/* The VEX.vvvv register, unencoded. */ +static int vex_reg; + +/* Flags stored in PREFIXES. */ +#define PREFIX_REPZ 1 +#define PREFIX_REPNZ 2 +#define PREFIX_LOCK 4 +#define PREFIX_CS 8 +#define PREFIX_SS 0x10 +#define PREFIX_DS 0x20 +#define PREFIX_ES 0x40 +#define PREFIX_FS 0x80 +#define PREFIX_GS 0x100 +#define PREFIX_DATA 0x200 +#define PREFIX_ADDR 0x400 +#define PREFIX_FWAIT 0x800 + +#define PREFIX_VEX_0F 0x1000 +#define PREFIX_VEX_0F38 0x2000 +#define PREFIX_VEX_0F3A 0x4000 + +/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) + to ADDR (exclusive) are valid. Returns 1 for success, longjmps + on error. */ +static int +fetch_data2(struct disassemble_info *info, bfd_byte *addr) +{ + int status; + struct dis_private *priv = (struct dis_private *) info->private_data; + bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); + + if (addr <= priv->the_buffer + MAX_MNEM_SIZE) + status = (*info->read_memory_func) (start, + priv->max_fetched, + addr - priv->max_fetched, + info); + else + status = -1; + if (status != 0) + { + /* If we did manage to read at least one byte, then + print_insn_i386 will do something sensible. Otherwise, print + an error. We do that here because this is where we know + STATUS. */ + if (priv->max_fetched == priv->the_buffer) + (*info->memory_error_func) (status, start, info); + siglongjmp(priv->bailout, 1); + } + else + priv->max_fetched = addr; + return 1; +} + +static int +fetch_data(struct disassemble_info *info, bfd_byte *addr) +{ + if (addr <= ((struct dis_private *) (info->private_data))->max_fetched) { + return 1; + } else { + return fetch_data2(info, addr); + } +} + + +#define XX { NULL, 0 } + +#define Bv { OP_vvvv, v_mode } +#define Eb { OP_E, b_mode } +#define Ev { OP_E, v_mode } +#define Ed { OP_E, d_mode } +#define Edq { OP_E, dq_mode } +#define Edqw { OP_E, dqw_mode } +#define Edqb { OP_E, dqb_mode } +#define Edqd { OP_E, dqd_mode } +#define indirEv { OP_indirE, stack_v_mode } +#define indirEp { OP_indirE, f_mode } +#define stackEv { OP_E, stack_v_mode } +#define Em { OP_E, m_mode } +#define Ew { OP_E, w_mode } +#define M { OP_M, 0 } /* lea, lgdt, etc. */ +#define Ma { OP_M, v_mode } +#define Mp { OP_M, f_mode } /* 32 or 48 bit memory operand for LDS, LES etc */ +#define Mq { OP_M, q_mode } +#define Gb { OP_G, b_mode } +#define Gv { OP_G, v_mode } +#define Gd { OP_G, d_mode } +#define Gdq { OP_G, dq_mode } +#define Gm { OP_G, m_mode } +#define Gw { OP_G, w_mode } +#define Rd { OP_R, d_mode } +#define Rm { OP_R, m_mode } +#define Ib { OP_I, b_mode } +#define sIb { OP_sI, b_mode } /* sign extened byte */ +#define Iv { OP_I, v_mode } +#define Iq { OP_I, q_mode } +#define Iv64 { OP_I64, v_mode } +#define Iw { OP_I, w_mode } +#define I1 { OP_I, const_1_mode } +#define Jb { OP_J, b_mode } +#define Jv { OP_J, v_mode } +#define Cm { OP_C, m_mode } +#define Dm { OP_D, m_mode } +#define Td { OP_T, d_mode } + +#define RMeAX { OP_REG, eAX_reg } +#define RMeBX { OP_REG, eBX_reg } +#define RMeCX { OP_REG, eCX_reg } +#define RMeDX { OP_REG, eDX_reg } +#define RMeSP { OP_REG, eSP_reg } +#define RMeBP { OP_REG, eBP_reg } +#define RMeSI { OP_REG, eSI_reg } +#define RMeDI { OP_REG, eDI_reg } +#define RMrAX { OP_REG, rAX_reg } +#define RMrBX { OP_REG, rBX_reg } +#define RMrCX { OP_REG, rCX_reg } +#define RMrDX { OP_REG, rDX_reg } +#define RMrSP { OP_REG, rSP_reg } +#define RMrBP { OP_REG, rBP_reg } +#define RMrSI { OP_REG, rSI_reg } +#define RMrDI { OP_REG, rDI_reg } +#define RMAL { OP_REG, al_reg } +#define RMAL { OP_REG, al_reg } +#define RMCL { OP_REG, cl_reg } +#define RMDL { OP_REG, dl_reg } +#define RMBL { OP_REG, bl_reg } +#define RMAH { OP_REG, ah_reg } +#define RMCH { OP_REG, ch_reg } +#define RMDH { OP_REG, dh_reg } +#define RMBH { OP_REG, bh_reg } +#define RMAX { OP_REG, ax_reg } +#define RMDX { OP_REG, dx_reg } + +#define eAX { OP_IMREG, eAX_reg } +#define eBX { OP_IMREG, eBX_reg } +#define eCX { OP_IMREG, eCX_reg } +#define eDX { OP_IMREG, eDX_reg } +#define eSP { OP_IMREG, eSP_reg } +#define eBP { OP_IMREG, eBP_reg } +#define eSI { OP_IMREG, eSI_reg } +#define eDI { OP_IMREG, eDI_reg } +#define AL { OP_IMREG, al_reg } +#define CL { OP_IMREG, cl_reg } +#define DL { OP_IMREG, dl_reg } +#define BL { OP_IMREG, bl_reg } +#define AH { OP_IMREG, ah_reg } +#define CH { OP_IMREG, ch_reg } +#define DH { OP_IMREG, dh_reg } +#define BH { OP_IMREG, bh_reg } +#define AX { OP_IMREG, ax_reg } +#define DX { OP_IMREG, dx_reg } +#define zAX { OP_IMREG, z_mode_ax_reg } +#define indirDX { OP_IMREG, indir_dx_reg } + +#define Sw { OP_SEG, w_mode } +#define Sv { OP_SEG, v_mode } +#define Ap { OP_DIR, 0 } +#define Ob { OP_OFF64, b_mode } +#define Ov { OP_OFF64, v_mode } +#define Xb { OP_DSreg, eSI_reg } +#define Xv { OP_DSreg, eSI_reg } +#define Xz { OP_DSreg, eSI_reg } +#define Yb { OP_ESreg, eDI_reg } +#define Yv { OP_ESreg, eDI_reg } +#define DSBX { OP_DSreg, eBX_reg } + +#define es { OP_REG, es_reg } +#define ss { OP_REG, ss_reg } +#define cs { OP_REG, cs_reg } +#define ds { OP_REG, ds_reg } +#define fs { OP_REG, fs_reg } +#define gs { OP_REG, gs_reg } + +#define MX { OP_MMX, 0 } +#define XM { OP_XMM, 0 } +#define EM { OP_EM, v_mode } +#define EMd { OP_EM, d_mode } +#define EMq { OP_EM, q_mode } +#define EXd { OP_EX, d_mode } +#define EXq { OP_EX, q_mode } +#define EXx { OP_EX, x_mode } +#define MS { OP_MS, v_mode } +#define XS { OP_XS, v_mode } +#define EMC { OP_EMC, v_mode } +#define MXC { OP_MXC, 0 } +#define VM { OP_VMX, q_mode } +#define OPSUF { OP_3DNowSuffix, 0 } +#define OPSIMD { OP_SIMD_Suffix, 0 } +#define XMM0 { XMM_Fixup, 0 } + +/* Used handle "rep" prefix for string instructions. */ +#define Xbr { REP_Fixup, eSI_reg } +#define Xvr { REP_Fixup, eSI_reg } +#define Ybr { REP_Fixup, eDI_reg } +#define Yvr { REP_Fixup, eDI_reg } +#define Yzr { REP_Fixup, eDI_reg } +#define indirDXr { REP_Fixup, indir_dx_reg } +#define ALr { REP_Fixup, al_reg } +#define eAXr { REP_Fixup, eAX_reg } + +#define cond_jump_flag { NULL, cond_jump_mode } +#define loop_jcxz_flag { NULL, loop_jcxz_mode } + +/* bits in sizeflag */ +#define SUFFIX_ALWAYS 4 +#define AFLAG 2 +#define DFLAG 1 + +#define b_mode 1 /* byte operand */ +#define v_mode 2 /* operand size depends on prefixes */ +#define w_mode 3 /* word operand */ +#define d_mode 4 /* double word operand */ +#define q_mode 5 /* quad word operand */ +#define t_mode 6 /* ten-byte operand */ +#define x_mode 7 /* 16-byte XMM operand */ +#define m_mode 8 /* d_mode in 32bit, q_mode in 64bit mode. */ +#define cond_jump_mode 9 +#define loop_jcxz_mode 10 +#define dq_mode 11 /* operand size depends on REX prefixes. */ +#define dqw_mode 12 /* registers like dq_mode, memory like w_mode. */ +#define f_mode 13 /* 4- or 6-byte pointer operand */ +#define const_1_mode 14 +#define stack_v_mode 15 /* v_mode for stack-related opcodes. */ +#define z_mode 16 /* non-quad operand size depends on prefixes */ +#define o_mode 17 /* 16-byte operand */ +#define dqb_mode 18 /* registers like dq_mode, memory like b_mode. */ +#define dqd_mode 19 /* registers like dq_mode, memory like d_mode. */ + +#define es_reg 100 +#define cs_reg 101 +#define ss_reg 102 +#define ds_reg 103 +#define fs_reg 104 +#define gs_reg 105 + +#define eAX_reg 108 +#define eCX_reg 109 +#define eDX_reg 110 +#define eBX_reg 111 +#define eSP_reg 112 +#define eBP_reg 113 +#define eSI_reg 114 +#define eDI_reg 115 + +#define al_reg 116 +#define cl_reg 117 +#define dl_reg 118 +#define bl_reg 119 +#define ah_reg 120 +#define ch_reg 121 +#define dh_reg 122 +#define bh_reg 123 + +#define ax_reg 124 +#define cx_reg 125 +#define dx_reg 126 +#define bx_reg 127 +#define sp_reg 128 +#define bp_reg 129 +#define si_reg 130 +#define di_reg 131 + +#define rAX_reg 132 +#define rCX_reg 133 +#define rDX_reg 134 +#define rBX_reg 135 +#define rSP_reg 136 +#define rBP_reg 137 +#define rSI_reg 138 +#define rDI_reg 139 + +#define z_mode_ax_reg 149 +#define indir_dx_reg 150 + +#define FLOATCODE 1 +#define USE_GROUPS 2 +#define USE_PREFIX_USER_TABLE 3 +#define X86_64_SPECIAL 4 +#define IS_3BYTE_OPCODE 5 + +#define FLOAT NULL, { { NULL, FLOATCODE } } + +#define GRP1a NULL, { { NULL, USE_GROUPS }, { NULL, 0 } } +#define GRP1b NULL, { { NULL, USE_GROUPS }, { NULL, 1 } } +#define GRP1S NULL, { { NULL, USE_GROUPS }, { NULL, 2 } } +#define GRP1Ss NULL, { { NULL, USE_GROUPS }, { NULL, 3 } } +#define GRP2b NULL, { { NULL, USE_GROUPS }, { NULL, 4 } } +#define GRP2S NULL, { { NULL, USE_GROUPS }, { NULL, 5 } } +#define GRP2b_one NULL, { { NULL, USE_GROUPS }, { NULL, 6 } } +#define GRP2S_one NULL, { { NULL, USE_GROUPS }, { NULL, 7 } } +#define GRP2b_cl NULL, { { NULL, USE_GROUPS }, { NULL, 8 } } +#define GRP2S_cl NULL, { { NULL, USE_GROUPS }, { NULL, 9 } } +#define GRP3b NULL, { { NULL, USE_GROUPS }, { NULL, 10 } } +#define GRP3S NULL, { { NULL, USE_GROUPS }, { NULL, 11 } } +#define GRP4 NULL, { { NULL, USE_GROUPS }, { NULL, 12 } } +#define GRP5 NULL, { { NULL, USE_GROUPS }, { NULL, 13 } } +#define GRP6 NULL, { { NULL, USE_GROUPS }, { NULL, 14 } } +#define GRP7 NULL, { { NULL, USE_GROUPS }, { NULL, 15 } } +#define GRP8 NULL, { { NULL, USE_GROUPS }, { NULL, 16 } } +#define GRP9 NULL, { { NULL, USE_GROUPS }, { NULL, 17 } } +#define GRP11_C6 NULL, { { NULL, USE_GROUPS }, { NULL, 18 } } +#define GRP11_C7 NULL, { { NULL, USE_GROUPS }, { NULL, 19 } } +#define GRP12 NULL, { { NULL, USE_GROUPS }, { NULL, 20 } } +#define GRP13 NULL, { { NULL, USE_GROUPS }, { NULL, 21 } } +#define GRP14 NULL, { { NULL, USE_GROUPS }, { NULL, 22 } } +#define GRP15 NULL, { { NULL, USE_GROUPS }, { NULL, 23 } } +#define GRP16 NULL, { { NULL, USE_GROUPS }, { NULL, 24 } } +#define GRPAMD NULL, { { NULL, USE_GROUPS }, { NULL, 25 } } +#define GRPPADLCK1 NULL, { { NULL, USE_GROUPS }, { NULL, 26 } } +#define GRPPADLCK2 NULL, { { NULL, USE_GROUPS }, { NULL, 27 } } + +#define PREGRP0 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 0 } } +#define PREGRP1 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 1 } } +#define PREGRP2 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 2 } } +#define PREGRP3 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 3 } } +#define PREGRP4 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 4 } } +#define PREGRP5 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 5 } } +#define PREGRP6 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 6 } } +#define PREGRP7 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 7 } } +#define PREGRP8 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 8 } } +#define PREGRP9 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 9 } } +#define PREGRP10 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 10 } } +#define PREGRP11 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 11 } } +#define PREGRP12 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 12 } } +#define PREGRP13 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 13 } } +#define PREGRP14 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 14 } } +#define PREGRP15 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 15 } } +#define PREGRP16 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 16 } } +#define PREGRP17 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 17 } } +#define PREGRP18 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 18 } } +#define PREGRP19 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 19 } } +#define PREGRP20 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 20 } } +#define PREGRP21 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 21 } } +#define PREGRP22 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 22 } } +#define PREGRP23 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 23 } } +#define PREGRP24 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 24 } } +#define PREGRP25 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 25 } } +#define PREGRP26 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 26 } } +#define PREGRP27 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 27 } } +#define PREGRP28 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 28 } } +#define PREGRP29 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 29 } } +#define PREGRP30 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 30 } } +#define PREGRP31 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 31 } } +#define PREGRP32 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 32 } } +#define PREGRP33 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 33 } } +#define PREGRP34 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 34 } } +#define PREGRP35 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 35 } } +#define PREGRP36 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 36 } } +#define PREGRP37 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 37 } } +#define PREGRP38 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 38 } } +#define PREGRP39 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 39 } } +#define PREGRP40 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 40 } } +#define PREGRP41 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 41 } } +#define PREGRP42 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 42 } } +#define PREGRP43 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 43 } } +#define PREGRP44 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 44 } } +#define PREGRP45 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 45 } } +#define PREGRP46 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 46 } } +#define PREGRP47 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 47 } } +#define PREGRP48 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 48 } } +#define PREGRP49 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 49 } } +#define PREGRP50 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 50 } } +#define PREGRP51 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 51 } } +#define PREGRP52 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 52 } } +#define PREGRP53 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 53 } } +#define PREGRP54 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 54 } } +#define PREGRP55 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 55 } } +#define PREGRP56 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 56 } } +#define PREGRP57 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 57 } } +#define PREGRP58 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 58 } } +#define PREGRP59 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 59 } } +#define PREGRP60 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 60 } } +#define PREGRP61 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 61 } } +#define PREGRP62 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 62 } } +#define PREGRP63 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 63 } } +#define PREGRP64 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 64 } } +#define PREGRP65 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 65 } } +#define PREGRP66 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 66 } } +#define PREGRP67 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 67 } } +#define PREGRP68 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 68 } } +#define PREGRP69 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 69 } } +#define PREGRP70 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 70 } } +#define PREGRP71 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 71 } } +#define PREGRP72 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 72 } } +#define PREGRP73 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 73 } } +#define PREGRP74 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 74 } } +#define PREGRP75 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 75 } } +#define PREGRP76 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 76 } } +#define PREGRP77 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 77 } } +#define PREGRP78 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 78 } } +#define PREGRP79 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 79 } } +#define PREGRP80 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 80 } } +#define PREGRP81 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 81 } } +#define PREGRP82 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 82 } } +#define PREGRP83 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 83 } } +#define PREGRP84 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 84 } } +#define PREGRP85 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 85 } } +#define PREGRP86 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 86 } } +#define PREGRP87 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 87 } } +#define PREGRP88 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 88 } } +#define PREGRP89 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 89 } } +#define PREGRP90 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 90 } } +#define PREGRP91 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 91 } } +#define PREGRP92 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 92 } } +#define PREGRP93 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 93 } } +#define PREGRP94 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 94 } } +#define PREGRP95 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 95 } } +#define PREGRP96 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 96 } } +#define PREGRP97 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 97 } } +#define PREGRP98 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 98 } } +#define PREGRP99 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 99 } } +#define PREGRP100 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 100 } } +#define PREGRP101 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 101 } } +#define PREGRP102 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 102 } } +#define PREGRP103 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 103 } } +#define PREGRP104 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 104 } } +#define PREGRP105 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 105 } } +#define PREGRP106 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 106 } } + +#define X86_64_0 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 0 } } +#define X86_64_1 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 1 } } +#define X86_64_2 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 2 } } +#define X86_64_3 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 3 } } + +#define THREE_BYTE_0 NULL, { { NULL, IS_3BYTE_OPCODE }, { NULL, 0 } } +#define THREE_BYTE_1 NULL, { { NULL, IS_3BYTE_OPCODE }, { NULL, 1 } } + +typedef void (*op_rtn) (int bytemode, int sizeflag); + +struct dis386 { + const char *name; + struct + { + op_rtn rtn; + int bytemode; + } op[MAX_OPERANDS]; +}; + +/* Upper case letters in the instruction names here are macros. + 'A' => print 'b' if no register operands or suffix_always is true + 'B' => print 'b' if suffix_always is true + 'C' => print 's' or 'l' ('w' or 'd' in Intel mode) depending on operand + . size prefix + 'D' => print 'w' if no register operands or 'w', 'l' or 'q', if + . suffix_always is true + 'E' => print 'e' if 32-bit form of jcxz + 'F' => print 'w' or 'l' depending on address size prefix (loop insns) + 'G' => print 'w' or 'l' depending on operand size prefix (i/o insns) + 'H' => print ",pt" or ",pn" branch hint + 'I' => honor following macro letter even in Intel mode (implemented only + . for some of the macro letters) + 'J' => print 'l' + 'K' => print 'd' or 'q' if rex prefix is present. + 'L' => print 'l' if suffix_always is true + 'N' => print 'n' if instruction has no wait "prefix" + 'O' => print 'd' or 'o' (or 'q' in Intel mode) + 'P' => print 'w', 'l' or 'q' if instruction has an operand size prefix, + . or suffix_always is true. print 'q' if rex prefix is present. + 'Q' => print 'w', 'l' or 'q' if no register operands or suffix_always + . is true + 'R' => print 'w', 'l' or 'q' ('d' for 'l' and 'e' in Intel mode) + 'S' => print 'w', 'l' or 'q' if suffix_always is true + 'T' => print 'q' in 64bit mode and behave as 'P' otherwise + 'U' => print 'q' in 64bit mode and behave as 'Q' otherwise + 'V' => print 'q' in 64bit mode and behave as 'S' otherwise + 'W' => print 'b', 'w' or 'l' ('d' in Intel mode) + 'X' => print 's', 'd' depending on data16 prefix (for XMM) + 'Y' => 'q' if instruction has an REX 64bit overwrite prefix + 'Z' => print 'q' in 64bit mode and behave as 'L' otherwise + + Many of the above letters print nothing in Intel mode. See "putop" + for the details. + + Braces '{' and '}', and vertical bars '|', indicate alternative + mnemonic strings for AT&T, Intel, X86_64 AT&T, and X86_64 Intel + modes. In cases where there are only two alternatives, the X86_64 + instruction is reserved, and "(bad)" is printed. +*/ + +static const struct dis386 dis386[] = { + /* 00 */ + { "addB", { Eb, Gb } }, + { "addS", { Ev, Gv } }, + { "addB", { Gb, Eb } }, + { "addS", { Gv, Ev } }, + { "addB", { AL, Ib } }, + { "addS", { eAX, Iv } }, + { "push{T|}", { es } }, + { "pop{T|}", { es } }, + /* 08 */ + { "orB", { Eb, Gb } }, + { "orS", { Ev, Gv } }, + { "orB", { Gb, Eb } }, + { "orS", { Gv, Ev } }, + { "orB", { AL, Ib } }, + { "orS", { eAX, Iv } }, + { "push{T|}", { cs } }, + { "(bad)", { XX } }, /* 0x0f extended opcode escape */ + /* 10 */ + { "adcB", { Eb, Gb } }, + { "adcS", { Ev, Gv } }, + { "adcB", { Gb, Eb } }, + { "adcS", { Gv, Ev } }, + { "adcB", { AL, Ib } }, + { "adcS", { eAX, Iv } }, + { "push{T|}", { ss } }, + { "pop{T|}", { ss } }, + /* 18 */ + { "sbbB", { Eb, Gb } }, + { "sbbS", { Ev, Gv } }, + { "sbbB", { Gb, Eb } }, + { "sbbS", { Gv, Ev } }, + { "sbbB", { AL, Ib } }, + { "sbbS", { eAX, Iv } }, + { "push{T|}", { ds } }, + { "pop{T|}", { ds } }, + /* 20 */ + { "andB", { Eb, Gb } }, + { "andS", { Ev, Gv } }, + { "andB", { Gb, Eb } }, + { "andS", { Gv, Ev } }, + { "andB", { AL, Ib } }, + { "andS", { eAX, Iv } }, + { "(bad)", { XX } }, /* SEG ES prefix */ + { "daa{|}", { XX } }, + /* 28 */ + { "subB", { Eb, Gb } }, + { "subS", { Ev, Gv } }, + { "subB", { Gb, Eb } }, + { "subS", { Gv, Ev } }, + { "subB", { AL, Ib } }, + { "subS", { eAX, Iv } }, + { "(bad)", { XX } }, /* SEG CS prefix */ + { "das{|}", { XX } }, + /* 30 */ + { "xorB", { Eb, Gb } }, + { "xorS", { Ev, Gv } }, + { "xorB", { Gb, Eb } }, + { "xorS", { Gv, Ev } }, + { "xorB", { AL, Ib } }, + { "xorS", { eAX, Iv } }, + { "(bad)", { XX } }, /* SEG SS prefix */ + { "aaa{|}", { XX } }, + /* 38 */ + { "cmpB", { Eb, Gb } }, + { "cmpS", { Ev, Gv } }, + { "cmpB", { Gb, Eb } }, + { "cmpS", { Gv, Ev } }, + { "cmpB", { AL, Ib } }, + { "cmpS", { eAX, Iv } }, + { "(bad)", { XX } }, /* SEG DS prefix */ + { "aas{|}", { XX } }, + /* 40 */ + { "inc{S|}", { RMeAX } }, + { "inc{S|}", { RMeCX } }, + { "inc{S|}", { RMeDX } }, + { "inc{S|}", { RMeBX } }, + { "inc{S|}", { RMeSP } }, + { "inc{S|}", { RMeBP } }, + { "inc{S|}", { RMeSI } }, + { "inc{S|}", { RMeDI } }, + /* 48 */ + { "dec{S|}", { RMeAX } }, + { "dec{S|}", { RMeCX } }, + { "dec{S|}", { RMeDX } }, + { "dec{S|}", { RMeBX } }, + { "dec{S|}", { RMeSP } }, + { "dec{S|}", { RMeBP } }, + { "dec{S|}", { RMeSI } }, + { "dec{S|}", { RMeDI } }, + /* 50 */ + { "pushV", { RMrAX } }, + { "pushV", { RMrCX } }, + { "pushV", { RMrDX } }, + { "pushV", { RMrBX } }, + { "pushV", { RMrSP } }, + { "pushV", { RMrBP } }, + { "pushV", { RMrSI } }, + { "pushV", { RMrDI } }, + /* 58 */ + { "popV", { RMrAX } }, + { "popV", { RMrCX } }, + { "popV", { RMrDX } }, + { "popV", { RMrBX } }, + { "popV", { RMrSP } }, + { "popV", { RMrBP } }, + { "popV", { RMrSI } }, + { "popV", { RMrDI } }, + /* 60 */ + { X86_64_0 }, + { X86_64_1 }, + { X86_64_2 }, + { X86_64_3 }, + { "(bad)", { XX } }, /* seg fs */ + { "(bad)", { XX } }, /* seg gs */ + { "(bad)", { XX } }, /* op size prefix */ + { "(bad)", { XX } }, /* adr size prefix */ + /* 68 */ + { "pushT", { Iq } }, + { "imulS", { Gv, Ev, Iv } }, + { "pushT", { sIb } }, + { "imulS", { Gv, Ev, sIb } }, + { "ins{b||b|}", { Ybr, indirDX } }, + { "ins{R||G|}", { Yzr, indirDX } }, + { "outs{b||b|}", { indirDXr, Xb } }, + { "outs{R||G|}", { indirDXr, Xz } }, + /* 70 */ + { "joH", { Jb, XX, cond_jump_flag } }, + { "jnoH", { Jb, XX, cond_jump_flag } }, + { "jbH", { Jb, XX, cond_jump_flag } }, + { "jaeH", { Jb, XX, cond_jump_flag } }, + { "jeH", { Jb, XX, cond_jump_flag } }, + { "jneH", { Jb, XX, cond_jump_flag } }, + { "jbeH", { Jb, XX, cond_jump_flag } }, + { "jaH", { Jb, XX, cond_jump_flag } }, + /* 78 */ + { "jsH", { Jb, XX, cond_jump_flag } }, + { "jnsH", { Jb, XX, cond_jump_flag } }, + { "jpH", { Jb, XX, cond_jump_flag } }, + { "jnpH", { Jb, XX, cond_jump_flag } }, + { "jlH", { Jb, XX, cond_jump_flag } }, + { "jgeH", { Jb, XX, cond_jump_flag } }, + { "jleH", { Jb, XX, cond_jump_flag } }, + { "jgH", { Jb, XX, cond_jump_flag } }, + /* 80 */ + { GRP1b }, + { GRP1S }, + { "(bad)", { XX } }, + { GRP1Ss }, + { "testB", { Eb, Gb } }, + { "testS", { Ev, Gv } }, + { "xchgB", { Eb, Gb } }, + { "xchgS", { Ev, Gv } }, + /* 88 */ + { "movB", { Eb, Gb } }, + { "movS", { Ev, Gv } }, + { "movB", { Gb, Eb } }, + { "movS", { Gv, Ev } }, + { "movD", { Sv, Sw } }, + { "leaS", { Gv, M } }, + { "movD", { Sw, Sv } }, + { GRP1a }, + /* 90 */ + { PREGRP38 }, + { "xchgS", { RMeCX, eAX } }, + { "xchgS", { RMeDX, eAX } }, + { "xchgS", { RMeBX, eAX } }, + { "xchgS", { RMeSP, eAX } }, + { "xchgS", { RMeBP, eAX } }, + { "xchgS", { RMeSI, eAX } }, + { "xchgS", { RMeDI, eAX } }, + /* 98 */ + { "cW{t||t|}R", { XX } }, + { "cR{t||t|}O", { XX } }, + { "Jcall{T|}", { Ap } }, + { "(bad)", { XX } }, /* fwait */ + { "pushfT", { XX } }, + { "popfT", { XX } }, + { "sahf{|}", { XX } }, + { "lahf{|}", { XX } }, + /* a0 */ + { "movB", { AL, Ob } }, + { "movS", { eAX, Ov } }, + { "movB", { Ob, AL } }, + { "movS", { Ov, eAX } }, + { "movs{b||b|}", { Ybr, Xb } }, + { "movs{R||R|}", { Yvr, Xv } }, + { "cmps{b||b|}", { Xb, Yb } }, + { "cmps{R||R|}", { Xv, Yv } }, + /* a8 */ + { "testB", { AL, Ib } }, + { "testS", { eAX, Iv } }, + { "stosB", { Ybr, AL } }, + { "stosS", { Yvr, eAX } }, + { "lodsB", { ALr, Xb } }, + { "lodsS", { eAXr, Xv } }, + { "scasB", { AL, Yb } }, + { "scasS", { eAX, Yv } }, + /* b0 */ + { "movB", { RMAL, Ib } }, + { "movB", { RMCL, Ib } }, + { "movB", { RMDL, Ib } }, + { "movB", { RMBL, Ib } }, + { "movB", { RMAH, Ib } }, + { "movB", { RMCH, Ib } }, + { "movB", { RMDH, Ib } }, + { "movB", { RMBH, Ib } }, + /* b8 */ + { "movS", { RMeAX, Iv64 } }, + { "movS", { RMeCX, Iv64 } }, + { "movS", { RMeDX, Iv64 } }, + { "movS", { RMeBX, Iv64 } }, + { "movS", { RMeSP, Iv64 } }, + { "movS", { RMeBP, Iv64 } }, + { "movS", { RMeSI, Iv64 } }, + { "movS", { RMeDI, Iv64 } }, + /* c0 */ + { GRP2b }, + { GRP2S }, + { "retT", { Iw } }, + { "retT", { XX } }, + { "les{S|}", { Gv, Mp } }, + { "ldsS", { Gv, Mp } }, + { GRP11_C6 }, + { GRP11_C7 }, + /* c8 */ + { "enterT", { Iw, Ib } }, + { "leaveT", { XX } }, + { "lretP", { Iw } }, + { "lretP", { XX } }, + { "int3", { XX } }, + { "int", { Ib } }, + { "into{|}", { XX } }, + { "iretP", { XX } }, + /* d0 */ + { GRP2b_one }, + { GRP2S_one }, + { GRP2b_cl }, + { GRP2S_cl }, + { "aam{|}", { sIb } }, + { "aad{|}", { sIb } }, + { "(bad)", { XX } }, + { "xlat", { DSBX } }, + /* d8 */ + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + /* e0 */ + { "loopneFH", { Jb, XX, loop_jcxz_flag } }, + { "loopeFH", { Jb, XX, loop_jcxz_flag } }, + { "loopFH", { Jb, XX, loop_jcxz_flag } }, + { "jEcxzH", { Jb, XX, loop_jcxz_flag } }, + { "inB", { AL, Ib } }, + { "inG", { zAX, Ib } }, + { "outB", { Ib, AL } }, + { "outG", { Ib, zAX } }, + /* e8 */ + { "callT", { Jv } }, + { "jmpT", { Jv } }, + { "Jjmp{T|}", { Ap } }, + { "jmp", { Jb } }, + { "inB", { AL, indirDX } }, + { "inG", { zAX, indirDX } }, + { "outB", { indirDX, AL } }, + { "outG", { indirDX, zAX } }, + /* f0 */ + { "(bad)", { XX } }, /* lock prefix */ + { "icebp", { XX } }, + { "(bad)", { XX } }, /* repne */ + { "(bad)", { XX } }, /* repz */ + { "hlt", { XX } }, + { "cmc", { XX } }, + { GRP3b }, + { GRP3S }, + /* f8 */ + { "clc", { XX } }, + { "stc", { XX } }, + { "cli", { XX } }, + { "sti", { XX } }, + { "cld", { XX } }, + { "std", { XX } }, + { GRP4 }, + { GRP5 }, +}; + +static const struct dis386 dis386_twobyte[] = { + /* 00 */ + { GRP6 }, + { GRP7 }, + { "larS", { Gv, Ew } }, + { "lslS", { Gv, Ew } }, + { "(bad)", { XX } }, + { "syscall", { XX } }, + { "clts", { XX } }, + { "sysretP", { XX } }, + /* 08 */ + { "invd", { XX } }, + { "wbinvd", { XX } }, + { "(bad)", { XX } }, + { "ud2a", { XX } }, + { "(bad)", { XX } }, + { GRPAMD }, + { "femms", { XX } }, + { "", { MX, EM, OPSUF } }, /* See OP_3DNowSuffix. */ + /* 10 */ + { PREGRP8 }, + { PREGRP9 }, + { PREGRP30 }, + { "movlpX", { EXq, XM, { SIMD_Fixup, 'h' } } }, + { "unpcklpX", { XM, EXq } }, + { "unpckhpX", { XM, EXq } }, + { PREGRP31 }, + { "movhpX", { EXq, XM, { SIMD_Fixup, 'l' } } }, + /* 18 */ + { GRP16 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "nopQ", { Ev } }, + /* 20 */ + { "movZ", { Rm, Cm } }, + { "movZ", { Rm, Dm } }, + { "movZ", { Cm, Rm } }, + { "movZ", { Dm, Rm } }, + { "movL", { Rd, Td } }, + { "(bad)", { XX } }, + { "movL", { Td, Rd } }, + { "(bad)", { XX } }, + /* 28 */ + { "movapX", { XM, EXx } }, + { "movapX", { EXx, XM } }, + { PREGRP2 }, + { PREGRP33 }, + { PREGRP4 }, + { PREGRP3 }, + { PREGRP93 }, + { PREGRP94 }, + /* 30 */ + { "wrmsr", { XX } }, + { "rdtsc", { XX } }, + { "rdmsr", { XX } }, + { "rdpmc", { XX } }, + { "sysenter", { XX } }, + { "sysexit", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 38 */ + { THREE_BYTE_0 }, + { "(bad)", { XX } }, + { THREE_BYTE_1 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 40 */ + { "cmovo", { Gv, Ev } }, + { "cmovno", { Gv, Ev } }, + { "cmovb", { Gv, Ev } }, + { "cmovae", { Gv, Ev } }, + { "cmove", { Gv, Ev } }, + { "cmovne", { Gv, Ev } }, + { "cmovbe", { Gv, Ev } }, + { "cmova", { Gv, Ev } }, + /* 48 */ + { "cmovs", { Gv, Ev } }, + { "cmovns", { Gv, Ev } }, + { "cmovp", { Gv, Ev } }, + { "cmovnp", { Gv, Ev } }, + { "cmovl", { Gv, Ev } }, + { "cmovge", { Gv, Ev } }, + { "cmovle", { Gv, Ev } }, + { "cmovg", { Gv, Ev } }, + /* 50 */ + { "movmskpX", { Gdq, XS } }, + { PREGRP13 }, + { PREGRP12 }, + { PREGRP11 }, + { "andpX", { XM, EXx } }, + { "andnpX", { XM, EXx } }, + { "orpX", { XM, EXx } }, + { "xorpX", { XM, EXx } }, + /* 58 */ + { PREGRP0 }, + { PREGRP10 }, + { PREGRP17 }, + { PREGRP16 }, + { PREGRP14 }, + { PREGRP7 }, + { PREGRP5 }, + { PREGRP6 }, + /* 60 */ + { PREGRP95 }, + { PREGRP96 }, + { PREGRP97 }, + { "packsswb", { MX, EM } }, + { "pcmpgtb", { MX, EM } }, + { "pcmpgtw", { MX, EM } }, + { "pcmpgtd", { MX, EM } }, + { "packuswb", { MX, EM } }, + /* 68 */ + { "punpckhbw", { MX, EM } }, + { "punpckhwd", { MX, EM } }, + { "punpckhdq", { MX, EM } }, + { "packssdw", { MX, EM } }, + { PREGRP26 }, + { PREGRP24 }, + { "movd", { MX, Edq } }, + { PREGRP19 }, + /* 70 */ + { PREGRP22 }, + { GRP12 }, + { GRP13 }, + { GRP14 }, + { "pcmpeqb", { MX, EM } }, + { "pcmpeqw", { MX, EM } }, + { "pcmpeqd", { MX, EM } }, + { "emms", { XX } }, + /* 78 */ + { PREGRP34 }, + { PREGRP35 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { PREGRP28 }, + { PREGRP29 }, + { PREGRP23 }, + { PREGRP20 }, + /* 80 */ + { "joH", { Jv, XX, cond_jump_flag } }, + { "jnoH", { Jv, XX, cond_jump_flag } }, + { "jbH", { Jv, XX, cond_jump_flag } }, + { "jaeH", { Jv, XX, cond_jump_flag } }, + { "jeH", { Jv, XX, cond_jump_flag } }, + { "jneH", { Jv, XX, cond_jump_flag } }, + { "jbeH", { Jv, XX, cond_jump_flag } }, + { "jaH", { Jv, XX, cond_jump_flag } }, + /* 88 */ + { "jsH", { Jv, XX, cond_jump_flag } }, + { "jnsH", { Jv, XX, cond_jump_flag } }, + { "jpH", { Jv, XX, cond_jump_flag } }, + { "jnpH", { Jv, XX, cond_jump_flag } }, + { "jlH", { Jv, XX, cond_jump_flag } }, + { "jgeH", { Jv, XX, cond_jump_flag } }, + { "jleH", { Jv, XX, cond_jump_flag } }, + { "jgH", { Jv, XX, cond_jump_flag } }, + /* 90 */ + { "seto", { Eb } }, + { "setno", { Eb } }, + { "setb", { Eb } }, + { "setae", { Eb } }, + { "sete", { Eb } }, + { "setne", { Eb } }, + { "setbe", { Eb } }, + { "seta", { Eb } }, + /* 98 */ + { "sets", { Eb } }, + { "setns", { Eb } }, + { "setp", { Eb } }, + { "setnp", { Eb } }, + { "setl", { Eb } }, + { "setge", { Eb } }, + { "setle", { Eb } }, + { "setg", { Eb } }, + /* a0 */ + { "pushT", { fs } }, + { "popT", { fs } }, + { "cpuid", { XX } }, + { "btS", { Ev, Gv } }, + { "shldS", { Ev, Gv, Ib } }, + { "shldS", { Ev, Gv, CL } }, + { GRPPADLCK2 }, + { GRPPADLCK1 }, + /* a8 */ + { "pushT", { gs } }, + { "popT", { gs } }, + { "rsm", { XX } }, + { "btsS", { Ev, Gv } }, + { "shrdS", { Ev, Gv, Ib } }, + { "shrdS", { Ev, Gv, CL } }, + { GRP15 }, + { "imulS", { Gv, Ev } }, + /* b0 */ + { "cmpxchgB", { Eb, Gb } }, + { "cmpxchgS", { Ev, Gv } }, + { "lssS", { Gv, Mp } }, + { "btrS", { Ev, Gv } }, + { "lfsS", { Gv, Mp } }, + { "lgsS", { Gv, Mp } }, + { "movz{bR|x|bR|x}", { Gv, Eb } }, + { "movz{wR|x|wR|x}", { Gv, Ew } }, /* yes, there really is movzww ! */ + /* b8 */ + { PREGRP37 }, + { "ud2b", { XX } }, + { GRP8 }, + { "btcS", { Ev, Gv } }, + { "bsfS", { Gv, Ev } }, + { PREGRP36 }, + { "movs{bR|x|bR|x}", { Gv, Eb } }, + { "movs{wR|x|wR|x}", { Gv, Ew } }, /* yes, there really is movsww ! */ + /* c0 */ + { "xaddB", { Eb, Gb } }, + { "xaddS", { Ev, Gv } }, + { PREGRP1 }, + { "movntiS", { Ev, Gv } }, + { "pinsrw", { MX, Edqw, Ib } }, + { "pextrw", { Gdq, MS, Ib } }, + { "shufpX", { XM, EXx, Ib } }, + { GRP9 }, + /* c8 */ + { "bswap", { RMeAX } }, + { "bswap", { RMeCX } }, + { "bswap", { RMeDX } }, + { "bswap", { RMeBX } }, + { "bswap", { RMeSP } }, + { "bswap", { RMeBP } }, + { "bswap", { RMeSI } }, + { "bswap", { RMeDI } }, + /* d0 */ + { PREGRP27 }, + { "psrlw", { MX, EM } }, + { "psrld", { MX, EM } }, + { "psrlq", { MX, EM } }, + { "paddq", { MX, EM } }, + { "pmullw", { MX, EM } }, + { PREGRP21 }, + { "pmovmskb", { Gdq, MS } }, + /* d8 */ + { "psubusb", { MX, EM } }, + { "psubusw", { MX, EM } }, + { "pminub", { MX, EM } }, + { "pand", { MX, EM } }, + { "paddusb", { MX, EM } }, + { "paddusw", { MX, EM } }, + { "pmaxub", { MX, EM } }, + { "pandn", { MX, EM } }, + /* e0 */ + { "pavgb", { MX, EM } }, + { "psraw", { MX, EM } }, + { "psrad", { MX, EM } }, + { "pavgw", { MX, EM } }, + { "pmulhuw", { MX, EM } }, + { "pmulhw", { MX, EM } }, + { PREGRP15 }, + { PREGRP25 }, + /* e8 */ + { "psubsb", { MX, EM } }, + { "psubsw", { MX, EM } }, + { "pminsw", { MX, EM } }, + { "por", { MX, EM } }, + { "paddsb", { MX, EM } }, + { "paddsw", { MX, EM } }, + { "pmaxsw", { MX, EM } }, + { "pxor", { MX, EM } }, + /* f0 */ + { PREGRP32 }, + { "psllw", { MX, EM } }, + { "pslld", { MX, EM } }, + { "psllq", { MX, EM } }, + { "pmuludq", { MX, EM } }, + { "pmaddwd", { MX, EM } }, + { "psadbw", { MX, EM } }, + { PREGRP18 }, + /* f8 */ + { "psubb", { MX, EM } }, + { "psubw", { MX, EM } }, + { "psubd", { MX, EM } }, + { "psubq", { MX, EM } }, + { "paddb", { MX, EM } }, + { "paddw", { MX, EM } }, + { "paddd", { MX, EM } }, + { "(bad)", { XX } }, +}; + +static const unsigned char onebyte_has_modrm[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 00 */ + /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */ + /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */ + /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */ + /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 60 */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */ + /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 80 */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */ + /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* c0 */ + /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* d0 */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0 */ + /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 /* f0 */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static const unsigned char twobyte_has_modrm[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1, /* 0f */ + /* 10 */ 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1, /* 1f */ + /* 20 */ 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, /* 3f */ + /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */ + /* 50 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 5f */ + /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6f */ + /* 70 */ 1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */ + /* a0 */ 0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1, /* af */ + /* b0 */ 1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1, /* bf */ + /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* df */ + /* e0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* ef */ + /* f0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static const unsigned char twobyte_uses_DATA_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, /* 6f */ + /* 70 */ 1,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0 /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static const unsigned char twobyte_uses_REPNZ_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,1,0,0,0,0,0,0,1,1,1,0,1,1,1,1, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */ + /* 70 */ 1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static const unsigned char twobyte_uses_REPZ_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, /* 6f */ + /* 70 */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, /* bf */ + /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +/* This is used to determine if opcode 0f 38 XX uses DATA prefix. */ +static const unsigned char threebyte_0x38_uses_DATA_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, /* 0f */ + /* 10 */ 1,0,0,0,1,1,0,1,0,0,0,0,1,1,1,0, /* 1f */ + /* 20 */ 1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0, /* 2f */ + /* 30 */ 1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1, /* 3f */ + /* 40 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1, /* df */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +/* This is used to determine if opcode 0f 38 XX uses REPNZ prefix. */ +static const unsigned char threebyte_0x38_uses_REPNZ_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0, /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +/* This is used to determine if opcode 0f 38 XX uses REPZ prefix. */ +static const unsigned char threebyte_0x38_uses_REPZ_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +/* This is used to determine if opcode 0f 3a XX uses DATA prefix. */ +static const unsigned char threebyte_0x3a_uses_DATA_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, /* 0f */ + /* 10 */ 0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ + /* 60 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, /* df */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +/* This is used to determine if opcode 0f 3a XX uses REPNZ prefix. */ +static const unsigned char threebyte_0x3a_uses_REPNZ_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +/* This is used to determine if opcode 0f 3a XX uses REPZ prefix. */ +static const unsigned char threebyte_0x3a_uses_REPZ_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static char obuf[100]; +static char *obufp; +static char scratchbuf[100]; +static unsigned char *start_codep; +static unsigned char *insn_codep; +static unsigned char *codep; +static disassemble_info *the_info; +static struct + { + int mod; + int reg; + int rm; + } +modrm; +static unsigned char need_modrm; + +/* If we are accessing mod/rm/reg without need_modrm set, then the + values are stale. Hitting this abort likely indicates that you + need to update onebyte_has_modrm or twobyte_has_modrm. */ +#define MODRM_CHECK if (!need_modrm) abort () + +static const char * const *names64; +static const char * const *names32; +static const char * const *names16; +static const char * const *names8; +static const char * const *names8rex; +static const char * const *names_seg; +static const char * const *index16; + +static const char * const intel_names64[] = { + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" +}; +static const char * const intel_names32[] = { + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" +}; +static const char * const intel_names16[] = { + "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" +}; +static const char * const intel_names8[] = { + "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", +}; +static const char * const intel_names8rex[] = { + "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" +}; +static const char * const intel_names_seg[] = { + "es", "cs", "ss", "ds", "fs", "gs", "?", "?", +}; +static const char * const intel_index16[] = { + "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx" +}; + +static const char * const att_names64[] = { + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" +}; +static const char * const att_names32[] = { + "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", + "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" +}; +static const char * const att_names16[] = { + "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di", + "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" +}; +static const char * const att_names8[] = { + "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh", +}; +static const char * const att_names8rex[] = { + "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil", + "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" +}; +static const char * const att_names_seg[] = { + "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "%?", "%?", +}; +static const char * const att_index16[] = { + "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx" +}; + +static const struct dis386 grps[][8] = { + /* GRP1a */ + { + { "popU", { stackEv } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* GRP1b */ + { + { "addA", { Eb, Ib } }, + { "orA", { Eb, Ib } }, + { "adcA", { Eb, Ib } }, + { "sbbA", { Eb, Ib } }, + { "andA", { Eb, Ib } }, + { "subA", { Eb, Ib } }, + { "xorA", { Eb, Ib } }, + { "cmpA", { Eb, Ib } }, + }, + /* GRP1S */ + { + { "addQ", { Ev, Iv } }, + { "orQ", { Ev, Iv } }, + { "adcQ", { Ev, Iv } }, + { "sbbQ", { Ev, Iv } }, + { "andQ", { Ev, Iv } }, + { "subQ", { Ev, Iv } }, + { "xorQ", { Ev, Iv } }, + { "cmpQ", { Ev, Iv } }, + }, + /* GRP1Ss */ + { + { "addQ", { Ev, sIb } }, + { "orQ", { Ev, sIb } }, + { "adcQ", { Ev, sIb } }, + { "sbbQ", { Ev, sIb } }, + { "andQ", { Ev, sIb } }, + { "subQ", { Ev, sIb } }, + { "xorQ", { Ev, sIb } }, + { "cmpQ", { Ev, sIb } }, + }, + /* GRP2b */ + { + { "rolA", { Eb, Ib } }, + { "rorA", { Eb, Ib } }, + { "rclA", { Eb, Ib } }, + { "rcrA", { Eb, Ib } }, + { "shlA", { Eb, Ib } }, + { "shrA", { Eb, Ib } }, + { "(bad)", { XX } }, + { "sarA", { Eb, Ib } }, + }, + /* GRP2S */ + { + { "rolQ", { Ev, Ib } }, + { "rorQ", { Ev, Ib } }, + { "rclQ", { Ev, Ib } }, + { "rcrQ", { Ev, Ib } }, + { "shlQ", { Ev, Ib } }, + { "shrQ", { Ev, Ib } }, + { "(bad)", { XX } }, + { "sarQ", { Ev, Ib } }, + }, + /* GRP2b_one */ + { + { "rolA", { Eb, I1 } }, + { "rorA", { Eb, I1 } }, + { "rclA", { Eb, I1 } }, + { "rcrA", { Eb, I1 } }, + { "shlA", { Eb, I1 } }, + { "shrA", { Eb, I1 } }, + { "(bad)", { XX } }, + { "sarA", { Eb, I1 } }, + }, + /* GRP2S_one */ + { + { "rolQ", { Ev, I1 } }, + { "rorQ", { Ev, I1 } }, + { "rclQ", { Ev, I1 } }, + { "rcrQ", { Ev, I1 } }, + { "shlQ", { Ev, I1 } }, + { "shrQ", { Ev, I1 } }, + { "(bad)", { XX } }, + { "sarQ", { Ev, I1 } }, + }, + /* GRP2b_cl */ + { + { "rolA", { Eb, CL } }, + { "rorA", { Eb, CL } }, + { "rclA", { Eb, CL } }, + { "rcrA", { Eb, CL } }, + { "shlA", { Eb, CL } }, + { "shrA", { Eb, CL } }, + { "(bad)", { XX } }, + { "sarA", { Eb, CL } }, + }, + /* GRP2S_cl */ + { + { "rolQ", { Ev, CL } }, + { "rorQ", { Ev, CL } }, + { "rclQ", { Ev, CL } }, + { "rcrQ", { Ev, CL } }, + { "shlQ", { Ev, CL } }, + { "shrQ", { Ev, CL } }, + { "(bad)", { XX } }, + { "sarQ", { Ev, CL } }, + }, + /* GRP3b */ + { + { "testA", { Eb, Ib } }, + { "(bad)", { Eb } }, + { "notA", { Eb } }, + { "negA", { Eb } }, + { "mulA", { Eb } }, /* Don't print the implicit %al register, */ + { "imulA", { Eb } }, /* to distinguish these opcodes from other */ + { "divA", { Eb } }, /* mul/imul opcodes. Do the same for div */ + { "idivA", { Eb } }, /* and idiv for consistency. */ + }, + /* GRP3S */ + { + { "testQ", { Ev, Iv } }, + { "(bad)", { XX } }, + { "notQ", { Ev } }, + { "negQ", { Ev } }, + { "mulQ", { Ev } }, /* Don't print the implicit register. */ + { "imulQ", { Ev } }, + { "divQ", { Ev } }, + { "idivQ", { Ev } }, + }, + /* GRP4 */ + { + { "incA", { Eb } }, + { "decA", { Eb } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* GRP5 */ + { + { "incQ", { Ev } }, + { "decQ", { Ev } }, + { "callT", { indirEv } }, + { "JcallT", { indirEp } }, + { "jmpT", { indirEv } }, + { "JjmpT", { indirEp } }, + { "pushU", { stackEv } }, + { "(bad)", { XX } }, + }, + /* GRP6 */ + { + { "sldtD", { Sv } }, + { "strD", { Sv } }, + { "lldt", { Ew } }, + { "ltr", { Ew } }, + { "verr", { Ew } }, + { "verw", { Ew } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* GRP7 */ + { + { "sgdt{Q|IQ||}", { { VMX_Fixup, 0 } } }, + { "sidt{Q|IQ||}", { { PNI_Fixup, 0 } } }, + { "lgdt{Q|Q||}", { M } }, + { "lidt{Q|Q||}", { { SVME_Fixup, 0 } } }, + { "smswD", { Sv } }, + { "(bad)", { XX } }, + { "lmsw", { Ew } }, + { "invlpg", { { INVLPG_Fixup, w_mode } } }, + }, + /* GRP8 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "btQ", { Ev, Ib } }, + { "btsQ", { Ev, Ib } }, + { "btrQ", { Ev, Ib } }, + { "btcQ", { Ev, Ib } }, + }, + /* GRP9 */ + { + { "(bad)", { XX } }, + { "cmpxchg8b", { { CMPXCHG8B_Fixup, q_mode } } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "", { VM } }, /* See OP_VMX. */ + { "vmptrst", { Mq } }, + }, + /* GRP11_C6 */ + { + { "movA", { Eb, Ib } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* GRP11_C7 */ + { + { "movQ", { Ev, Iv } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* GRP12 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "psrlw", { MS, Ib } }, + { "(bad)", { XX } }, + { "psraw", { MS, Ib } }, + { "(bad)", { XX } }, + { "psllw", { MS, Ib } }, + { "(bad)", { XX } }, + }, + /* GRP13 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "psrld", { MS, Ib } }, + { "(bad)", { XX } }, + { "psrad", { MS, Ib } }, + { "(bad)", { XX } }, + { "pslld", { MS, Ib } }, + { "(bad)", { XX } }, + }, + /* GRP14 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "psrlq", { MS, Ib } }, + { "psrldq", { MS, Ib } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "psllq", { MS, Ib } }, + { "pslldq", { MS, Ib } }, + }, + /* GRP15 */ + { + { "fxsave", { Ev } }, + { "fxrstor", { Ev } }, + { "ldmxcsr", { Ev } }, + { "stmxcsr", { Ev } }, + { "(bad)", { XX } }, + { "lfence", { { OP_0fae, 0 } } }, + { "mfence", { { OP_0fae, 0 } } }, + { "clflush", { { OP_0fae, 0 } } }, + }, + /* GRP16 */ + { + { "prefetchnta", { Ev } }, + { "prefetcht0", { Ev } }, + { "prefetcht1", { Ev } }, + { "prefetcht2", { Ev } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* GRPAMD */ + { + { "prefetch", { Eb } }, + { "prefetchw", { Eb } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* GRPPADLCK1 */ + { + { "xstore-rng", { { OP_0f07, 0 } } }, + { "xcrypt-ecb", { { OP_0f07, 0 } } }, + { "xcrypt-cbc", { { OP_0f07, 0 } } }, + { "xcrypt-ctr", { { OP_0f07, 0 } } }, + { "xcrypt-cfb", { { OP_0f07, 0 } } }, + { "xcrypt-ofb", { { OP_0f07, 0 } } }, + { "(bad)", { { OP_0f07, 0 } } }, + { "(bad)", { { OP_0f07, 0 } } }, + }, + /* GRPPADLCK2 */ + { + { "montmul", { { OP_0f07, 0 } } }, + { "xsha1", { { OP_0f07, 0 } } }, + { "xsha256", { { OP_0f07, 0 } } }, + { "(bad)", { { OP_0f07, 0 } } }, + { "(bad)", { { OP_0f07, 0 } } }, + { "(bad)", { { OP_0f07, 0 } } }, + { "(bad)", { { OP_0f07, 0 } } }, + { "(bad)", { { OP_0f07, 0 } } }, + } +}; + +static const struct dis386 prefix_user_table[][4] = { + /* PREGRP0 */ + { + { "addps", { XM, EXx } }, + { "addss", { XM, EXd } }, + { "addpd", { XM, EXx } }, + { "addsd", { XM, EXq } }, + }, + /* PREGRP1 */ + { + { "", { XM, EXx, OPSIMD } }, /* See OP_SIMD_SUFFIX. */ + { "", { XM, EXx, OPSIMD } }, + { "", { XM, EXx, OPSIMD } }, + { "", { XM, EXx, OPSIMD } }, + }, + /* PREGRP2 */ + { + { "cvtpi2ps", { XM, EMC } }, + { "cvtsi2ssY", { XM, Ev } }, + { "cvtpi2pd", { XM, EMC } }, + { "cvtsi2sdY", { XM, Ev } }, + }, + /* PREGRP3 */ + { + { "cvtps2pi", { MXC, EXx } }, + { "cvtss2siY", { Gv, EXx } }, + { "cvtpd2pi", { MXC, EXx } }, + { "cvtsd2siY", { Gv, EXx } }, + }, + /* PREGRP4 */ + { + { "cvttps2pi", { MXC, EXx } }, + { "cvttss2siY", { Gv, EXx } }, + { "cvttpd2pi", { MXC, EXx } }, + { "cvttsd2siY", { Gv, EXx } }, + }, + /* PREGRP5 */ + { + { "divps", { XM, EXx } }, + { "divss", { XM, EXx } }, + { "divpd", { XM, EXx } }, + { "divsd", { XM, EXx } }, + }, + /* PREGRP6 */ + { + { "maxps", { XM, EXx } }, + { "maxss", { XM, EXx } }, + { "maxpd", { XM, EXx } }, + { "maxsd", { XM, EXx } }, + }, + /* PREGRP7 */ + { + { "minps", { XM, EXx } }, + { "minss", { XM, EXx } }, + { "minpd", { XM, EXx } }, + { "minsd", { XM, EXx } }, + }, + /* PREGRP8 */ + { + { "movups", { XM, EXx } }, + { "movss", { XM, EXx } }, + { "movupd", { XM, EXx } }, + { "movsd", { XM, EXx } }, + }, + /* PREGRP9 */ + { + { "movups", { EXx, XM } }, + { "movss", { EXx, XM } }, + { "movupd", { EXx, XM } }, + { "movsd", { EXx, XM } }, + }, + /* PREGRP10 */ + { + { "mulps", { XM, EXx } }, + { "mulss", { XM, EXx } }, + { "mulpd", { XM, EXx } }, + { "mulsd", { XM, EXx } }, + }, + /* PREGRP11 */ + { + { "rcpps", { XM, EXx } }, + { "rcpss", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + }, + /* PREGRP12 */ + { + { "rsqrtps",{ XM, EXx } }, + { "rsqrtss",{ XM, EXx } }, + { "(bad)", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + }, + /* PREGRP13 */ + { + { "sqrtps", { XM, EXx } }, + { "sqrtss", { XM, EXx } }, + { "sqrtpd", { XM, EXx } }, + { "sqrtsd", { XM, EXx } }, + }, + /* PREGRP14 */ + { + { "subps", { XM, EXx } }, + { "subss", { XM, EXx } }, + { "subpd", { XM, EXx } }, + { "subsd", { XM, EXx } }, + }, + /* PREGRP15 */ + { + { "(bad)", { XM, EXx } }, + { "cvtdq2pd", { XM, EXq } }, + { "cvttpd2dq", { XM, EXx } }, + { "cvtpd2dq", { XM, EXx } }, + }, + /* PREGRP16 */ + { + { "cvtdq2ps", { XM, EXx } }, + { "cvttps2dq", { XM, EXx } }, + { "cvtps2dq", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + }, + /* PREGRP17 */ + { + { "cvtps2pd", { XM, EXq } }, + { "cvtss2sd", { XM, EXx } }, + { "cvtpd2ps", { XM, EXx } }, + { "cvtsd2ss", { XM, EXx } }, + }, + /* PREGRP18 */ + { + { "maskmovq", { MX, MS } }, + { "(bad)", { XM, EXx } }, + { "maskmovdqu", { XM, XS } }, + { "(bad)", { XM, EXx } }, + }, + /* PREGRP19 */ + { + { "movq", { MX, EM } }, + { "movdqu", { XM, EXx } }, + { "movdqa", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + }, + /* PREGRP20 */ + { + { "movq", { EM, MX } }, + { "movdqu", { EXx, XM } }, + { "movdqa", { EXx, XM } }, + { "(bad)", { EXx, XM } }, + }, + /* PREGRP21 */ + { + { "(bad)", { EXx, XM } }, + { "movq2dq",{ XM, MS } }, + { "movq", { EXx, XM } }, + { "movdq2q",{ MX, XS } }, + }, + /* PREGRP22 */ + { + { "pshufw", { MX, EM, Ib } }, + { "pshufhw",{ XM, EXx, Ib } }, + { "pshufd", { XM, EXx, Ib } }, + { "pshuflw",{ XM, EXx, Ib } }, + }, + /* PREGRP23 */ + { + { "movd", { Edq, MX } }, + { "movq", { XM, EXx } }, + { "movd", { Edq, XM } }, + { "(bad)", { Ed, XM } }, + }, + /* PREGRP24 */ + { + { "(bad)", { MX, EXx } }, + { "(bad)", { XM, EXx } }, + { "punpckhqdq", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + }, + /* PREGRP25 */ + { + { "movntq", { EM, MX } }, + { "(bad)", { EM, XM } }, + { "movntdq",{ EM, XM } }, + { "(bad)", { EM, XM } }, + }, + /* PREGRP26 */ + { + { "(bad)", { MX, EXx } }, + { "(bad)", { XM, EXx } }, + { "punpcklqdq", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + }, + /* PREGRP27 */ + { + { "(bad)", { MX, EXx } }, + { "(bad)", { XM, EXx } }, + { "addsubpd", { XM, EXx } }, + { "addsubps", { XM, EXx } }, + }, + /* PREGRP28 */ + { + { "(bad)", { MX, EXx } }, + { "(bad)", { XM, EXx } }, + { "haddpd", { XM, EXx } }, + { "haddps", { XM, EXx } }, + }, + /* PREGRP29 */ + { + { "(bad)", { MX, EXx } }, + { "(bad)", { XM, EXx } }, + { "hsubpd", { XM, EXx } }, + { "hsubps", { XM, EXx } }, + }, + /* PREGRP30 */ + { + { "movlpX", { XM, EXq, { SIMD_Fixup, 'h' } } }, /* really only 2 operands */ + { "movsldup", { XM, EXx } }, + { "movlpd", { XM, EXq } }, + { "movddup", { XM, EXq } }, + }, + /* PREGRP31 */ + { + { "movhpX", { XM, EXq, { SIMD_Fixup, 'l' } } }, + { "movshdup", { XM, EXx } }, + { "movhpd", { XM, EXq } }, + { "(bad)", { XM, EXq } }, + }, + /* PREGRP32 */ + { + { "(bad)", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + { "(bad)", { XM, EXx } }, + { "lddqu", { XM, M } }, + }, + /* PREGRP33 */ + { + {"movntps", { Ev, XM } }, + {"movntss", { Ev, XM } }, + {"movntpd", { Ev, XM } }, + {"movntsd", { Ev, XM } }, + }, + + /* PREGRP34 */ + { + {"vmread", { Em, Gm } }, + {"(bad)", { XX } }, + {"extrq", { XS, Ib, Ib } }, + {"insertq", { XM, XS, Ib, Ib } }, + }, + + /* PREGRP35 */ + { + {"vmwrite", { Gm, Em } }, + {"(bad)", { XX } }, + {"extrq", { XM, XS } }, + {"insertq", { XM, XS } }, + }, + + /* PREGRP36 */ + { + { "bsrS", { Gv, Ev } }, + { "lzcntS", { Gv, Ev } }, + { "bsrS", { Gv, Ev } }, + { "(bad)", { XX } }, + }, + + /* PREGRP37 */ + { + { "(bad)", { XX } }, + { "popcntS", { Gv, Ev } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + + /* PREGRP38 */ + { + { "xchgS", { { NOP_Fixup1, eAX_reg }, { NOP_Fixup2, eAX_reg } } }, + { "pause", { XX } }, + { "xchgS", { { NOP_Fixup1, eAX_reg }, { NOP_Fixup2, eAX_reg } } }, + { "(bad)", { XX } }, + }, + + /* PREGRP39 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pblendvb", {XM, EXx, XMM0 } }, + { "(bad)", { XX } }, + }, + + /* PREGRP40 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "blendvps", {XM, EXx, XMM0 } }, + { "(bad)", { XX } }, + }, + + /* PREGRP41 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "blendvpd", { XM, EXx, XMM0 } }, + { "(bad)", { XX } }, + }, + + /* PREGRP42 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "ptest", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP43 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovsxbw", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP44 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovsxbd", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP45 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovsxbq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP46 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovsxwd", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP47 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovsxwq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP48 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovsxdq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP49 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmuldq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP50 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pcmpeqq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP51 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "movntdqa", { XM, EM } }, + { "(bad)", { XX } }, + }, + + /* PREGRP52 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "packusdw", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP53 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovzxbw", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP54 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovzxbd", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP55 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovzxbq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP56 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovzxwd", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP57 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovzxwq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP58 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmovzxdq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP59 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pminsb", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP60 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pminsd", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP61 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pminuw", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP62 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pminud", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP63 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmaxsb", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP64 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmaxsd", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP65 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmaxuw", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP66 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmaxud", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP67 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pmulld", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP68 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "phminposuw", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP69 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "roundps", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP70 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "roundpd", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP71 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "roundss", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP72 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "roundsd", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP73 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "blendps", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP74 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "blendpd", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP75 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pblendw", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP76 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pextrb", { Edqb, XM, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP77 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pextrw", { Edqw, XM, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP78 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pextrK", { Edq, XM, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP79 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "extractps", { Edqd, XM, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP80 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pinsrb", { XM, Edqb, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP81 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "insertps", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP82 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pinsrK", { XM, Edq, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP83 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "dpps", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP84 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "dppd", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP85 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "mpsadbw", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP86 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pcmpgtq", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP87 */ + { + { "movbe", { Gv, Ev } }, + { "(bad)", { XX } }, + { "movbe", { Gv, Ev } }, + { "crc32", { Gdq, { CRC32_Fixup, b_mode } } }, + }, + + /* PREGRP88 */ + { + { "movbe", { Ev, Gv } }, + { "(bad)", { XX } }, + { "movbe", { Ev, Gv } }, + { "crc32", { Gdq, { CRC32_Fixup, v_mode } } }, + }, + + /* PREGRP89 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pcmpestrm", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP90 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pcmpestri", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP91 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pcmpistrm", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP92 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pcmpistri", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP93 */ + { + { "ucomiss",{ XM, EXd } }, + { "(bad)", { XX } }, + { "ucomisd",{ XM, EXq } }, + { "(bad)", { XX } }, + }, + + /* PREGRP94 */ + { + { "comiss", { XM, EXd } }, + { "(bad)", { XX } }, + { "comisd", { XM, EXq } }, + { "(bad)", { XX } }, + }, + + /* PREGRP95 */ + { + { "punpcklbw",{ MX, EMd } }, + { "(bad)", { XX } }, + { "punpcklbw",{ MX, EMq } }, + { "(bad)", { XX } }, + }, + + /* PREGRP96 */ + { + { "punpcklwd",{ MX, EMd } }, + { "(bad)", { XX } }, + { "punpcklwd",{ MX, EMq } }, + { "(bad)", { XX } }, + }, + + /* PREGRP97 */ + { + { "punpckldq",{ MX, EMd } }, + { "(bad)", { XX } }, + { "punpckldq",{ MX, EMq } }, + { "(bad)", { XX } }, + }, + + /* PREGRP98 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pclmulqdq", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP99 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "aesimc", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP100 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "aesenc", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP101 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "aesenclast", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP102 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "aesdec", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP103 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "aesdeclast", { XM, EXx } }, + { "(bad)", { XX } }, + }, + + /* PREGRP104 */ + { + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "aeskeygenassist", { XM, EXx, Ib } }, + { "(bad)", { XX } }, + }, + + /* PREGRP105 */ + { + { "andnS", { Gv, Bv, Ev } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + + /* PREGRP106 */ + { + { "bextrS", { Gv, Ev, Bv } }, + { "sarxS", { Gv, Ev, Bv } }, + { "shlxS", { Gv, Ev, Bv } }, + { "shrxS", { Gv, Ev, Bv } }, + }, + +}; + +static const struct dis386 x86_64_table[][2] = { + { + { "pusha{P|}", { XX } }, + { "(bad)", { XX } }, + }, + { + { "popa{P|}", { XX } }, + { "(bad)", { XX } }, + }, + { + { "bound{S|}", { Gv, Ma } }, + { "(bad)", { XX } }, + }, + { + { "arpl", { Ew, Gw } }, + { "movs{||lq|xd}", { Gv, Ed } }, + }, +}; + +static const struct dis386 three_byte_table[][256] = { + /* THREE_BYTE_0 */ + { + /* 00 */ + { "pshufb", { MX, EM } }, + { "phaddw", { MX, EM } }, + { "phaddd", { MX, EM } }, + { "phaddsw", { MX, EM } }, + { "pmaddubsw", { MX, EM } }, + { "phsubw", { MX, EM } }, + { "phsubd", { MX, EM } }, + { "phsubsw", { MX, EM } }, + /* 08 */ + { "psignb", { MX, EM } }, + { "psignw", { MX, EM } }, + { "psignd", { MX, EM } }, + { "pmulhrsw", { MX, EM } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 10 */ + { PREGRP39 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { PREGRP40 }, + { PREGRP41 }, + { "(bad)", { XX } }, + { PREGRP42 }, + /* 18 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "pabsb", { MX, EM } }, + { "pabsw", { MX, EM } }, + { "pabsd", { MX, EM } }, + { "(bad)", { XX } }, + /* 20 */ + { PREGRP43 }, + { PREGRP44 }, + { PREGRP45 }, + { PREGRP46 }, + { PREGRP47 }, + { PREGRP48 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 28 */ + { PREGRP49 }, + { PREGRP50 }, + { PREGRP51 }, + { PREGRP52 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 30 */ + { PREGRP53 }, + { PREGRP54 }, + { PREGRP55 }, + { PREGRP56 }, + { PREGRP57 }, + { PREGRP58 }, + { "(bad)", { XX } }, + { PREGRP86 }, + /* 38 */ + { PREGRP59 }, + { PREGRP60 }, + { PREGRP61 }, + { PREGRP62 }, + { PREGRP63 }, + { PREGRP64 }, + { PREGRP65 }, + { PREGRP66 }, + /* 40 */ + { PREGRP67 }, + { PREGRP68 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 48 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 50 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 58 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 60 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 68 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 70 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 78 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 80 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 88 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 90 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 98 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* a0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* a8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* b0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* b8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* c0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* c8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* d0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* d8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { PREGRP99 }, + { PREGRP100 }, + { PREGRP101 }, + { PREGRP102 }, + { PREGRP103 }, + /* e0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* e8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* f0 */ + { PREGRP87 }, + { PREGRP88 }, + { PREGRP105 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { PREGRP106 }, + /* f8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* THREE_BYTE_1 */ + { + /* 00 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 08 */ + { PREGRP69 }, + { PREGRP70 }, + { PREGRP71 }, + { PREGRP72 }, + { PREGRP73 }, + { PREGRP74 }, + { PREGRP75 }, + { "palignr", { MX, EM, Ib } }, + /* 10 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { PREGRP76 }, + { PREGRP77 }, + { PREGRP78 }, + { PREGRP79 }, + /* 18 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 20 */ + { PREGRP80 }, + { PREGRP81 }, + { PREGRP82 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 28 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 30 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 38 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 40 */ + { PREGRP83 }, + { PREGRP84 }, + { PREGRP85 }, + { "(bad)", { XX } }, + { PREGRP98 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 48 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 50 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 58 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 60 */ + { PREGRP89 }, + { PREGRP90 }, + { PREGRP91 }, + { PREGRP92 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 68 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 70 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 78 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 80 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 88 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 90 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* 98 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* a0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* a8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* b0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* b8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* c0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* c8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* d0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* d8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { PREGRP104 }, + /* e0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* e8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* f0 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + /* f8 */ + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + } +}; + +#define INTERNAL_DISASSEMBLER_ERROR _("") + +static void +ckprefix (void) +{ + int newrex; + rex = 0; + prefixes = 0; + used_prefixes = 0; + rex_used = 0; + while (1) + { + fetch_data(the_info, codep + 1); + newrex = 0; + switch (*codep) + { + /* REX prefixes family. */ + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + if (address_mode == mode_64bit) + newrex = *codep; + else + return; + break; + case 0xf3: + prefixes |= PREFIX_REPZ; + break; + case 0xf2: + prefixes |= PREFIX_REPNZ; + break; + case 0xf0: + prefixes |= PREFIX_LOCK; + break; + case 0x2e: + prefixes |= PREFIX_CS; + break; + case 0x36: + prefixes |= PREFIX_SS; + break; + case 0x3e: + prefixes |= PREFIX_DS; + break; + case 0x26: + prefixes |= PREFIX_ES; + break; + case 0x64: + prefixes |= PREFIX_FS; + break; + case 0x65: + prefixes |= PREFIX_GS; + break; + case 0x66: + prefixes |= PREFIX_DATA; + break; + case 0x67: + prefixes |= PREFIX_ADDR; + break; + case FWAIT_OPCODE: + /* fwait is really an instruction. If there are prefixes + before the fwait, they belong to the fwait, *not* to the + following instruction. */ + if (prefixes || rex) + { + prefixes |= PREFIX_FWAIT; + codep++; + return; + } + prefixes = PREFIX_FWAIT; + break; + default: + return; + } + /* Rex is ignored when followed by another prefix. */ + if (rex) + { + rex_used = rex; + return; + } + rex = newrex; + codep++; + } +} + +static void +ckvexprefix (void) +{ + int op, vex2, vex3, newrex = 0, newpfx = prefixes; + + if (address_mode == mode_16bit) { + return; + } + + fetch_data(the_info, codep + 1); + op = *codep; + + if (op != 0xc4 && op != 0xc5) { + return; + } + + fetch_data(the_info, codep + 2); + vex2 = codep[1]; + + if (address_mode == mode_32bit && (vex2 & 0xc0) != 0xc0) { + return; + } + + if (op == 0xc4) { + /* Three byte VEX prefix. */ + fetch_data(the_info, codep + 3); + vex3 = codep[2]; + + newrex |= (vex2 & 0x80 ? 0 : REX_R); + newrex |= (vex2 & 0x40 ? 0 : REX_X); + newrex |= (vex2 & 0x20 ? 0 : REX_B); + newrex |= (vex3 & 0x80 ? REX_W : 0); + switch (vex2 & 0x1f) { /* VEX.m-mmmm */ + case 1: + newpfx |= PREFIX_VEX_0F; + break; + case 2: + newpfx |= PREFIX_VEX_0F | PREFIX_VEX_0F38; + break; + case 3: + newpfx |= PREFIX_VEX_0F | PREFIX_VEX_0F3A; + break; + } + vex2 = vex3; + codep += 3; + } else { + /* Two byte VEX prefix. */ + newrex |= (vex2 & 0x80 ? 0 : REX_R); + codep += 2; + } + + vex_reg = (~vex2 >> 3) & 15; /* VEX.vvvv */ + switch (vex2 & 3) { /* VEX.pp */ + case 1: + newpfx |= PREFIX_DATA; /* 0x66 */ + break; + case 2: + newpfx |= PREFIX_REPZ; /* 0xf3 */ + break; + case 3: + newpfx |= PREFIX_REPNZ; /* 0xf2 */ + break; + } + + rex = newrex; + prefixes = newpfx; +} + +/* Return the name of the prefix byte PREF, or NULL if PREF is not a + prefix byte. */ + +static const char * +prefix_name (int pref, int sizeflag) +{ + static const char * const rexes [16] = + { + "rex", /* 0x40 */ + "rex.B", /* 0x41 */ + "rex.X", /* 0x42 */ + "rex.XB", /* 0x43 */ + "rex.R", /* 0x44 */ + "rex.RB", /* 0x45 */ + "rex.RX", /* 0x46 */ + "rex.RXB", /* 0x47 */ + "rex.W", /* 0x48 */ + "rex.WB", /* 0x49 */ + "rex.WX", /* 0x4a */ + "rex.WXB", /* 0x4b */ + "rex.WR", /* 0x4c */ + "rex.WRB", /* 0x4d */ + "rex.WRX", /* 0x4e */ + "rex.WRXB", /* 0x4f */ + }; + + switch (pref) + { + /* REX prefixes family. */ + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + return rexes [pref - 0x40]; + case 0xf3: + return "repz"; + case 0xf2: + return "repnz"; + case 0xf0: + return "lock"; + case 0x2e: + return "cs"; + case 0x36: + return "ss"; + case 0x3e: + return "ds"; + case 0x26: + return "es"; + case 0x64: + return "fs"; + case 0x65: + return "gs"; + case 0x66: + return (sizeflag & DFLAG) ? "data16" : "data32"; + case 0x67: + if (address_mode == mode_64bit) + return (sizeflag & AFLAG) ? "addr32" : "addr64"; + else + return (sizeflag & AFLAG) ? "addr16" : "addr32"; + case FWAIT_OPCODE: + return "fwait"; + default: + return NULL; + } +} + +static char op_out[MAX_OPERANDS][100]; +static int op_ad, op_index[MAX_OPERANDS]; +static int two_source_ops; +static bfd_vma op_address[MAX_OPERANDS]; +static bfd_vma op_riprel[MAX_OPERANDS]; +static bfd_vma start_pc; + +/* + * On the 386's of 1988, the maximum length of an instruction is 15 bytes. + * (see topic "Redundant prefixes" in the "Differences from 8086" + * section of the "Virtual 8086 Mode" chapter.) + * 'pc' should be the address of this instruction, it will + * be used to print the target address if this is a relative jump or call + * The function returns the length of this instruction in bytes. + */ + +static char intel_syntax; +static char open_char; +static char close_char; +static char separator_char; +static char scale_char; + +int +print_insn_i386 (bfd_vma pc, disassemble_info *info) +{ + intel_syntax = -1; + + return print_insn (pc, info); +} + +static int +print_insn (bfd_vma pc, disassemble_info *info) +{ + const struct dis386 *dp; + int i; + char *op_txt[MAX_OPERANDS]; + int needcomma; + unsigned char uses_DATA_prefix, uses_LOCK_prefix; + unsigned char uses_REPNZ_prefix, uses_REPZ_prefix; + int sizeflag; + const char *p; + struct dis_private priv; + unsigned char op; + unsigned char threebyte; + + if (info->mach == bfd_mach_x86_64_intel_syntax + || info->mach == bfd_mach_x86_64) + address_mode = mode_64bit; + else + address_mode = mode_32bit; + + if (intel_syntax == (char) -1) + intel_syntax = (info->mach == bfd_mach_i386_i386_intel_syntax + || info->mach == bfd_mach_x86_64_intel_syntax); + + if (info->mach == bfd_mach_i386_i386 + || info->mach == bfd_mach_x86_64 + || info->mach == bfd_mach_i386_i386_intel_syntax + || info->mach == bfd_mach_x86_64_intel_syntax) + priv.orig_sizeflag = AFLAG | DFLAG; + else if (info->mach == bfd_mach_i386_i8086) + priv.orig_sizeflag = 0; + else + abort (); + + for (p = info->disassembler_options; p != NULL; ) + { + if (strncmp (p, "x86-64", 6) == 0) + { + address_mode = mode_64bit; + priv.orig_sizeflag = AFLAG | DFLAG; + } + else if (strncmp (p, "i386", 4) == 0) + { + address_mode = mode_32bit; + priv.orig_sizeflag = AFLAG | DFLAG; + } + else if (strncmp (p, "i8086", 5) == 0) + { + address_mode = mode_16bit; + priv.orig_sizeflag = 0; + } + else if (strncmp (p, "intel", 5) == 0) + { + intel_syntax = 1; + } + else if (strncmp (p, "att", 3) == 0) + { + intel_syntax = 0; + } + else if (strncmp (p, "addr", 4) == 0) + { + if (address_mode == mode_64bit) + { + if (p[4] == '3' && p[5] == '2') + priv.orig_sizeflag &= ~AFLAG; + else if (p[4] == '6' && p[5] == '4') + priv.orig_sizeflag |= AFLAG; + } + else + { + if (p[4] == '1' && p[5] == '6') + priv.orig_sizeflag &= ~AFLAG; + else if (p[4] == '3' && p[5] == '2') + priv.orig_sizeflag |= AFLAG; + } + } + else if (strncmp (p, "data", 4) == 0) + { + if (p[4] == '1' && p[5] == '6') + priv.orig_sizeflag &= ~DFLAG; + else if (p[4] == '3' && p[5] == '2') + priv.orig_sizeflag |= DFLAG; + } + else if (strncmp (p, "suffix", 6) == 0) + priv.orig_sizeflag |= SUFFIX_ALWAYS; + + p = strchr (p, ','); + if (p != NULL) + p++; + } + + if (intel_syntax) + { + names64 = intel_names64; + names32 = intel_names32; + names16 = intel_names16; + names8 = intel_names8; + names8rex = intel_names8rex; + names_seg = intel_names_seg; + index16 = intel_index16; + open_char = '['; + close_char = ']'; + separator_char = '+'; + scale_char = '*'; + } + else + { + names64 = att_names64; + names32 = att_names32; + names16 = att_names16; + names8 = att_names8; + names8rex = att_names8rex; + names_seg = att_names_seg; + index16 = att_index16; + open_char = '('; + close_char = ')'; + separator_char = ','; + scale_char = ','; + } + + /* The output looks better if we put 7 bytes on a line, since that + puts most long word instructions on a single line. */ + info->bytes_per_line = 7; + + info->private_data = &priv; + priv.max_fetched = priv.the_buffer; + priv.insn_start = pc; + + obuf[0] = 0; + for (i = 0; i < MAX_OPERANDS; ++i) + { + op_out[i][0] = 0; + op_index[i] = -1; + } + + the_info = info; + start_pc = pc; + start_codep = priv.the_buffer; + codep = priv.the_buffer; + + if (sigsetjmp(priv.bailout, 0) != 0) + { + const char *name; + + /* Getting here means we tried for data but didn't get it. That + means we have an incomplete instruction of some sort. Just + print the first byte as a prefix or a .byte pseudo-op. */ + if (codep > priv.the_buffer) + { + name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); + if (name != NULL) + (*info->fprintf_func) (info->stream, "%s", name); + else + { + /* Just print the first byte as a .byte instruction. */ + (*info->fprintf_func) (info->stream, ".byte 0x%x", + (unsigned int) priv.the_buffer[0]); + } + + return 1; + } + + return -1; + } + + obufp = obuf; + ckprefix (); + ckvexprefix (); + + insn_codep = codep; + sizeflag = priv.orig_sizeflag; + + fetch_data(info, codep + 1); + two_source_ops = (*codep == 0x62) || (*codep == 0xc8); + + if (((prefixes & PREFIX_FWAIT) + && ((*codep < 0xd8) || (*codep > 0xdf))) + || (rex && rex_used)) + { + const char *name; + + /* fwait not followed by floating point instruction, or rex followed + by other prefixes. Print the first prefix. */ + name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s", name); + return 1; + } + + op = 0; + if (prefixes & PREFIX_VEX_0F) + { + used_prefixes |= PREFIX_VEX_0F | PREFIX_VEX_0F38 | PREFIX_VEX_0F3A; + if (prefixes & PREFIX_VEX_0F38) + threebyte = 0x38; + else if (prefixes & PREFIX_VEX_0F3A) + threebyte = 0x3a; + else + threebyte = *codep++; + goto vex_opcode; + } + if (*codep == 0x0f) + { + fetch_data(info, codep + 2); + threebyte = codep[1]; + codep += 2; + vex_opcode: + dp = &dis386_twobyte[threebyte]; + need_modrm = twobyte_has_modrm[threebyte]; + uses_DATA_prefix = twobyte_uses_DATA_prefix[threebyte]; + uses_REPNZ_prefix = twobyte_uses_REPNZ_prefix[threebyte]; + uses_REPZ_prefix = twobyte_uses_REPZ_prefix[threebyte]; + uses_LOCK_prefix = (threebyte & ~0x02) == 0x20; + if (dp->name == NULL && dp->op[0].bytemode == IS_3BYTE_OPCODE) + { + fetch_data(info, codep + 2); + op = *codep++; + switch (threebyte) + { + case 0x38: + uses_DATA_prefix = threebyte_0x38_uses_DATA_prefix[op]; + uses_REPNZ_prefix = threebyte_0x38_uses_REPNZ_prefix[op]; + uses_REPZ_prefix = threebyte_0x38_uses_REPZ_prefix[op]; + break; + case 0x3a: + uses_DATA_prefix = threebyte_0x3a_uses_DATA_prefix[op]; + uses_REPNZ_prefix = threebyte_0x3a_uses_REPNZ_prefix[op]; + uses_REPZ_prefix = threebyte_0x3a_uses_REPZ_prefix[op]; + break; + default: + break; + } + } + } + else + { + dp = &dis386[*codep]; + need_modrm = onebyte_has_modrm[*codep]; + uses_DATA_prefix = 0; + uses_REPNZ_prefix = 0; + /* pause is 0xf3 0x90. */ + uses_REPZ_prefix = *codep == 0x90; + uses_LOCK_prefix = 0; + codep++; + } + + if (!uses_REPZ_prefix && (prefixes & PREFIX_REPZ)) + { + oappend ("repz "); + used_prefixes |= PREFIX_REPZ; + } + if (!uses_REPNZ_prefix && (prefixes & PREFIX_REPNZ)) + { + oappend ("repnz "); + used_prefixes |= PREFIX_REPNZ; + } + + if (!uses_LOCK_prefix && (prefixes & PREFIX_LOCK)) + { + oappend ("lock "); + used_prefixes |= PREFIX_LOCK; + } + + if (prefixes & PREFIX_ADDR) + { + sizeflag ^= AFLAG; + if (dp->op[2].bytemode != loop_jcxz_mode || intel_syntax) + { + if ((sizeflag & AFLAG) || address_mode == mode_64bit) + oappend ("addr32 "); + else + oappend ("addr16 "); + used_prefixes |= PREFIX_ADDR; + } + } + + if (!uses_DATA_prefix && (prefixes & PREFIX_DATA)) + { + sizeflag ^= DFLAG; + if (dp->op[2].bytemode == cond_jump_mode + && dp->op[0].bytemode == v_mode + && !intel_syntax) + { + if (sizeflag & DFLAG) + oappend ("data32 "); + else + oappend ("data16 "); + used_prefixes |= PREFIX_DATA; + } + } + + if (dp->name == NULL && dp->op[0].bytemode == IS_3BYTE_OPCODE) + { + dp = &three_byte_table[dp->op[1].bytemode][op]; + modrm.mod = (*codep >> 6) & 3; + modrm.reg = (*codep >> 3) & 7; + modrm.rm = *codep & 7; + } + else if (need_modrm) + { + fetch_data(info, codep + 1); + modrm.mod = (*codep >> 6) & 3; + modrm.reg = (*codep >> 3) & 7; + modrm.rm = *codep & 7; + } + + if (dp->name == NULL && dp->op[0].bytemode == FLOATCODE) + { + dofloat (sizeflag); + } + else + { + int index; + if (dp->name == NULL) + { + switch (dp->op[0].bytemode) + { + case USE_GROUPS: + dp = &grps[dp->op[1].bytemode][modrm.reg]; + break; + + case USE_PREFIX_USER_TABLE: + index = 0; + used_prefixes |= (prefixes & PREFIX_REPZ); + if (prefixes & PREFIX_REPZ) + index = 1; + else + { + /* We should check PREFIX_REPNZ and PREFIX_REPZ + before PREFIX_DATA. */ + used_prefixes |= (prefixes & PREFIX_REPNZ); + if (prefixes & PREFIX_REPNZ) + index = 3; + else + { + used_prefixes |= (prefixes & PREFIX_DATA); + if (prefixes & PREFIX_DATA) + index = 2; + } + } + dp = &prefix_user_table[dp->op[1].bytemode][index]; + break; + + case X86_64_SPECIAL: + index = address_mode == mode_64bit ? 1 : 0; + dp = &x86_64_table[dp->op[1].bytemode][index]; + break; + + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } + } + + if (putop (dp->name, sizeflag) == 0) + { + for (i = 0; i < MAX_OPERANDS; ++i) + { + obufp = op_out[i]; + op_ad = MAX_OPERANDS - 1 - i; + if (dp->op[i].rtn) + (*dp->op[i].rtn) (dp->op[i].bytemode, sizeflag); + } + } + } + + /* See if any prefixes were not used. If so, print the first one + separately. If we don't do this, we'll wind up printing an + instruction stream which does not precisely correspond to the + bytes we are disassembling. */ + if ((prefixes & ~used_prefixes) != 0) + { + const char *name; + + name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s", name); + return 1; + } + if (rex & ~rex_used) + { + const char *name; + name = prefix_name (rex | 0x40, priv.orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s ", name); + } + + obufp = obuf + strlen (obuf); + for (i = strlen (obuf); i < 6; i++) + oappend (" "); + oappend (" "); + (*info->fprintf_func) (info->stream, "%s", obuf); + + /* The enter and bound instructions are printed with operands in the same + order as the intel book; everything else is printed in reverse order. */ + if (intel_syntax || two_source_ops) + { + bfd_vma riprel; + + for (i = 0; i < MAX_OPERANDS; ++i) + op_txt[i] = op_out[i]; + + for (i = 0; i < (MAX_OPERANDS >> 1); ++i) + { + op_ad = op_index[i]; + op_index[i] = op_index[MAX_OPERANDS - 1 - i]; + op_index[MAX_OPERANDS - 1 - i] = op_ad; + riprel = op_riprel[i]; + op_riprel[i] = op_riprel [MAX_OPERANDS - 1 - i]; + op_riprel[MAX_OPERANDS - 1 - i] = riprel; + } + } + else + { + for (i = 0; i < MAX_OPERANDS; ++i) + op_txt[MAX_OPERANDS - 1 - i] = op_out[i]; + } + + needcomma = 0; + for (i = 0; i < MAX_OPERANDS; ++i) + if (*op_txt[i]) + { + if (needcomma) + (*info->fprintf_func) (info->stream, ","); + if (op_index[i] != -1 && !op_riprel[i]) + (*info->print_address_func) ((bfd_vma) op_address[op_index[i]], info); + else + (*info->fprintf_func) (info->stream, "%s", op_txt[i]); + needcomma = 1; + } + + for (i = 0; i < MAX_OPERANDS; i++) + if (op_index[i] != -1 && op_riprel[i]) + { + (*info->fprintf_func) (info->stream, " # "); + (*info->print_address_func) ((bfd_vma) (start_pc + codep - start_codep + + op_address[op_index[i]]), info); + break; + } + return codep - priv.the_buffer; +} + +static const char *float_mem[] = { + /* d8 */ + "fadd{s||s|}", + "fmul{s||s|}", + "fcom{s||s|}", + "fcomp{s||s|}", + "fsub{s||s|}", + "fsubr{s||s|}", + "fdiv{s||s|}", + "fdivr{s||s|}", + /* d9 */ + "fld{s||s|}", + "(bad)", + "fst{s||s|}", + "fstp{s||s|}", + "fldenvIC", + "fldcw", + "fNstenvIC", + "fNstcw", + /* da */ + "fiadd{l||l|}", + "fimul{l||l|}", + "ficom{l||l|}", + "ficomp{l||l|}", + "fisub{l||l|}", + "fisubr{l||l|}", + "fidiv{l||l|}", + "fidivr{l||l|}", + /* db */ + "fild{l||l|}", + "fisttp{l||l|}", + "fist{l||l|}", + "fistp{l||l|}", + "(bad)", + "fld{t||t|}", + "(bad)", + "fstp{t||t|}", + /* dc */ + "fadd{l||l|}", + "fmul{l||l|}", + "fcom{l||l|}", + "fcomp{l||l|}", + "fsub{l||l|}", + "fsubr{l||l|}", + "fdiv{l||l|}", + "fdivr{l||l|}", + /* dd */ + "fld{l||l|}", + "fisttp{ll||ll|}", + "fst{l||l|}", + "fstp{l||l|}", + "frstorIC", + "(bad)", + "fNsaveIC", + "fNstsw", + /* de */ + "fiadd", + "fimul", + "ficom", + "ficomp", + "fisub", + "fisubr", + "fidiv", + "fidivr", + /* df */ + "fild", + "fisttp", + "fist", + "fistp", + "fbld", + "fild{ll||ll|}", + "fbstp", + "fistp{ll||ll|}", +}; + +static const unsigned char float_mem_mode[] = { + /* d8 */ + d_mode, + d_mode, + d_mode, + d_mode, + d_mode, + d_mode, + d_mode, + d_mode, + /* d9 */ + d_mode, + 0, + d_mode, + d_mode, + 0, + w_mode, + 0, + w_mode, + /* da */ + d_mode, + d_mode, + d_mode, + d_mode, + d_mode, + d_mode, + d_mode, + d_mode, + /* db */ + d_mode, + d_mode, + d_mode, + d_mode, + 0, + t_mode, + 0, + t_mode, + /* dc */ + q_mode, + q_mode, + q_mode, + q_mode, + q_mode, + q_mode, + q_mode, + q_mode, + /* dd */ + q_mode, + q_mode, + q_mode, + q_mode, + 0, + 0, + 0, + w_mode, + /* de */ + w_mode, + w_mode, + w_mode, + w_mode, + w_mode, + w_mode, + w_mode, + w_mode, + /* df */ + w_mode, + w_mode, + w_mode, + w_mode, + t_mode, + q_mode, + t_mode, + q_mode +}; + +#define ST { OP_ST, 0 } +#define STi { OP_STi, 0 } + +#define FGRPd9_2 NULL, { { NULL, 0 } } +#define FGRPd9_4 NULL, { { NULL, 1 } } +#define FGRPd9_5 NULL, { { NULL, 2 } } +#define FGRPd9_6 NULL, { { NULL, 3 } } +#define FGRPd9_7 NULL, { { NULL, 4 } } +#define FGRPda_5 NULL, { { NULL, 5 } } +#define FGRPdb_4 NULL, { { NULL, 6 } } +#define FGRPde_3 NULL, { { NULL, 7 } } +#define FGRPdf_4 NULL, { { NULL, 8 } } + +static const struct dis386 float_reg[][8] = { + /* d8 */ + { + { "fadd", { ST, STi } }, + { "fmul", { ST, STi } }, + { "fcom", { STi } }, + { "fcomp", { STi } }, + { "fsub", { ST, STi } }, + { "fsubr", { ST, STi } }, + { "fdiv", { ST, STi } }, + { "fdivr", { ST, STi } }, + }, + /* d9 */ + { + { "fld", { STi } }, + { "fxch", { STi } }, + { FGRPd9_2 }, + { "(bad)", { XX } }, + { FGRPd9_4 }, + { FGRPd9_5 }, + { FGRPd9_6 }, + { FGRPd9_7 }, + }, + /* da */ + { + { "fcmovb", { ST, STi } }, + { "fcmove", { ST, STi } }, + { "fcmovbe",{ ST, STi } }, + { "fcmovu", { ST, STi } }, + { "(bad)", { XX } }, + { FGRPda_5 }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* db */ + { + { "fcmovnb",{ ST, STi } }, + { "fcmovne",{ ST, STi } }, + { "fcmovnbe",{ ST, STi } }, + { "fcmovnu",{ ST, STi } }, + { FGRPdb_4 }, + { "fucomi", { ST, STi } }, + { "fcomi", { ST, STi } }, + { "(bad)", { XX } }, + }, + /* dc */ + { + { "fadd", { STi, ST } }, + { "fmul", { STi, ST } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, +#if SYSV386_COMPAT + { "fsub", { STi, ST } }, + { "fsubr", { STi, ST } }, + { "fdiv", { STi, ST } }, + { "fdivr", { STi, ST } }, +#else + { "fsubr", { STi, ST } }, + { "fsub", { STi, ST } }, + { "fdivr", { STi, ST } }, + { "fdiv", { STi, ST } }, +#endif + }, + /* dd */ + { + { "ffree", { STi } }, + { "(bad)", { XX } }, + { "fst", { STi } }, + { "fstp", { STi } }, + { "fucom", { STi } }, + { "fucomp", { STi } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + }, + /* de */ + { + { "faddp", { STi, ST } }, + { "fmulp", { STi, ST } }, + { "(bad)", { XX } }, + { FGRPde_3 }, +#if SYSV386_COMPAT + { "fsubp", { STi, ST } }, + { "fsubrp", { STi, ST } }, + { "fdivp", { STi, ST } }, + { "fdivrp", { STi, ST } }, +#else + { "fsubrp", { STi, ST } }, + { "fsubp", { STi, ST } }, + { "fdivrp", { STi, ST } }, + { "fdivp", { STi, ST } }, +#endif + }, + /* df */ + { + { "ffreep", { STi } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { "(bad)", { XX } }, + { FGRPdf_4 }, + { "fucomip", { ST, STi } }, + { "fcomip", { ST, STi } }, + { "(bad)", { XX } }, + }, +}; + +static const char *fgrps[][8] = { + /* d9_2 0 */ + { + "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* d9_4 1 */ + { + "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)", + }, + + /* d9_5 2 */ + { + "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)", + }, + + /* d9_6 3 */ + { + "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp", + }, + + /* d9_7 4 */ + { + "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos", + }, + + /* da_5 5 */ + { + "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* db_4 6 */ + { + "feni(287 only)","fdisi(287 only)","fNclex","fNinit", + "fNsetpm(287 only)","(bad)","(bad)","(bad)", + }, + + /* de_3 7 */ + { + "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* df_4 8 */ + { + "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, +}; + +static void +dofloat (int sizeflag) +{ + const struct dis386 *dp; + unsigned char floatop; + + floatop = codep[-1]; + + if (modrm.mod != 3) + { + int fp_indx = (floatop - 0xd8) * 8 + modrm.reg; + + putop (float_mem[fp_indx], sizeflag); + obufp = op_out[0]; + op_ad = 2; + OP_E (float_mem_mode[fp_indx], sizeflag); + return; + } + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + + dp = &float_reg[floatop - 0xd8][modrm.reg]; + if (dp->name == NULL) + { + putop (fgrps[dp->op[0].bytemode][modrm.rm], sizeflag); + + /* Instruction fnstsw is only one with strange arg. */ + if (floatop == 0xdf && codep[-1] == 0xe0) + pstrcpy (op_out[0], sizeof(op_out[0]), names16[0]); + } + else + { + putop (dp->name, sizeflag); + + obufp = op_out[0]; + op_ad = 2; + if (dp->op[0].rtn) + (*dp->op[0].rtn) (dp->op[0].bytemode, sizeflag); + + obufp = op_out[1]; + op_ad = 1; + if (dp->op[1].rtn) + (*dp->op[1].rtn) (dp->op[1].bytemode, sizeflag); + } +} + +static void +OP_ST (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + oappend ("%st" + intel_syntax); +} + +static void +OP_STi (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + snprintf (scratchbuf, sizeof(scratchbuf), "%%st(%d)", modrm.rm); + oappend (scratchbuf + intel_syntax); +} + +/* Capital letters in template are macros. */ +static int +putop (const char *template, int sizeflag) +{ + const char *p; + int alt = 0; + + for (p = template; *p; p++) + { + switch (*p) + { + default: + *obufp++ = *p; + break; + case '{': + alt = 0; + if (intel_syntax) + alt += 1; + if (address_mode == mode_64bit) + alt += 2; + while (alt != 0) + { + while (*++p != '|') + { + if (*p == '}') + { + /* Alternative not valid. */ + pstrcpy (obuf, sizeof(obuf), "(bad)"); + obufp = obuf + 5; + return 1; + } + else if (*p == '\0') + abort (); + } + alt--; + } + /* Fall through. */ + case 'I': + alt = 1; + continue; + case '|': + while (*++p != '}') + { + if (*p == '\0') + abort (); + } + break; + case '}': + break; + case 'A': + if (intel_syntax) + break; + if (modrm.mod != 3 || (sizeflag & SUFFIX_ALWAYS)) + *obufp++ = 'b'; + break; + case 'B': + if (intel_syntax) + break; + if (sizeflag & SUFFIX_ALWAYS) + *obufp++ = 'b'; + break; + case 'C': + if (intel_syntax && !alt) + break; + if ((prefixes & PREFIX_DATA) || (sizeflag & SUFFIX_ALWAYS)) + { + if (sizeflag & DFLAG) + *obufp++ = intel_syntax ? 'd' : 'l'; + else + *obufp++ = intel_syntax ? 'w' : 's'; + used_prefixes |= (prefixes & PREFIX_DATA); + } + break; + case 'D': + if (intel_syntax || !(sizeflag & SUFFIX_ALWAYS)) + break; + USED_REX (REX_W); + if (modrm.mod == 3) + { + if (rex & REX_W) + *obufp++ = 'q'; + else if (sizeflag & DFLAG) + *obufp++ = intel_syntax ? 'd' : 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } + else + *obufp++ = 'w'; + break; + case 'E': /* For jcxz/jecxz */ + if (address_mode == mode_64bit) + { + if (sizeflag & AFLAG) + *obufp++ = 'r'; + else + *obufp++ = 'e'; + } + else + if (sizeflag & AFLAG) + *obufp++ = 'e'; + used_prefixes |= (prefixes & PREFIX_ADDR); + break; + case 'F': + if (intel_syntax) + break; + if ((prefixes & PREFIX_ADDR) || (sizeflag & SUFFIX_ALWAYS)) + { + if (sizeflag & AFLAG) + *obufp++ = address_mode == mode_64bit ? 'q' : 'l'; + else + *obufp++ = address_mode == mode_64bit ? 'l' : 'w'; + used_prefixes |= (prefixes & PREFIX_ADDR); + } + break; + case 'G': + if (intel_syntax || (obufp[-1] != 's' && !(sizeflag & SUFFIX_ALWAYS))) + break; + if ((rex & REX_W) || (sizeflag & DFLAG)) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + if (!(rex & REX_W)) + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 'H': + if (intel_syntax) + break; + if ((prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_CS + || (prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_DS) + { + used_prefixes |= prefixes & (PREFIX_CS | PREFIX_DS); + *obufp++ = ','; + *obufp++ = 'p'; + if (prefixes & PREFIX_DS) + *obufp++ = 't'; + else + *obufp++ = 'n'; + } + break; + case 'J': + if (intel_syntax) + break; + *obufp++ = 'l'; + break; + case 'K': + USED_REX (REX_W); + if (rex & REX_W) + *obufp++ = 'q'; + else + *obufp++ = 'd'; + break; + case 'Z': + if (intel_syntax) + break; + if (address_mode == mode_64bit && (sizeflag & SUFFIX_ALWAYS)) + { + *obufp++ = 'q'; + break; + } + /* Fall through. */ + case 'L': + if (intel_syntax) + break; + if (sizeflag & SUFFIX_ALWAYS) + *obufp++ = 'l'; + break; + case 'N': + if ((prefixes & PREFIX_FWAIT) == 0) + *obufp++ = 'n'; + else + used_prefixes |= PREFIX_FWAIT; + break; + case 'O': + USED_REX (REX_W); + if (rex & REX_W) + *obufp++ = 'o'; + else if (intel_syntax && (sizeflag & DFLAG)) + *obufp++ = 'q'; + else + *obufp++ = 'd'; + if (!(rex & REX_W)) + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 'T': + if (intel_syntax) + break; + if (address_mode == mode_64bit && (sizeflag & DFLAG)) + { + *obufp++ = 'q'; + break; + } + /* Fall through. */ + case 'P': + if (intel_syntax) + break; + if ((prefixes & PREFIX_DATA) + || (rex & REX_W) + || (sizeflag & SUFFIX_ALWAYS)) + { + USED_REX (REX_W); + if (rex & REX_W) + *obufp++ = 'q'; + else + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + } + used_prefixes |= (prefixes & PREFIX_DATA); + } + break; + case 'U': + if (intel_syntax) + break; + if (address_mode == mode_64bit && (sizeflag & DFLAG)) + { + if (modrm.mod != 3 || (sizeflag & SUFFIX_ALWAYS)) + *obufp++ = 'q'; + break; + } + /* Fall through. */ + case 'Q': + if (intel_syntax && !alt) + break; + USED_REX (REX_W); + if (modrm.mod != 3 || (sizeflag & SUFFIX_ALWAYS)) + { + if (rex & REX_W) + *obufp++ = 'q'; + else + { + if (sizeflag & DFLAG) + *obufp++ = intel_syntax ? 'd' : 'l'; + else + *obufp++ = 'w'; + } + used_prefixes |= (prefixes & PREFIX_DATA); + } + break; + case 'R': + USED_REX (REX_W); + if (rex & REX_W) + *obufp++ = 'q'; + else if (sizeflag & DFLAG) + { + if (intel_syntax) + *obufp++ = 'd'; + else + *obufp++ = 'l'; + } + else + *obufp++ = 'w'; + if (intel_syntax && !p[1] + && ((rex & REX_W) || (sizeflag & DFLAG))) + *obufp++ = 'e'; + if (!(rex & REX_W)) + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 'V': + if (intel_syntax) + break; + if (address_mode == mode_64bit && (sizeflag & DFLAG)) + { + if (sizeflag & SUFFIX_ALWAYS) + *obufp++ = 'q'; + break; + } + /* Fall through. */ + case 'S': + if (intel_syntax) + break; + if (sizeflag & SUFFIX_ALWAYS) + { + if (rex & REX_W) + *obufp++ = 'q'; + else + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } + } + break; + case 'X': + if (prefixes & PREFIX_DATA) + *obufp++ = 'd'; + else + *obufp++ = 's'; + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 'Y': + if (intel_syntax) + break; + if (rex & REX_W) + { + USED_REX (REX_W); + *obufp++ = 'q'; + } + break; + /* implicit operand size 'l' for i386 or 'q' for x86-64 */ + case 'W': + /* operand size flag for cwtl, cbtw */ + USED_REX (REX_W); + if (rex & REX_W) + { + if (intel_syntax) + *obufp++ = 'd'; + else + *obufp++ = 'l'; + } + else if (sizeflag & DFLAG) + *obufp++ = 'w'; + else + *obufp++ = 'b'; + if (!(rex & REX_W)) + used_prefixes |= (prefixes & PREFIX_DATA); + break; + } + alt = 0; + } + *obufp = 0; + return 0; +} + +static void +oappend (const char *s) +{ + strcpy (obufp, s); + obufp += strlen (s); +} + +static void +append_seg (void) +{ + if (prefixes & PREFIX_CS) + { + used_prefixes |= PREFIX_CS; + oappend ("%cs:" + intel_syntax); + } + if (prefixes & PREFIX_DS) + { + used_prefixes |= PREFIX_DS; + oappend ("%ds:" + intel_syntax); + } + if (prefixes & PREFIX_SS) + { + used_prefixes |= PREFIX_SS; + oappend ("%ss:" + intel_syntax); + } + if (prefixes & PREFIX_ES) + { + used_prefixes |= PREFIX_ES; + oappend ("%es:" + intel_syntax); + } + if (prefixes & PREFIX_FS) + { + used_prefixes |= PREFIX_FS; + oappend ("%fs:" + intel_syntax); + } + if (prefixes & PREFIX_GS) + { + used_prefixes |= PREFIX_GS; + oappend ("%gs:" + intel_syntax); + } +} + +static void +OP_indirE (int bytemode, int sizeflag) +{ + if (!intel_syntax) + oappend ("*"); + OP_E (bytemode, sizeflag); +} + +static void +print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma disp) +{ + if (address_mode == mode_64bit) + { + if (hex) + { + char tmp[30]; + int i; + buf[0] = '0'; + buf[1] = 'x'; + snprintf_vma (tmp, sizeof(tmp), disp); + for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++) { + } + pstrcpy (buf + 2, bufsize - 2, tmp + i); + } + else + { + bfd_signed_vma v = disp; + char tmp[30]; + int i; + if (v < 0) + { + *(buf++) = '-'; + v = -disp; + /* Check for possible overflow on 0x8000000000000000. */ + if (v < 0) + { + pstrcpy (buf, bufsize, "9223372036854775808"); + return; + } + } + if (!v) + { + pstrcpy (buf, bufsize, "0"); + return; + } + + i = 0; + tmp[29] = 0; + while (v) + { + tmp[28 - i] = (v % 10) + '0'; + v /= 10; + i++; + } + pstrcpy (buf, bufsize, tmp + 29 - i); + } + } + else + { + if (hex) + snprintf (buf, bufsize, "0x%x", (unsigned int) disp); + else + snprintf (buf, bufsize, "%d", (int) disp); + } +} + +/* Put DISP in BUF as signed hex number. */ + +static void +print_displacement (char *buf, bfd_vma disp) +{ + bfd_signed_vma val = disp; + char tmp[30]; + int i, j = 0; + + if (val < 0) + { + buf[j++] = '-'; + val = -disp; + + /* Check for possible overflow. */ + if (val < 0) + { + switch (address_mode) + { + case mode_64bit: + strcpy (buf + j, "0x8000000000000000"); + break; + case mode_32bit: + strcpy (buf + j, "0x80000000"); + break; + case mode_16bit: + strcpy (buf + j, "0x8000"); + break; + } + return; + } + } + + buf[j++] = '0'; + buf[j++] = 'x'; + + snprintf_vma (tmp, sizeof(tmp), val); + for (i = 0; tmp[i] == '0'; i++) + continue; + if (tmp[i] == '\0') + i--; + strcpy (buf + j, tmp + i); +} + +static void +intel_operand_size (int bytemode, int sizeflag) +{ + switch (bytemode) + { + case b_mode: + case dqb_mode: + oappend ("BYTE PTR "); + break; + case w_mode: + case dqw_mode: + oappend ("WORD PTR "); + break; + case stack_v_mode: + if (address_mode == mode_64bit && (sizeflag & DFLAG)) + { + oappend ("QWORD PTR "); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + } + /* FALLTHRU */ + case v_mode: + case dq_mode: + USED_REX (REX_W); + if (rex & REX_W) + oappend ("QWORD PTR "); + else if ((sizeflag & DFLAG) || bytemode == dq_mode) + oappend ("DWORD PTR "); + else + oappend ("WORD PTR "); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case z_mode: + if ((rex & REX_W) || (sizeflag & DFLAG)) + *obufp++ = 'D'; + oappend ("WORD PTR "); + if (!(rex & REX_W)) + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case d_mode: + case dqd_mode: + oappend ("DWORD PTR "); + break; + case q_mode: + oappend ("QWORD PTR "); + break; + case m_mode: + if (address_mode == mode_64bit) + oappend ("QWORD PTR "); + else + oappend ("DWORD PTR "); + break; + case f_mode: + if (sizeflag & DFLAG) + oappend ("FWORD PTR "); + else + oappend ("DWORD PTR "); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case t_mode: + oappend ("TBYTE PTR "); + break; + case x_mode: + oappend ("XMMWORD PTR "); + break; + case o_mode: + oappend ("OWORD PTR "); + break; + default: + break; + } +} + +static void +OP_E (int bytemode, int sizeflag) +{ + bfd_vma disp; + int add = 0; + int riprel = 0; + USED_REX (REX_B); + if (rex & REX_B) + add += 8; + + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + + if (modrm.mod == 3) + { + switch (bytemode) + { + case b_mode: + USED_REX (0); + if (rex) + oappend (names8rex[modrm.rm + add]); + else + oappend (names8[modrm.rm + add]); + break; + case w_mode: + oappend (names16[modrm.rm + add]); + break; + case d_mode: + oappend (names32[modrm.rm + add]); + break; + case q_mode: + oappend (names64[modrm.rm + add]); + break; + case m_mode: + if (address_mode == mode_64bit) + oappend (names64[modrm.rm + add]); + else + oappend (names32[modrm.rm + add]); + break; + case stack_v_mode: + if (address_mode == mode_64bit && (sizeflag & DFLAG)) + { + oappend (names64[modrm.rm + add]); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + } + bytemode = v_mode; + /* FALLTHRU */ + case v_mode: + case dq_mode: + case dqb_mode: + case dqd_mode: + case dqw_mode: + USED_REX (REX_W); + if (rex & REX_W) + oappend (names64[modrm.rm + add]); + else if ((sizeflag & DFLAG) || bytemode != v_mode) + oappend (names32[modrm.rm + add]); + else + oappend (names16[modrm.rm + add]); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 0: + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } + return; + } + + disp = 0; + if (intel_syntax) + intel_operand_size (bytemode, sizeflag); + append_seg (); + + if ((sizeflag & AFLAG) || address_mode == mode_64bit) + { + /* 32/64 bit address mode */ + int havedisp; + int havesib; + int havebase; + int base; + int index = 0; + int scale = 0; + + havesib = 0; + havebase = 1; + base = modrm.rm; + + if (base == 4) + { + havesib = 1; + fetch_data(the_info, codep + 1); + index = (*codep >> 3) & 7; + if (address_mode == mode_64bit || index != 0x4) + /* When INDEX == 0x4 in 32 bit mode, SCALE is ignored. */ + scale = (*codep >> 6) & 3; + base = *codep & 7; + USED_REX (REX_X); + if (rex & REX_X) + index += 8; + codep++; + } + base += add; + + switch (modrm.mod) + { + case 0: + if ((base & 7) == 5) + { + havebase = 0; + if (address_mode == mode_64bit && !havesib) + riprel = 1; + disp = get32s (); + } + break; + case 1: + fetch_data (the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case 2: + disp = get32s (); + break; + } + + havedisp = havebase || (havesib && (index != 4 || scale != 0)); + + if (!intel_syntax) + if (modrm.mod != 0 || (base & 7) == 5) + { + if (havedisp || riprel) + print_displacement (scratchbuf, disp); + else + print_operand_value (scratchbuf, sizeof(scratchbuf), 1, disp); + oappend (scratchbuf); + if (riprel) + { + set_op (disp, 1); + oappend ("(%rip)"); + } + } + + if (havedisp || (intel_syntax && riprel)) + { + *obufp++ = open_char; + if (intel_syntax && riprel) + { + set_op (disp, 1); + oappend ("rip"); + } + *obufp = '\0'; + if (havebase) + oappend (address_mode == mode_64bit && (sizeflag & AFLAG) + ? names64[base] : names32[base]); + if (havesib) + { + if (index != 4) + { + if (!intel_syntax || havebase) + { + *obufp++ = separator_char; + *obufp = '\0'; + } + oappend (address_mode == mode_64bit && (sizeflag & AFLAG) + ? names64[index] : names32[index]); + } + if (scale != 0 || (!intel_syntax && index != 4)) + { + *obufp++ = scale_char; + *obufp = '\0'; + snprintf (scratchbuf, sizeof(scratchbuf), "%d", 1 << scale); + oappend (scratchbuf); + } + } + if (intel_syntax + && (disp || modrm.mod != 0 || (base & 7) == 5)) + { + if ((bfd_signed_vma) disp >= 0) + { + *obufp++ = '+'; + *obufp = '\0'; + } + else if (modrm.mod != 1) + { + *obufp++ = '-'; + *obufp = '\0'; + disp = - (bfd_signed_vma) disp; + } + + print_displacement (scratchbuf, disp); + oappend (scratchbuf); + } + + *obufp++ = close_char; + *obufp = '\0'; + } + else if (intel_syntax) + { + if (modrm.mod != 0 || (base & 7) == 5) + { + if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS)) + ; + else + { + oappend (names_seg[ds_reg - es_reg]); + oappend (":"); + } + print_operand_value (scratchbuf, sizeof(scratchbuf), 1, disp); + oappend (scratchbuf); + } + } + } + else + { /* 16 bit address mode */ + switch (modrm.mod) + { + case 0: + if (modrm.rm == 6) + { + disp = get16 (); + if ((disp & 0x8000) != 0) + disp -= 0x10000; + } + break; + case 1: + fetch_data(the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case 2: + disp = get16 (); + if ((disp & 0x8000) != 0) + disp -= 0x10000; + break; + } + + if (!intel_syntax) + if (modrm.mod != 0 || modrm.rm == 6) + { + print_displacement (scratchbuf, disp); + oappend (scratchbuf); + } + + if (modrm.mod != 0 || modrm.rm != 6) + { + *obufp++ = open_char; + *obufp = '\0'; + oappend (index16[modrm.rm]); + if (intel_syntax + && (disp || modrm.mod != 0 || modrm.rm == 6)) + { + if ((bfd_signed_vma) disp >= 0) + { + *obufp++ = '+'; + *obufp = '\0'; + } + else if (modrm.mod != 1) + { + *obufp++ = '-'; + *obufp = '\0'; + disp = - (bfd_signed_vma) disp; + } + + print_displacement (scratchbuf, disp); + oappend (scratchbuf); + } + + *obufp++ = close_char; + *obufp = '\0'; + } + else if (intel_syntax) + { + if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS)) + ; + else + { + oappend (names_seg[ds_reg - es_reg]); + oappend (":"); + } + print_operand_value (scratchbuf, sizeof(scratchbuf), 1, + disp & 0xffff); + oappend (scratchbuf); + } + } +} + +static void +OP_G (int bytemode, int sizeflag) +{ + int add = 0; + USED_REX (REX_R); + if (rex & REX_R) + add += 8; + switch (bytemode) + { + case b_mode: + USED_REX (0); + if (rex) + oappend (names8rex[modrm.reg + add]); + else + oappend (names8[modrm.reg + add]); + break; + case w_mode: + oappend (names16[modrm.reg + add]); + break; + case d_mode: + oappend (names32[modrm.reg + add]); + break; + case q_mode: + oappend (names64[modrm.reg + add]); + break; + case v_mode: + case dq_mode: + case dqb_mode: + case dqd_mode: + case dqw_mode: + USED_REX (REX_W); + if (rex & REX_W) + oappend (names64[modrm.reg + add]); + else if ((sizeflag & DFLAG) || bytemode != v_mode) + oappend (names32[modrm.reg + add]); + else + oappend (names16[modrm.reg + add]); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case m_mode: + if (address_mode == mode_64bit) + oappend (names64[modrm.reg + add]); + else + oappend (names32[modrm.reg + add]); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } +} + +static void +OP_vvvv (int bytemode, int sizeflags) +{ + USED_REX (REX_W); + if (rex & REX_W) { + oappend(names64[vex_reg]); + } else { + oappend(names32[vex_reg]); + } +} + +static bfd_vma +get64 (void) +{ + bfd_vma x; +#ifdef BFD64 + unsigned int a; + unsigned int b; + + fetch_data(the_info, codep + 8); + a = *codep++ & 0xff; + a |= (*codep++ & 0xff) << 8; + a |= (*codep++ & 0xff) << 16; + a |= (*codep++ & 0xff) << 24; + b = *codep++ & 0xff; + b |= (*codep++ & 0xff) << 8; + b |= (*codep++ & 0xff) << 16; + b |= (*codep++ & 0xff) << 24; + x = a + ((bfd_vma) b << 32); +#else + abort (); + x = 0; +#endif + return x; +} + +static bfd_signed_vma +get32 (void) +{ + bfd_signed_vma x = 0; + + fetch_data(the_info, codep + 4); + x = *codep++ & (bfd_signed_vma) 0xff; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 8; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 16; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 24; + return x; +} + +static bfd_signed_vma +get32s (void) +{ + bfd_signed_vma x = 0; + + fetch_data(the_info, codep + 4); + x = *codep++ & (bfd_signed_vma) 0xff; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 8; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 16; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 24; + + x = (x ^ ((bfd_signed_vma) 1 << 31)) - ((bfd_signed_vma) 1 << 31); + + return x; +} + +static int +get16 (void) +{ + int x = 0; + + fetch_data(the_info, codep + 2); + x = *codep++ & 0xff; + x |= (*codep++ & 0xff) << 8; + return x; +} + +static void +set_op (bfd_vma op, int riprel) +{ + op_index[op_ad] = op_ad; + if (address_mode == mode_64bit) + { + op_address[op_ad] = op; + op_riprel[op_ad] = riprel; + } + else + { + /* Mask to get a 32-bit address. */ + op_address[op_ad] = op & 0xffffffff; + op_riprel[op_ad] = riprel & 0xffffffff; + } +} + +static void +OP_REG (int code, int sizeflag) +{ + const char *s; + int add = 0; + USED_REX (REX_B); + if (rex & REX_B) + add = 8; + + switch (code) + { + case ax_reg: case cx_reg: case dx_reg: case bx_reg: + case sp_reg: case bp_reg: case si_reg: case di_reg: + s = names16[code - ax_reg + add]; + break; + case es_reg: case ss_reg: case cs_reg: + case ds_reg: case fs_reg: case gs_reg: + s = names_seg[code - es_reg + add]; + break; + case al_reg: case ah_reg: case cl_reg: case ch_reg: + case dl_reg: case dh_reg: case bl_reg: case bh_reg: + USED_REX (0); + if (rex) + s = names8rex[code - al_reg + add]; + else + s = names8[code - al_reg]; + break; + case rAX_reg: case rCX_reg: case rDX_reg: case rBX_reg: + case rSP_reg: case rBP_reg: case rSI_reg: case rDI_reg: + if (address_mode == mode_64bit && (sizeflag & DFLAG)) + { + s = names64[code - rAX_reg + add]; + break; + } + code += eAX_reg - rAX_reg; + /* Fall through. */ + case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg: + case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg: + USED_REX (REX_W); + if (rex & REX_W) + s = names64[code - eAX_reg + add]; + else if (sizeflag & DFLAG) + s = names32[code - eAX_reg + add]; + else + s = names16[code - eAX_reg + add]; + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + s = INTERNAL_DISASSEMBLER_ERROR; + break; + } + oappend (s); +} + +static void +OP_IMREG (int code, int sizeflag) +{ + const char *s; + + switch (code) + { + case indir_dx_reg: + if (intel_syntax) + s = "dx"; + else + s = "(%dx)"; + break; + case ax_reg: case cx_reg: case dx_reg: case bx_reg: + case sp_reg: case bp_reg: case si_reg: case di_reg: + s = names16[code - ax_reg]; + break; + case es_reg: case ss_reg: case cs_reg: + case ds_reg: case fs_reg: case gs_reg: + s = names_seg[code - es_reg]; + break; + case al_reg: case ah_reg: case cl_reg: case ch_reg: + case dl_reg: case dh_reg: case bl_reg: case bh_reg: + USED_REX (0); + if (rex) + s = names8rex[code - al_reg]; + else + s = names8[code - al_reg]; + break; + case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg: + case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg: + USED_REX (REX_W); + if (rex & REX_W) + s = names64[code - eAX_reg]; + else if (sizeflag & DFLAG) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case z_mode_ax_reg: + if ((rex & REX_W) || (sizeflag & DFLAG)) + s = *names32; + else + s = *names16; + if (!(rex & REX_W)) + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + s = INTERNAL_DISASSEMBLER_ERROR; + break; + } + oappend (s); +} + +static void +OP_I (int bytemode, int sizeflag) +{ + bfd_signed_vma op; + bfd_signed_vma mask = -1; + + switch (bytemode) + { + case b_mode: + fetch_data(the_info, codep + 1); + op = *codep++; + mask = 0xff; + break; + case q_mode: + if (address_mode == mode_64bit) + { + op = get32s (); + break; + } + /* Fall through. */ + case v_mode: + USED_REX (REX_W); + if (rex & REX_W) + op = get32s (); + else if (sizeflag & DFLAG) + { + op = get32 (); + mask = 0xffffffff; + } + else + { + op = get16 (); + mask = 0xfffff; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case w_mode: + mask = 0xfffff; + op = get16 (); + break; + case const_1_mode: + if (intel_syntax) + oappend ("1"); + return; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + + op &= mask; + scratchbuf[0] = '$'; + print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op); + oappend (scratchbuf + intel_syntax); + scratchbuf[0] = '\0'; +} + +static void +OP_I64 (int bytemode, int sizeflag) +{ + bfd_signed_vma op; + bfd_signed_vma mask = -1; + + if (address_mode != mode_64bit) + { + OP_I (bytemode, sizeflag); + return; + } + + switch (bytemode) + { + case b_mode: + fetch_data(the_info, codep + 1); + op = *codep++; + mask = 0xff; + break; + case v_mode: + USED_REX (REX_W); + if (rex & REX_W) + op = get64 (); + else if (sizeflag & DFLAG) + { + op = get32 (); + mask = 0xffffffff; + } + else + { + op = get16 (); + mask = 0xfffff; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case w_mode: + mask = 0xfffff; + op = get16 (); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + + op &= mask; + scratchbuf[0] = '$'; + print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op); + oappend (scratchbuf + intel_syntax); + scratchbuf[0] = '\0'; +} + +static void +OP_sI (int bytemode, int sizeflag) +{ + bfd_signed_vma op; + + switch (bytemode) + { + case b_mode: + fetch_data(the_info, codep + 1); + op = *codep++; + if ((op & 0x80) != 0) + op -= 0x100; + break; + case v_mode: + USED_REX (REX_W); + if (rex & REX_W) + op = get32s (); + else if (sizeflag & DFLAG) + { + op = get32s (); + } + else + { + op = get16 (); + if ((op & 0x8000) != 0) + op -= 0x10000; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case w_mode: + op = get16 (); + if ((op & 0x8000) != 0) + op -= 0x10000; + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + + scratchbuf[0] = '$'; + print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_J (int bytemode, int sizeflag) +{ + bfd_vma disp; + bfd_vma mask = -1; + bfd_vma segment = 0; + + switch (bytemode) + { + case b_mode: + fetch_data(the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case v_mode: + if ((sizeflag & DFLAG) || (rex & REX_W)) + disp = get32s (); + else + { + disp = get16 (); + if ((disp & 0x8000) != 0) + disp -= 0x10000; + /* In 16bit mode, address is wrapped around at 64k within + the same segment. Otherwise, a data16 prefix on a jump + instruction means that the pc is masked to 16 bits after + the displacement is added! */ + mask = 0xffff; + if ((prefixes & PREFIX_DATA) == 0) + segment = ((start_pc + codep - start_codep) + & ~((bfd_vma) 0xffff)); + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + disp = ((start_pc + codep - start_codep + disp) & mask) | segment; + set_op (disp, 0); + print_operand_value (scratchbuf, sizeof(scratchbuf), 1, disp); + oappend (scratchbuf); +} + +static void +OP_SEG (int bytemode, int sizeflag) +{ + if (bytemode == w_mode) + oappend (names_seg[modrm.reg]); + else + OP_E (modrm.mod == 3 ? bytemode : w_mode, sizeflag); +} + +static void +OP_DIR (int dummy ATTRIBUTE_UNUSED, int sizeflag) +{ + int seg, offset; + + if (sizeflag & DFLAG) + { + offset = get32 (); + seg = get16 (); + } + else + { + offset = get16 (); + seg = get16 (); + } + used_prefixes |= (prefixes & PREFIX_DATA); + if (intel_syntax) + snprintf (scratchbuf, sizeof(scratchbuf), "0x%x:0x%x", seg, offset); + else + snprintf (scratchbuf, sizeof(scratchbuf), "$0x%x,$0x%x", seg, offset); + oappend (scratchbuf); +} + +static void +OP_OFF (int bytemode, int sizeflag) +{ + bfd_vma off; + + if (intel_syntax && (sizeflag & SUFFIX_ALWAYS)) + intel_operand_size (bytemode, sizeflag); + append_seg (); + + if ((sizeflag & AFLAG) || address_mode == mode_64bit) + off = get32 (); + else + off = get16 (); + + if (intel_syntax) + { + if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS))) + { + oappend (names_seg[ds_reg - es_reg]); + oappend (":"); + } + } + print_operand_value (scratchbuf, sizeof(scratchbuf), 1, off); + oappend (scratchbuf); +} + +static void +OP_OFF64 (int bytemode, int sizeflag) +{ + bfd_vma off; + + if (address_mode != mode_64bit + || (prefixes & PREFIX_ADDR)) + { + OP_OFF (bytemode, sizeflag); + return; + } + + if (intel_syntax && (sizeflag & SUFFIX_ALWAYS)) + intel_operand_size (bytemode, sizeflag); + append_seg (); + + off = get64 (); + + if (intel_syntax) + { + if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS))) + { + oappend (names_seg[ds_reg - es_reg]); + oappend (":"); + } + } + print_operand_value (scratchbuf, sizeof(scratchbuf), 1, off); + oappend (scratchbuf); +} + +static void +ptr_reg (int code, int sizeflag) +{ + const char *s; + + *obufp++ = open_char; + used_prefixes |= (prefixes & PREFIX_ADDR); + if (address_mode == mode_64bit) + { + if (!(sizeflag & AFLAG)) + s = names32[code - eAX_reg]; + else + s = names64[code - eAX_reg]; + } + else if (sizeflag & AFLAG) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + oappend (s); + *obufp++ = close_char; + *obufp = 0; +} + +static void +OP_ESreg (int code, int sizeflag) +{ + if (intel_syntax) + { + switch (codep[-1]) + { + case 0x6d: /* insw/insl */ + intel_operand_size (z_mode, sizeflag); + break; + case 0xa5: /* movsw/movsl/movsq */ + case 0xa7: /* cmpsw/cmpsl/cmpsq */ + case 0xab: /* stosw/stosl */ + case 0xaf: /* scasw/scasl */ + intel_operand_size (v_mode, sizeflag); + break; + default: + intel_operand_size (b_mode, sizeflag); + } + } + oappend ("%es:" + intel_syntax); + ptr_reg (code, sizeflag); +} + +static void +OP_DSreg (int code, int sizeflag) +{ + if (intel_syntax) + { + switch (codep[-1]) + { + case 0x6f: /* outsw/outsl */ + intel_operand_size (z_mode, sizeflag); + break; + case 0xa5: /* movsw/movsl/movsq */ + case 0xa7: /* cmpsw/cmpsl/cmpsq */ + case 0xad: /* lodsw/lodsl/lodsq */ + intel_operand_size (v_mode, sizeflag); + break; + default: + intel_operand_size (b_mode, sizeflag); + } + } + if ((prefixes + & (PREFIX_CS + | PREFIX_DS + | PREFIX_SS + | PREFIX_ES + | PREFIX_FS + | PREFIX_GS)) == 0) + prefixes |= PREFIX_DS; + append_seg (); + ptr_reg (code, sizeflag); +} + +static void +OP_C (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + int add = 0; + if (rex & REX_R) + { + USED_REX (REX_R); + add = 8; + } + else if (address_mode != mode_64bit && (prefixes & PREFIX_LOCK)) + { + used_prefixes |= PREFIX_LOCK; + add = 8; + } + snprintf (scratchbuf, sizeof(scratchbuf), "%%cr%d", modrm.reg + add); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_D (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + int add = 0; + USED_REX (REX_R); + if (rex & REX_R) + add = 8; + if (intel_syntax) + snprintf (scratchbuf, sizeof(scratchbuf), "db%d", modrm.reg + add); + else + snprintf (scratchbuf, sizeof(scratchbuf), "%%db%d", modrm.reg + add); + oappend (scratchbuf); +} + +static void +OP_T (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + snprintf (scratchbuf, sizeof(scratchbuf), "%%tr%d", modrm.reg); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_R (int bytemode, int sizeflag) +{ + if (modrm.mod == 3) + OP_E (bytemode, sizeflag); + else + BadOp (); +} + +static void +OP_MMX (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + used_prefixes |= (prefixes & PREFIX_DATA); + if (prefixes & PREFIX_DATA) + { + int add = 0; + USED_REX (REX_R); + if (rex & REX_R) + add = 8; + snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", modrm.reg + add); + } + else + snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", modrm.reg); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_XMM (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + int add = 0; + USED_REX (REX_R); + if (rex & REX_R) + add = 8; + snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", modrm.reg + add); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_EM (int bytemode, int sizeflag) +{ + if (modrm.mod != 3) + { + if (intel_syntax && bytemode == v_mode) + { + bytemode = (prefixes & PREFIX_DATA) ? x_mode : q_mode; + used_prefixes |= (prefixes & PREFIX_DATA); + } + OP_E (bytemode, sizeflag); + return; + } + + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + used_prefixes |= (prefixes & PREFIX_DATA); + if (prefixes & PREFIX_DATA) + { + int add = 0; + + USED_REX (REX_B); + if (rex & REX_B) + add = 8; + snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", modrm.rm + add); + } + else + snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", modrm.rm); + oappend (scratchbuf + intel_syntax); +} + +/* cvt* are the only instructions in sse2 which have + both SSE and MMX operands and also have 0x66 prefix + in their opcode. 0x66 was originally used to differentiate + between SSE and MMX instruction(operands). So we have to handle the + cvt* separately using OP_EMC and OP_MXC */ +static void +OP_EMC (int bytemode, int sizeflag) +{ + if (modrm.mod != 3) + { + if (intel_syntax && bytemode == v_mode) + { + bytemode = (prefixes & PREFIX_DATA) ? x_mode : q_mode; + used_prefixes |= (prefixes & PREFIX_DATA); + } + OP_E (bytemode, sizeflag); + return; + } + + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + used_prefixes |= (prefixes & PREFIX_DATA); + snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", modrm.rm); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_MXC (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + used_prefixes |= (prefixes & PREFIX_DATA); + snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", modrm.reg); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_EX (int bytemode, int sizeflag) +{ + int add = 0; + if (modrm.mod != 3) + { + OP_E (bytemode, sizeflag); + return; + } + USED_REX (REX_B); + if (rex & REX_B) + add = 8; + + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", modrm.rm + add); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_MS (int bytemode, int sizeflag) +{ + if (modrm.mod == 3) + OP_EM (bytemode, sizeflag); + else + BadOp (); +} + +static void +OP_XS (int bytemode, int sizeflag) +{ + if (modrm.mod == 3) + OP_EX (bytemode, sizeflag); + else + BadOp (); +} + +static void +OP_M (int bytemode, int sizeflag) +{ + if (modrm.mod == 3) + /* bad bound,lea,lds,les,lfs,lgs,lss,cmpxchg8b,vmptrst modrm */ + BadOp (); + else + OP_E (bytemode, sizeflag); +} + +static void +OP_0f07 (int bytemode, int sizeflag) +{ + if (modrm.mod != 3 || modrm.rm != 0) + BadOp (); + else + OP_E (bytemode, sizeflag); +} + +static void +OP_0fae (int bytemode, int sizeflag) +{ + if (modrm.mod == 3) + { + if (modrm.reg == 7) + strcpy (obuf + strlen (obuf) - sizeof ("clflush") + 1, "sfence"); + + if (modrm.reg < 5 || modrm.rm != 0) + { + BadOp (); /* bad sfence, mfence, or lfence */ + return; + } + } + else if (modrm.reg != 7) + { + BadOp (); /* bad clflush */ + return; + } + + OP_E (bytemode, sizeflag); +} + +/* NOP is an alias of "xchg %ax,%ax" in 16bit mode, "xchg %eax,%eax" in + 32bit mode and "xchg %rax,%rax" in 64bit mode. */ + +static void +NOP_Fixup1 (int bytemode, int sizeflag) +{ + if ((prefixes & PREFIX_DATA) != 0 + || (rex != 0 + && rex != 0x48 + && address_mode == mode_64bit)) + OP_REG (bytemode, sizeflag); + else + strcpy (obuf, "nop"); +} + +static void +NOP_Fixup2 (int bytemode, int sizeflag) +{ + if ((prefixes & PREFIX_DATA) != 0 + || (rex != 0 + && rex != 0x48 + && address_mode == mode_64bit)) + OP_IMREG (bytemode, sizeflag); +} + +static const char *Suffix3DNow[] = { +/* 00 */ NULL, NULL, NULL, NULL, +/* 04 */ NULL, NULL, NULL, NULL, +/* 08 */ NULL, NULL, NULL, NULL, +/* 0C */ "pi2fw", "pi2fd", NULL, NULL, +/* 10 */ NULL, NULL, NULL, NULL, +/* 14 */ NULL, NULL, NULL, NULL, +/* 18 */ NULL, NULL, NULL, NULL, +/* 1C */ "pf2iw", "pf2id", NULL, NULL, +/* 20 */ NULL, NULL, NULL, NULL, +/* 24 */ NULL, NULL, NULL, NULL, +/* 28 */ NULL, NULL, NULL, NULL, +/* 2C */ NULL, NULL, NULL, NULL, +/* 30 */ NULL, NULL, NULL, NULL, +/* 34 */ NULL, NULL, NULL, NULL, +/* 38 */ NULL, NULL, NULL, NULL, +/* 3C */ NULL, NULL, NULL, NULL, +/* 40 */ NULL, NULL, NULL, NULL, +/* 44 */ NULL, NULL, NULL, NULL, +/* 48 */ NULL, NULL, NULL, NULL, +/* 4C */ NULL, NULL, NULL, NULL, +/* 50 */ NULL, NULL, NULL, NULL, +/* 54 */ NULL, NULL, NULL, NULL, +/* 58 */ NULL, NULL, NULL, NULL, +/* 5C */ NULL, NULL, NULL, NULL, +/* 60 */ NULL, NULL, NULL, NULL, +/* 64 */ NULL, NULL, NULL, NULL, +/* 68 */ NULL, NULL, NULL, NULL, +/* 6C */ NULL, NULL, NULL, NULL, +/* 70 */ NULL, NULL, NULL, NULL, +/* 74 */ NULL, NULL, NULL, NULL, +/* 78 */ NULL, NULL, NULL, NULL, +/* 7C */ NULL, NULL, NULL, NULL, +/* 80 */ NULL, NULL, NULL, NULL, +/* 84 */ NULL, NULL, NULL, NULL, +/* 88 */ NULL, NULL, "pfnacc", NULL, +/* 8C */ NULL, NULL, "pfpnacc", NULL, +/* 90 */ "pfcmpge", NULL, NULL, NULL, +/* 94 */ "pfmin", NULL, "pfrcp", "pfrsqrt", +/* 98 */ NULL, NULL, "pfsub", NULL, +/* 9C */ NULL, NULL, "pfadd", NULL, +/* A0 */ "pfcmpgt", NULL, NULL, NULL, +/* A4 */ "pfmax", NULL, "pfrcpit1", "pfrsqit1", +/* A8 */ NULL, NULL, "pfsubr", NULL, +/* AC */ NULL, NULL, "pfacc", NULL, +/* B0 */ "pfcmpeq", NULL, NULL, NULL, +/* B4 */ "pfmul", NULL, "pfrcpit2", "pmulhrw", +/* B8 */ NULL, NULL, NULL, "pswapd", +/* BC */ NULL, NULL, NULL, "pavgusb", +/* C0 */ NULL, NULL, NULL, NULL, +/* C4 */ NULL, NULL, NULL, NULL, +/* C8 */ NULL, NULL, NULL, NULL, +/* CC */ NULL, NULL, NULL, NULL, +/* D0 */ NULL, NULL, NULL, NULL, +/* D4 */ NULL, NULL, NULL, NULL, +/* D8 */ NULL, NULL, NULL, NULL, +/* DC */ NULL, NULL, NULL, NULL, +/* E0 */ NULL, NULL, NULL, NULL, +/* E4 */ NULL, NULL, NULL, NULL, +/* E8 */ NULL, NULL, NULL, NULL, +/* EC */ NULL, NULL, NULL, NULL, +/* F0 */ NULL, NULL, NULL, NULL, +/* F4 */ NULL, NULL, NULL, NULL, +/* F8 */ NULL, NULL, NULL, NULL, +/* FC */ NULL, NULL, NULL, NULL, +}; + +static void +OP_3DNowSuffix (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + const char *mnemonic; + + fetch_data(the_info, codep + 1); + /* AMD 3DNow! instructions are specified by an opcode suffix in the + place where an 8-bit immediate would normally go. ie. the last + byte of the instruction. */ + obufp = obuf + strlen (obuf); + mnemonic = Suffix3DNow[*codep++ & 0xff]; + if (mnemonic) + oappend (mnemonic); + else + { + /* Since a variable sized modrm/sib chunk is between the start + of the opcode (0x0f0f) and the opcode suffix, we need to do + all the modrm processing first, and don't know until now that + we have a bad opcode. This necessitates some cleaning up. */ + op_out[0][0] = '\0'; + op_out[1][0] = '\0'; + BadOp (); + } +} + +static const char *simd_cmp_op[] = { + "eq", + "lt", + "le", + "unord", + "neq", + "nlt", + "nle", + "ord" +}; + +static void +OP_SIMD_Suffix (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) +{ + unsigned int cmp_type; + + fetch_data(the_info, codep + 1); + obufp = obuf + strlen (obuf); + cmp_type = *codep++ & 0xff; + if (cmp_type < 8) + { + char suffix1 = 'p', suffix2 = 's'; + used_prefixes |= (prefixes & PREFIX_REPZ); + if (prefixes & PREFIX_REPZ) + suffix1 = 's'; + else + { + used_prefixes |= (prefixes & PREFIX_DATA); + if (prefixes & PREFIX_DATA) + suffix2 = 'd'; + else + { + used_prefixes |= (prefixes & PREFIX_REPNZ); + if (prefixes & PREFIX_REPNZ) + suffix1 = 's', suffix2 = 'd'; + } + } + snprintf (scratchbuf, sizeof(scratchbuf), "cmp%s%c%c", + simd_cmp_op[cmp_type], suffix1, suffix2); + used_prefixes |= (prefixes & PREFIX_REPZ); + oappend (scratchbuf); + } + else + { + /* We have a bad extension byte. Clean up. */ + op_out[0][0] = '\0'; + op_out[1][0] = '\0'; + BadOp (); + } +} + +static void +SIMD_Fixup (int extrachar, int sizeflag ATTRIBUTE_UNUSED) +{ + /* Change movlps/movhps to movhlps/movlhps for 2 register operand + forms of these instructions. */ + if (modrm.mod == 3) + { + char *p = obuf + strlen (obuf); + *(p + 1) = '\0'; + *p = *(p - 1); + *(p - 1) = *(p - 2); + *(p - 2) = *(p - 3); + *(p - 3) = extrachar; + } +} + +static void +PNI_Fixup (int extrachar ATTRIBUTE_UNUSED, int sizeflag) +{ + if (modrm.mod == 3 && modrm.reg == 1 && modrm.rm <= 1) + { + /* Override "sidt". */ + size_t olen = strlen (obuf); + char *p = obuf + olen - 4; + const char * const *names = (address_mode == mode_64bit + ? names64 : names32); + + /* We might have a suffix when disassembling with -Msuffix. */ + if (*p == 'i') + --p; + + /* Remove "addr16/addr32" if we aren't in Intel mode. */ + if (!intel_syntax + && (prefixes & PREFIX_ADDR) + && olen >= (4 + 7) + && *(p - 1) == ' ' + && strncmp (p - 7, "addr", 4) == 0 + && (strncmp (p - 3, "16", 2) == 0 + || strncmp (p - 3, "32", 2) == 0)) + p -= 7; + + if (modrm.rm) + { + /* mwait %eax,%ecx */ + strcpy (p, "mwait"); + if (!intel_syntax) + strcpy (op_out[0], names[0]); + } + else + { + /* monitor %eax,%ecx,%edx" */ + strcpy (p, "monitor"); + if (!intel_syntax) + { + const char * const *op1_names; + if (!(prefixes & PREFIX_ADDR)) + op1_names = (address_mode == mode_16bit + ? names16 : names); + else + { + op1_names = (address_mode != mode_32bit + ? names32 : names16); + used_prefixes |= PREFIX_ADDR; + } + strcpy (op_out[0], op1_names[0]); + strcpy (op_out[2], names[2]); + } + } + if (!intel_syntax) + { + strcpy (op_out[1], names[1]); + two_source_ops = 1; + } + + codep++; + } + else + OP_M (0, sizeflag); +} + +static void +SVME_Fixup (int bytemode, int sizeflag) +{ + const char *alt; + char *p; + + switch (*codep) + { + case 0xd8: + alt = "vmrun"; + break; + case 0xd9: + alt = "vmmcall"; + break; + case 0xda: + alt = "vmload"; + break; + case 0xdb: + alt = "vmsave"; + break; + case 0xdc: + alt = "stgi"; + break; + case 0xdd: + alt = "clgi"; + break; + case 0xde: + alt = "skinit"; + break; + case 0xdf: + alt = "invlpga"; + break; + default: + OP_M (bytemode, sizeflag); + return; + } + /* Override "lidt". */ + p = obuf + strlen (obuf) - 4; + /* We might have a suffix. */ + if (*p == 'i') + --p; + strcpy (p, alt); + if (!(prefixes & PREFIX_ADDR)) + { + ++codep; + return; + } + used_prefixes |= PREFIX_ADDR; + switch (*codep++) + { + case 0xdf: + strcpy (op_out[1], names32[1]); + two_source_ops = 1; + /* Fall through. */ + case 0xd8: + case 0xda: + case 0xdb: + *obufp++ = open_char; + if (address_mode == mode_64bit || (sizeflag & AFLAG)) + alt = names32[0]; + else + alt = names16[0]; + strcpy (obufp, alt); + obufp += strlen (alt); + *obufp++ = close_char; + *obufp = '\0'; + break; + } +} + +static void +INVLPG_Fixup (int bytemode, int sizeflag) +{ + const char *alt; + + switch (*codep) + { + case 0xf8: + alt = "swapgs"; + break; + case 0xf9: + alt = "rdtscp"; + break; + default: + OP_M (bytemode, sizeflag); + return; + } + /* Override "invlpg". */ + strcpy (obuf + strlen (obuf) - 6, alt); + codep++; +} + +static void +BadOp (void) +{ + /* Throw away prefixes and 1st. opcode byte. */ + codep = insn_codep + 1; + oappend ("(bad)"); +} + +static void +VMX_Fixup (int extrachar ATTRIBUTE_UNUSED, int sizeflag) +{ + if (modrm.mod == 3 + && modrm.reg == 0 + && modrm.rm >=1 + && modrm.rm <= 4) + { + /* Override "sgdt". */ + char *p = obuf + strlen (obuf) - 4; + + /* We might have a suffix when disassembling with -Msuffix. */ + if (*p == 'g') + --p; + + switch (modrm.rm) + { + case 1: + strcpy (p, "vmcall"); + break; + case 2: + strcpy (p, "vmlaunch"); + break; + case 3: + strcpy (p, "vmresume"); + break; + case 4: + strcpy (p, "vmxoff"); + break; + } + + codep++; + } + else + OP_E (0, sizeflag); +} + +static void +OP_VMX (int bytemode, int sizeflag) +{ + used_prefixes |= (prefixes & (PREFIX_DATA | PREFIX_REPZ)); + if (prefixes & PREFIX_DATA) + strcpy (obuf, "vmclear"); + else if (prefixes & PREFIX_REPZ) + strcpy (obuf, "vmxon"); + else + strcpy (obuf, "vmptrld"); + OP_E (bytemode, sizeflag); +} + +static void +REP_Fixup (int bytemode, int sizeflag) +{ + /* The 0xf3 prefix should be displayed as "rep" for ins, outs, movs, + lods and stos. */ + size_t ilen = 0; + + if (prefixes & PREFIX_REPZ) + switch (*insn_codep) + { + case 0x6e: /* outsb */ + case 0x6f: /* outsw/outsl */ + case 0xa4: /* movsb */ + case 0xa5: /* movsw/movsl/movsq */ + if (!intel_syntax) + ilen = 5; + else + ilen = 4; + break; + case 0xaa: /* stosb */ + case 0xab: /* stosw/stosl/stosq */ + case 0xac: /* lodsb */ + case 0xad: /* lodsw/lodsl/lodsq */ + if (!intel_syntax && (sizeflag & SUFFIX_ALWAYS)) + ilen = 5; + else + ilen = 4; + break; + case 0x6c: /* insb */ + case 0x6d: /* insl/insw */ + if (!intel_syntax) + ilen = 4; + else + ilen = 3; + break; + default: + abort (); + break; + } + + if (ilen != 0) + { + size_t olen; + char *p; + + olen = strlen (obuf); + p = obuf + olen - ilen - 1 - 4; + /* Handle "repz [addr16|addr32]". */ + if ((prefixes & PREFIX_ADDR)) + p -= 1 + 6; + + memmove (p + 3, p + 4, olen - (p + 3 - obuf)); + } + + switch (bytemode) + { + case al_reg: + case eAX_reg: + case indir_dx_reg: + OP_IMREG (bytemode, sizeflag); + break; + case eDI_reg: + OP_ESreg (bytemode, sizeflag); + break; + case eSI_reg: + OP_DSreg (bytemode, sizeflag); + break; + default: + abort (); + break; + } +} + +static void +CMPXCHG8B_Fixup (int bytemode, int sizeflag) +{ + USED_REX (REX_W); + if (rex & REX_W) + { + /* Change cmpxchg8b to cmpxchg16b. */ + char *p = obuf + strlen (obuf) - 2; + strcpy (p, "16b"); + bytemode = o_mode; + } + OP_M (bytemode, sizeflag); +} + +static void +XMM_Fixup (int reg, int sizeflag ATTRIBUTE_UNUSED) +{ + snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", reg); + oappend (scratchbuf + intel_syntax); +} + +static void +CRC32_Fixup (int bytemode, int sizeflag) +{ + /* Add proper suffix to "crc32". */ + char *p = obuf + strlen (obuf); + + switch (bytemode) + { + case b_mode: + if (intel_syntax) + break; + + *p++ = 'b'; + break; + case v_mode: + if (intel_syntax) + break; + + USED_REX (REX_W); + if (rex & REX_W) + *p++ = 'q'; + else if (sizeflag & DFLAG) + *p++ = 'l'; + else + *p++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } + *p = '\0'; + + if (modrm.mod == 3) + { + int add; + + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + + USED_REX (REX_B); + add = (rex & REX_B) ? 8 : 0; + if (bytemode == b_mode) + { + USED_REX (0); + if (rex) + oappend (names8rex[modrm.rm + add]); + else + oappend (names8[modrm.rm + add]); + } + else + { + USED_REX (REX_W); + if (rex & REX_W) + oappend (names64[modrm.rm + add]); + else if ((prefixes & PREFIX_DATA)) + oappend (names16[modrm.rm + add]); + else + oappend (names32[modrm.rm + add]); + } + } + else + OP_E (bytemode, sizeflag); +} diff --git a/disas/ia64.c b/disas/ia64.c new file mode 100644 index 0000000..a8fe26c --- /dev/null +++ b/disas/ia64.c @@ -0,0 +1,10602 @@ +/* ia64-dis.c -- Disassemble ia64 instructions + Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, see + . */ + +#include +#include + +#include "disas/bfd.h" + +/* ia64.h -- Header file for ia64 opcode table + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006 + Free Software Foundation, Inc. + Contributed by David Mosberger-Tang */ + +#include + +typedef uint64_t ia64_insn; + +enum ia64_insn_type + { + IA64_TYPE_NIL = 0, /* illegal type */ + IA64_TYPE_A, /* integer alu (I- or M-unit) */ + IA64_TYPE_I, /* non-alu integer (I-unit) */ + IA64_TYPE_M, /* memory (M-unit) */ + IA64_TYPE_B, /* branch (B-unit) */ + IA64_TYPE_F, /* floating-point (F-unit) */ + IA64_TYPE_X, /* long encoding (X-unit) */ + IA64_TYPE_DYN, /* Dynamic opcode */ + IA64_NUM_TYPES + }; + +enum ia64_unit + { + IA64_UNIT_NIL = 0, /* illegal unit */ + IA64_UNIT_I, /* integer unit */ + IA64_UNIT_M, /* memory unit */ + IA64_UNIT_B, /* branching unit */ + IA64_UNIT_F, /* floating-point unit */ + IA64_UNIT_L, /* long "unit" */ + IA64_UNIT_X, /* may be integer or branch unit */ + IA64_NUM_UNITS + }; + +/* Changes to this enumeration must be propagated to the operand table in + bfd/cpu-ia64-opc.c + */ +enum ia64_opnd + { + IA64_OPND_NIL, /* no operand---MUST BE FIRST!*/ + + /* constants */ + IA64_OPND_AR_CSD, /* application register csd (ar.csd) */ + IA64_OPND_AR_CCV, /* application register ccv (ar.ccv) */ + IA64_OPND_AR_PFS, /* application register pfs (ar.pfs) */ + IA64_OPND_C1, /* the constant 1 */ + IA64_OPND_C8, /* the constant 8 */ + IA64_OPND_C16, /* the constant 16 */ + IA64_OPND_GR0, /* gr0 */ + IA64_OPND_IP, /* instruction pointer (ip) */ + IA64_OPND_PR, /* predicate register (pr) */ + IA64_OPND_PR_ROT, /* rotating predicate register (pr.rot) */ + IA64_OPND_PSR, /* processor status register (psr) */ + IA64_OPND_PSR_L, /* processor status register L (psr.l) */ + IA64_OPND_PSR_UM, /* processor status register UM (psr.um) */ + + /* register operands: */ + IA64_OPND_AR3, /* third application register # (bits 20-26) */ + IA64_OPND_B1, /* branch register # (bits 6-8) */ + IA64_OPND_B2, /* branch register # (bits 13-15) */ + IA64_OPND_CR3, /* third control register # (bits 20-26) */ + IA64_OPND_F1, /* first floating-point register # */ + IA64_OPND_F2, /* second floating-point register # */ + IA64_OPND_F3, /* third floating-point register # */ + IA64_OPND_F4, /* fourth floating-point register # */ + IA64_OPND_P1, /* first predicate # */ + IA64_OPND_P2, /* second predicate # */ + IA64_OPND_R1, /* first register # */ + IA64_OPND_R2, /* second register # */ + IA64_OPND_R3, /* third register # */ + IA64_OPND_R3_2, /* third register # (limited to gr0-gr3) */ + + /* memory operands: */ + IA64_OPND_MR3, /* memory at addr of third register # */ + + /* indirect operands: */ + IA64_OPND_CPUID_R3, /* cpuid[reg] */ + IA64_OPND_DBR_R3, /* dbr[reg] */ + IA64_OPND_DTR_R3, /* dtr[reg] */ + IA64_OPND_ITR_R3, /* itr[reg] */ + IA64_OPND_IBR_R3, /* ibr[reg] */ + IA64_OPND_MSR_R3, /* msr[reg] */ + IA64_OPND_PKR_R3, /* pkr[reg] */ + IA64_OPND_PMC_R3, /* pmc[reg] */ + IA64_OPND_PMD_R3, /* pmd[reg] */ + IA64_OPND_RR_R3, /* rr[reg] */ + + /* immediate operands: */ + IA64_OPND_CCNT5, /* 5-bit count (31 - bits 20-24) */ + IA64_OPND_CNT2a, /* 2-bit count (1 + bits 27-28) */ + IA64_OPND_CNT2b, /* 2-bit count (bits 27-28): 1, 2, 3 */ + IA64_OPND_CNT2c, /* 2-bit count (bits 30-31): 0, 7, 15, or 16 */ + IA64_OPND_CNT5, /* 5-bit count (bits 14-18) */ + IA64_OPND_CNT6, /* 6-bit count (bits 27-32) */ + IA64_OPND_CPOS6a, /* 6-bit count (63 - bits 20-25) */ + IA64_OPND_CPOS6b, /* 6-bit count (63 - bits 14-19) */ + IA64_OPND_CPOS6c, /* 6-bit count (63 - bits 31-36) */ + IA64_OPND_IMM1, /* signed 1-bit immediate (bit 36) */ + IA64_OPND_IMMU2, /* unsigned 2-bit immediate (bits 13-14) */ + IA64_OPND_IMMU5b, /* unsigned 5-bit immediate (32 + bits 14-18) */ + IA64_OPND_IMMU7a, /* unsigned 7-bit immediate (bits 13-19) */ + IA64_OPND_IMMU7b, /* unsigned 7-bit immediate (bits 20-26) */ + IA64_OPND_SOF, /* 8-bit stack frame size */ + IA64_OPND_SOL, /* 8-bit size of locals */ + IA64_OPND_SOR, /* 6-bit number of rotating registers (scaled by 8) */ + IA64_OPND_IMM8, /* signed 8-bit immediate (bits 13-19 & 36) */ + IA64_OPND_IMM8U4, /* cmp4*u signed 8-bit immediate (bits 13-19 & 36) */ + IA64_OPND_IMM8M1, /* signed 8-bit immediate -1 (bits 13-19 & 36) */ + IA64_OPND_IMM8M1U4, /* cmp4*u signed 8-bit immediate -1 (bits 13-19 & 36)*/ + IA64_OPND_IMM8M1U8, /* cmp*u signed 8-bit immediate -1 (bits 13-19 & 36) */ + IA64_OPND_IMMU9, /* unsigned 9-bit immediate (bits 33-34, 20-26) */ + IA64_OPND_IMM9a, /* signed 9-bit immediate (bits 6-12, 27, 36) */ + IA64_OPND_IMM9b, /* signed 9-bit immediate (bits 13-19, 27, 36) */ + IA64_OPND_IMM14, /* signed 14-bit immediate (bits 13-19, 27-32, 36) */ + IA64_OPND_IMM17, /* signed 17-bit immediate (2*bits 6-12, 24-31, 36) */ + IA64_OPND_IMMU21, /* unsigned 21-bit immediate (bits 6-25, 36) */ + IA64_OPND_IMM22, /* signed 22-bit immediate (bits 13-19, 22-36) */ + IA64_OPND_IMMU24, /* unsigned 24-bit immediate (bits 6-26, 31-32, 36) */ + IA64_OPND_IMM44, /* signed 44-bit immediate (2^16*bits 6-32, 36) */ + IA64_OPND_IMMU62, /* unsigned 62-bit immediate */ + IA64_OPND_IMMU64, /* unsigned 64-bit immediate (lotsa bits...) */ + IA64_OPND_INC3, /* signed 3-bit (bits 13-15): +/-1, 4, 8, 16 */ + IA64_OPND_LEN4, /* 4-bit count (bits 27-30 + 1) */ + IA64_OPND_LEN6, /* 6-bit count (bits 27-32 + 1) */ + IA64_OPND_MBTYPE4, /* 4-bit mux type (bits 20-23) */ + IA64_OPND_MHTYPE8, /* 8-bit mux type (bits 20-27) */ + IA64_OPND_POS6, /* 6-bit count (bits 14-19) */ + IA64_OPND_TAG13, /* signed 13-bit tag (ip + 16*bits 6-12, 33-34) */ + IA64_OPND_TAG13b, /* signed 13-bit tag (ip + 16*bits 24-32) */ + IA64_OPND_TGT25, /* signed 25-bit (ip + 16*bits 6-25, 36) */ + IA64_OPND_TGT25b, /* signed 25-bit (ip + 16*bits 6-12, 20-32, 36) */ + IA64_OPND_TGT25c, /* signed 25-bit (ip + 16*bits 13-32, 36) */ + IA64_OPND_TGT64, /* 64-bit (ip + 16*bits 13-32, 36, 2-40(L)) */ + IA64_OPND_LDXMOV, /* any symbol, generates R_IA64_LDXMOV. */ + + IA64_OPND_COUNT /* # of operand types (MUST BE LAST!) */ + }; + +enum ia64_dependency_mode +{ + IA64_DV_RAW, + IA64_DV_WAW, + IA64_DV_WAR, +}; + +enum ia64_dependency_semantics +{ + IA64_DVS_NONE, + IA64_DVS_IMPLIED, + IA64_DVS_IMPLIEDF, + IA64_DVS_DATA, + IA64_DVS_INSTR, + IA64_DVS_SPECIFIC, + IA64_DVS_STOP, + IA64_DVS_OTHER, +}; + +enum ia64_resource_specifier +{ + IA64_RS_ANY, + IA64_RS_AR_K, + IA64_RS_AR_UNAT, + IA64_RS_AR, /* 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111 */ + IA64_RS_ARb, /* 48-63, 112-127 */ + IA64_RS_BR, + IA64_RS_CFM, + IA64_RS_CPUID, + IA64_RS_CR_IRR, + IA64_RS_CR_LRR, + IA64_RS_CR, /* 3-7,10-15,18,26-63,75-79,82-127 */ + IA64_RS_DBR, + IA64_RS_FR, + IA64_RS_FRb, + IA64_RS_GR0, + IA64_RS_GR, + IA64_RS_IBR, + IA64_RS_INSERVICE, /* CR[EOI] or CR[IVR] */ + IA64_RS_MSR, + IA64_RS_PKR, + IA64_RS_PMC, + IA64_RS_PMD, + IA64_RS_PR, /* non-rotating, 1-15 */ + IA64_RS_PRr, /* rotating, 16-62 */ + IA64_RS_PR63, + IA64_RS_RR, + + IA64_RS_ARX, /* ARs not in RS_AR or RS_ARb */ + IA64_RS_CRX, /* CRs not in RS_CR */ + IA64_RS_PSR, /* PSR bits */ + IA64_RS_RSE, /* implementation-specific RSE resources */ + IA64_RS_AR_FPSR, +}; + +enum ia64_rse_resource +{ + IA64_RSE_N_STACKED_PHYS, + IA64_RSE_BOF, + IA64_RSE_STORE_REG, + IA64_RSE_LOAD_REG, + IA64_RSE_BSPLOAD, + IA64_RSE_RNATBITINDEX, + IA64_RSE_CFLE, + IA64_RSE_NDIRTY, +}; + +/* Information about a given resource dependency */ +struct ia64_dependency +{ + /* Name of the resource */ + const char *name; + /* Does this dependency need further specification? */ + enum ia64_resource_specifier specifier; + /* Mode of dependency */ + enum ia64_dependency_mode mode; + /* Dependency semantics */ + enum ia64_dependency_semantics semantics; + /* Register index, if applicable (distinguishes AR, CR, and PSR deps) */ +#define REG_NONE (-1) + int regindex; + /* Special info on semantics */ + const char *info; +}; + +/* Two arrays of indexes into the ia64_dependency table. + chks are dependencies to check for conflicts when an opcode is + encountered; regs are dependencies to register (mark as used) when an + opcode is used. chks correspond to readers (RAW) or writers (WAW or + WAR) of a resource, while regs correspond to writers (RAW or WAW) and + readers (WAR) of a resource. */ +struct ia64_opcode_dependency +{ + int nchks; + const unsigned short *chks; + int nregs; + const unsigned short *regs; +}; + +/* encode/extract the note/index for a dependency */ +#define RDEP(N,X) (((N)<<11)|(X)) +#define NOTE(X) (((X)>>11)&0x1F) +#define DEP(X) ((X)&0x7FF) + +/* A template descriptor describes the execution units that are active + for each of the three slots. It also specifies the location of + instruction group boundaries that may be present between two slots. */ +struct ia64_templ_desc + { + int group_boundary; /* 0=no boundary, 1=between slot 0 & 1, etc. */ + enum ia64_unit exec_unit[3]; + const char *name; + }; + +/* The opcode table is an array of struct ia64_opcode. */ + +struct ia64_opcode + { + /* The opcode name. */ + const char *name; + + /* The type of the instruction: */ + enum ia64_insn_type type; + + /* Number of output operands: */ + int num_outputs; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + ia64_insn opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + ia64_insn mask; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + enum ia64_opnd operands[5]; + + /* One bit flags for the opcode. These are primarily used to + indicate specific processors and environments support the + instructions. The defined values are listed below. */ + unsigned int flags; + + /* Used by ia64_find_next_opcode (). */ + short ent_index; + + /* Opcode dependencies. */ + const struct ia64_opcode_dependency *dependencies; + }; + +/* Values defined for the flags field of a struct ia64_opcode. */ + +#define IA64_OPCODE_FIRST (1<<0) /* must be first in an insn group */ +#define IA64_OPCODE_X_IN_MLX (1<<1) /* insn is allowed in X slot of MLX */ +#define IA64_OPCODE_LAST (1<<2) /* must be last in an insn group */ +#define IA64_OPCODE_PRIV (1<<3) /* privileged instruct */ +#define IA64_OPCODE_SLOT2 (1<<4) /* insn allowed in slot 2 only */ +#define IA64_OPCODE_NO_PRED (1<<5) /* insn cannot be predicated */ +#define IA64_OPCODE_PSEUDO (1<<6) /* insn is a pseudo-op */ +#define IA64_OPCODE_F2_EQ_F3 (1<<7) /* constraint: F2 == F3 */ +#define IA64_OPCODE_LEN_EQ_64MCNT (1<<8) /* constraint: LEN == 64-CNT */ +#define IA64_OPCODE_MOD_RRBS (1<<9) /* modifies all rrbs in CFM */ +#define IA64_OPCODE_POSTINC (1<<10) /* postincrement MR3 operand */ + +/* A macro to extract the major opcode from an instruction. */ +#define IA64_OP(i) (((i) >> 37) & 0xf) + +enum ia64_operand_class + { + IA64_OPND_CLASS_CST, /* constant */ + IA64_OPND_CLASS_REG, /* register */ + IA64_OPND_CLASS_IND, /* indirect register */ + IA64_OPND_CLASS_ABS, /* absolute value */ + IA64_OPND_CLASS_REL, /* IP-relative value */ + }; + +/* The operands table is an array of struct ia64_operand. */ + +struct ia64_operand +{ + enum ia64_operand_class class; + + /* Set VALUE as the operand bits for the operand of type SELF in the + instruction pointed to by CODE. If an error occurs, *CODE is not + modified and the returned string describes the cause of the + error. If no error occurs, NULL is returned. */ + const char *(*insert) (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code); + + /* Extract the operand bits for an operand of type SELF from + instruction CODE store them in *VALUE. If an error occurs, the + cause of the error is described by the string returned. If no + error occurs, NULL is returned. */ + const char *(*extract) (const struct ia64_operand *self, ia64_insn code, + ia64_insn *value); + + /* A string whose meaning depends on the operand class. */ + + const char *str; + + struct bit_field + { + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + } + field[4]; /* no operand has more than this many bit-fields */ + + unsigned int flags; + + const char *desc; /* brief description */ +}; + +/* Values defined for the flags field of a struct ia64_operand. */ + +/* Disassemble as signed decimal (instead of hex): */ +#define IA64_OPND_FLAG_DECIMAL_SIGNED (1<<0) +/* Disassemble as unsigned decimal (instead of hex): */ +#define IA64_OPND_FLAG_DECIMAL_UNSIGNED (1<<1) + +#define NELEMS(a) ((int) (sizeof (a) / sizeof (a[0]))) + +static const char* +ins_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED) +{ + return "internal error---this shouldn't happen"; +} + +static const char* +ext_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED) +{ + return "internal error---this shouldn't happen"; +} + +static const char* +ins_const (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char* +ext_const (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char* +ins_reg (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + if (value >= 1u << self->field[0].bits) + return "register number out of range"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_reg (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) + & ((1u << self->field[0].bits) - 1)); + return 0; +} + +static const char* +ins_immu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + ia64_insn new = 0; + int i; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + new |= ((value & ((((ia64_insn) 1) << self->field[i].bits) - 1)) + << self->field[i].shift); + value >>= self->field[i].bits; + } + if (value) + return "integer operand out of range"; + + *code |= new; + return 0; +} + +static const char* +ext_immu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + uint64_t value = 0; + int i, bits = 0, total = 0; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + bits = self->field[i].bits; + value |= ((code >> self->field[i].shift) + & ((((uint64_t) 1) << bits) - 1)) << total; + total += bits; + } + *valuep = value; + return 0; +} + +static const char* +ins_immu5b (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code) +{ + if (value < 32 || value > 63) + return "value must be between 32 and 63"; + return ins_immu (self, value - 32, code); +} + +static const char* +ext_immu5b (const struct ia64_operand *self, ia64_insn code, + ia64_insn *valuep) +{ + const char *result; + + result = ext_immu (self, code, valuep); + if (result) + return result; + + *valuep = *valuep + 32; + return 0; +} + +static const char* +ins_immus8 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + if (value & 0x7) + return "value not an integer multiple of 8"; + return ins_immu (self, value >> 3, code); +} + +static const char* +ext_immus8 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *result; + + result = ext_immu (self, code, valuep); + if (result) + return result; + + *valuep = *valuep << 3; + return 0; +} + +static const char* +ins_imms_scaled (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code, int scale) +{ + int64_t svalue = value, sign_bit = 0; + ia64_insn new = 0; + int i; + + svalue >>= scale; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + new |= ((svalue & ((((ia64_insn) 1) << self->field[i].bits) - 1)) + << self->field[i].shift); + sign_bit = (svalue >> (self->field[i].bits - 1)) & 1; + svalue >>= self->field[i].bits; + } + if ((!sign_bit && svalue != 0) || (sign_bit && svalue != -1)) + return "integer operand out of range"; + + *code |= new; + return 0; +} + +static const char* +ext_imms_scaled (const struct ia64_operand *self, ia64_insn code, + ia64_insn *valuep, int scale) +{ + int i, bits = 0, total = 0; + int64_t val = 0, sign; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + bits = self->field[i].bits; + val |= ((code >> self->field[i].shift) + & ((((uint64_t) 1) << bits) - 1)) << total; + total += bits; + } + /* sign extend: */ + sign = (int64_t) 1 << (total - 1); + val = (val ^ sign) - sign; + + *valuep = (val << scale); + return 0; +} + +static const char* +ins_imms (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ins_immsu4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; + + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ext_imms (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 0); +} + +static const char* +ins_immsm1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ins_immsm1u4 (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code) +{ + value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; + + --value; + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ext_immsm1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *res = ext_imms_scaled (self, code, valuep, 0); + + ++*valuep; + return res; +} + +static const char* +ins_imms1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 1); +} + +static const char* +ext_imms1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 1); +} + +static const char* +ins_imms4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 4); +} + +static const char* +ext_imms4 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 4); +} + +static const char* +ins_imms16 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 16); +} + +static const char* +ext_imms16 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 16); +} + +static const char* +ins_cimmu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + ia64_insn mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + return ins_immu (self, value ^ mask, code); +} + +static const char* +ext_cimmu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *result; + ia64_insn mask; + + mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + result = ext_immu (self, code, valuep); + if (!result) + { + mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + *valuep ^= mask; + } + return result; +} + +static const char* +ins_cnt (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + if (value >= ((uint64_t) 1) << self->field[0].bits) + return "count out of range"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) + & ((((uint64_t) 1) << self->field[0].bits) - 1)) + 1; + return 0; +} + +static const char* +ins_cnt2b (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + + if (value > 2) + return "count must be in range 1..3"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt2b (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) & 0x3) + 1; + return 0; +} + +static const char* +ins_cnt2c (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + switch (value) + { + case 0: value = 0; break; + case 7: value = 1; break; + case 15: value = 2; break; + case 16: value = 3; break; + default: return "count must be 0, 7, 15, or 16"; + } + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt2c (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + ia64_insn value; + + value = (code >> self->field[0].shift) & 0x3; + switch (value) + { + case 0: value = 0; break; + case 1: value = 7; break; + case 2: value = 15; break; + case 3: value = 16; break; + } + *valuep = value; + return 0; +} + +static const char* +ins_inc3 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + int64_t val = value; + uint64_t sign = 0; + + if (val < 0) + { + sign = 0x4; + value = -value; + } + switch (value) + { + case 1: value = 3; break; + case 4: value = 2; break; + case 8: value = 1; break; + case 16: value = 0; break; + default: return "count must be +/- 1, 4, 8, or 16"; + } + *code |= (sign | value) << self->field[0].shift; + return 0; +} + +static const char* +ext_inc3 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + int64_t val; + int negate; + + val = (code >> self->field[0].shift) & 0x7; + negate = val & 0x4; + switch (val & 0x3) + { + case 0: val = 16; break; + case 1: val = 8; break; + case 2: val = 4; break; + case 3: val = 1; break; + } + if (negate) + val = -val; + + *valuep = val; + return 0; +} + +/* glib.h defines ABS so we must undefine it to avoid a clash */ +#undef ABS + +#define CST IA64_OPND_CLASS_CST +#define REG IA64_OPND_CLASS_REG +#define IND IA64_OPND_CLASS_IND +#define ABS IA64_OPND_CLASS_ABS +#define REL IA64_OPND_CLASS_REL + +#define SDEC IA64_OPND_FLAG_DECIMAL_SIGNED +#define UDEC IA64_OPND_FLAG_DECIMAL_UNSIGNED + +const struct ia64_operand elf64_ia64_operands[IA64_OPND_COUNT] = + { + /* constants: */ + { CST, ins_const, ext_const, "NIL", {{ 0, 0}}, 0, "" }, + { CST, ins_const, ext_const, "ar.csd", {{ 0, 0}}, 0, "ar.csd" }, + { CST, ins_const, ext_const, "ar.ccv", {{ 0, 0}}, 0, "ar.ccv" }, + { CST, ins_const, ext_const, "ar.pfs", {{ 0, 0}}, 0, "ar.pfs" }, + { CST, ins_const, ext_const, "1", {{ 0, 0}}, 0, "1" }, + { CST, ins_const, ext_const, "8", {{ 0, 0}}, 0, "8" }, + { CST, ins_const, ext_const, "16", {{ 0, 0}}, 0, "16" }, + { CST, ins_const, ext_const, "r0", {{ 0, 0}}, 0, "r0" }, + { CST, ins_const, ext_const, "ip", {{ 0, 0}}, 0, "ip" }, + { CST, ins_const, ext_const, "pr", {{ 0, 0}}, 0, "pr" }, + { CST, ins_const, ext_const, "pr.rot", {{ 0, 0}}, 0, "pr.rot" }, + { CST, ins_const, ext_const, "psr", {{ 0, 0}}, 0, "psr" }, + { CST, ins_const, ext_const, "psr.l", {{ 0, 0}}, 0, "psr.l" }, + { CST, ins_const, ext_const, "psr.um", {{ 0, 0}}, 0, "psr.um" }, + + /* register operands: */ + { REG, ins_reg, ext_reg, "ar", {{ 7, 20}}, 0, /* AR3 */ + "an application register" }, + { REG, ins_reg, ext_reg, "b", {{ 3, 6}}, 0, /* B1 */ + "a branch register" }, + { REG, ins_reg, ext_reg, "b", {{ 3, 13}}, 0, /* B2 */ + "a branch register"}, + { REG, ins_reg, ext_reg, "cr", {{ 7, 20}}, 0, /* CR */ + "a control register"}, + { REG, ins_reg, ext_reg, "f", {{ 7, 6}}, 0, /* F1 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 13}}, 0, /* F2 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 20}}, 0, /* F3 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 27}}, 0, /* F4 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "p", {{ 6, 6}}, 0, /* P1 */ + "a predicate register" }, + { REG, ins_reg, ext_reg, "p", {{ 6, 27}}, 0, /* P2 */ + "a predicate register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 6}}, 0, /* R1 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 13}}, 0, /* R2 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 20}}, 0, /* R3 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 2, 20}}, 0, /* R3_2 */ + "a general register r0-r3" }, + + /* memory operands: */ + { IND, ins_reg, ext_reg, "", {{7, 20}}, 0, /* MR3 */ + "a memory address" }, + + /* indirect operands: */ + { IND, ins_reg, ext_reg, "cpuid", {{7, 20}}, 0, /* CPUID_R3 */ + "a cpuid register" }, + { IND, ins_reg, ext_reg, "dbr", {{7, 20}}, 0, /* DBR_R3 */ + "a dbr register" }, + { IND, ins_reg, ext_reg, "dtr", {{7, 20}}, 0, /* DTR_R3 */ + "a dtr register" }, + { IND, ins_reg, ext_reg, "itr", {{7, 20}}, 0, /* ITR_R3 */ + "an itr register" }, + { IND, ins_reg, ext_reg, "ibr", {{7, 20}}, 0, /* IBR_R3 */ + "an ibr register" }, + { IND, ins_reg, ext_reg, "msr", {{7, 20}}, 0, /* MSR_R3 */ + "an msr register" }, + { IND, ins_reg, ext_reg, "pkr", {{7, 20}}, 0, /* PKR_R3 */ + "a pkr register" }, + { IND, ins_reg, ext_reg, "pmc", {{7, 20}}, 0, /* PMC_R3 */ + "a pmc register" }, + { IND, ins_reg, ext_reg, "pmd", {{7, 20}}, 0, /* PMD_R3 */ + "a pmd register" }, + { IND, ins_reg, ext_reg, "rr", {{7, 20}}, 0, /* RR_R3 */ + "an rr register" }, + + /* immediate operands: */ + { ABS, ins_cimmu, ext_cimmu, 0, {{ 5, 20 }}, UDEC, /* CCNT5 */ + "a 5-bit count (0-31)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 2, 27 }}, UDEC, /* CNT2a */ + "a 2-bit count (1-4)" }, + { ABS, ins_cnt2b, ext_cnt2b, 0, {{ 2, 27 }}, UDEC, /* CNT2b */ + "a 2-bit count (1-3)" }, + { ABS, ins_cnt2c, ext_cnt2c, 0, {{ 2, 30 }}, UDEC, /* CNT2c */ + "a count (0, 7, 15, or 16)" }, + { ABS, ins_immu, ext_immu, 0, {{ 5, 14}}, UDEC, /* CNT5 */ + "a 5-bit count (0-31)" }, + { ABS, ins_immu, ext_immu, 0, {{ 6, 27}}, UDEC, /* CNT6 */ + "a 6-bit count (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 20}}, UDEC, /* CPOS6a */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 14}}, UDEC, /* CPOS6b */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 31}}, UDEC, /* CPOS6c */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_imms, ext_imms, 0, {{ 1, 36}}, SDEC, /* IMM1 */ + "a 1-bit integer (-1, 0)" }, + { ABS, ins_immu, ext_immu, 0, {{ 2, 13}}, UDEC, /* IMMU2 */ + "a 2-bit unsigned (0-3)" }, + { ABS, ins_immu5b, ext_immu5b, 0, {{ 5, 14}}, UDEC, /* IMMU5b */ + "a 5-bit unsigned (32 + (0-31))" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, 0, /* IMMU7a */ + "a 7-bit unsigned (0-127)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, 0, /* IMMU7b */ + "a 7-bit unsigned (0-127)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, UDEC, /* SOF */ + "a frame size (register count)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, UDEC, /* SOL */ + "a local register count" }, + { ABS, ins_immus8,ext_immus8,0, {{ 4, 27}}, UDEC, /* SOR */ + "a rotating register count (integer multiple of 8)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM8 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer (-128-127)" }, + { ABS, ins_immsu4, ext_imms, 0, /* IMM8U4 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit signed integer for 32-bit unsigned compare (-128-127)" }, + { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer (-127-128)" }, + { ABS, ins_immsm1u4, ext_immsm1, 0, /* IMM8M1U4 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer for 32-bit unsigned compare (-127-(-1),1-128,0x100000000)" }, + { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1U8 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer for 64-bit unsigned compare (-127-(-1),1-128,0x10000000000000000)" }, + { ABS, ins_immu, ext_immu, 0, {{ 2, 33}, { 7, 20}}, 0, /* IMMU9 */ + "a 9-bit unsigned (0-511)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM9a */ + {{ 7, 6}, { 1, 27}, { 1, 36}}, SDEC, + "a 9-bit integer (-256-255)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM9b */ + {{ 7, 13}, { 1, 27}, { 1, 36}}, SDEC, + "a 9-bit integer (-256-255)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM14 */ + {{ 7, 13}, { 6, 27}, { 1, 36}}, SDEC, + "a 14-bit integer (-8192-8191)" }, + { ABS, ins_imms1, ext_imms1, 0, /* IMM17 */ + {{ 7, 6}, { 8, 24}, { 1, 36}}, 0, + "a 17-bit integer (-65536-65535)" }, + { ABS, ins_immu, ext_immu, 0, {{20, 6}, { 1, 36}}, 0, /* IMMU21 */ + "a 21-bit unsigned" }, + { ABS, ins_imms, ext_imms, 0, /* IMM22 */ + {{ 7, 13}, { 9, 27}, { 5, 22}, { 1, 36}}, SDEC, + "a 22-bit signed integer" }, + { ABS, ins_immu, ext_immu, 0, /* IMMU24 */ + {{21, 6}, { 2, 31}, { 1, 36}}, 0, + "a 24-bit unsigned" }, + { ABS, ins_imms16,ext_imms16,0, {{27, 6}, { 1, 36}}, 0, /* IMM44 */ + "a 44-bit unsigned (least 16 bits ignored/zeroes)" }, + { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU62 */ + "a 62-bit unsigned" }, + { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU64 */ + "a 64-bit unsigned" }, + { ABS, ins_inc3, ext_inc3, 0, {{ 3, 13}}, SDEC, /* INC3 */ + "an increment (+/- 1, 4, 8, or 16)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 4, 27}}, UDEC, /* LEN4 */ + "a 4-bit length (1-16)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 6, 27}}, UDEC, /* LEN6 */ + "a 6-bit length (1-64)" }, + { ABS, ins_immu, ext_immu, 0, {{ 4, 20}}, 0, /* MBTYPE4 */ + "a mix type (@rev, @mix, @shuf, @alt, or @brcst)" }, + { ABS, ins_immu, ext_immu, 0, {{ 8, 20}}, 0, /* MBTYPE8 */ + "an 8-bit mix type" }, + { ABS, ins_immu, ext_immu, 0, {{ 6, 14}}, UDEC, /* POS6 */ + "a 6-bit bit pos (0-63)" }, + { REL, ins_imms4, ext_imms4, 0, {{ 7, 6}, { 2, 33}}, 0, /* TAG13 */ + "a branch tag" }, + { REL, ins_imms4, ext_imms4, 0, {{ 9, 24}}, 0, /* TAG13b */ + "a branch tag" }, + { REL, ins_imms4, ext_imms4, 0, {{20, 6}, { 1, 36}}, 0, /* TGT25 */ + "a branch target" }, + { REL, ins_imms4, ext_imms4, 0, /* TGT25b */ + {{ 7, 6}, {13, 20}, { 1, 36}}, 0, + "a branch target" }, + { REL, ins_imms4, ext_imms4, 0, {{20, 13}, { 1, 36}}, 0, /* TGT25c */ + "a branch target" }, + { REL, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* TGT64 */ + "a branch target" }, + + { ABS, ins_const, ext_const, 0, {{0, 0}}, 0, /* LDXMOV */ + "ldxmov target" }, + }; + + +/* ia64-asmtab.h -- Header for compacted IA-64 opcode tables. + Copyright 1999, 2000 Free Software Foundation, Inc. + Contributed by Bob Manson of Cygnus Support + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, see + . */ + +/* The primary opcode table is made up of the following: */ +struct ia64_main_table +{ + /* The entry in the string table that corresponds to the name of this + opcode. */ + unsigned short name_index; + + /* The type of opcode; corresponds to the TYPE field in + struct ia64_opcode. */ + unsigned char opcode_type; + + /* The number of outputs for this opcode. */ + unsigned char num_outputs; + + /* The base insn value for this opcode. It may be modified by completers. */ + ia64_insn opcode; + + /* The mask of valid bits in OPCODE. Zeros indicate operand fields. */ + ia64_insn mask; + + /* The operands of this instruction. Corresponds to the OPERANDS field + in struct ia64_opcode. */ + unsigned char operands[5]; + + /* The flags for this instruction. Corresponds to the FLAGS field in + struct ia64_opcode. */ + short flags; + + /* The tree of completers for this instruction; this is an offset into + completer_table. */ + short completers; +}; + +/* Each instruction has a set of possible "completers", or additional + suffixes that can alter the instruction's behavior, and which has + potentially different dependencies. + + The completer entries modify certain bits in the instruction opcode. + Which bits are to be modified are marked by the BITS, MASK and + OFFSET fields. The completer entry may also note dependencies for the + opcode. + + These completers are arranged in a DAG; the pointers are indexes + into the completer_table array. The completer DAG is searched by + find_completer () and ia64_find_matching_opcode (). + + Note that each completer needs to be applied in turn, so that if we + have the instruction + cmp.lt.unc + the completer entries for both "lt" and "unc" would need to be applied + to the opcode's value. + + Some instructions do not require any completers; these contain an + empty completer entry. Instructions that require a completer do + not contain an empty entry. + + Terminal completers (those completers that validly complete an + instruction) are marked by having the TERMINAL_COMPLETER flag set. + + Only dependencies listed in the terminal completer for an opcode are + considered to apply to that opcode instance. */ + +struct ia64_completer_table +{ + /* The bit value that this completer sets. */ + unsigned int bits; + + /* And its mask. 1s are bits that are to be modified in the + instruction. */ + unsigned int mask; + + /* The entry in the string table that corresponds to the name of this + completer. */ + unsigned short name_index; + + /* An alternative completer, or -1 if this is the end of the chain. */ + short alternative; + + /* A pointer to the DAG of completers that can potentially follow + this one, or -1. */ + short subentries; + + /* The bit offset in the instruction where BITS and MASK should be + applied. */ + unsigned char offset : 7; + + unsigned char terminal_completer : 1; + + /* Index into the dependency list table */ + short dependencies; +}; + +/* This contains sufficient information for the disassembler to resolve + the complete name of the original instruction. */ +struct ia64_dis_names +{ + /* COMPLETER_INDEX represents the tree of completers that make up + the instruction. The LSB represents the top of the tree for the + specified instruction. + + A 0 bit indicates to go to the next alternate completer via the + alternative field; a 1 bit indicates that the current completer + is part of the instruction, and to go down the subentries index. + We know we've reached the final completer when we run out of 1 + bits. + + There is always at least one 1 bit. */ + unsigned int completer_index : 20; + + /* The index in the main_table[] array for the instruction. */ + unsigned short insn_index : 11; + + /* If set, the next entry in this table is an alternate possibility + for this instruction encoding. Which one to use is determined by + the instruction type and other factors (see opcode_verify ()). */ + unsigned int next_flag : 1; + + /* The disassembly priority of this entry among instructions. */ + unsigned short priority; +}; + +static const char * const ia64_strings[] = { + "", "0", "1", "a", "acq", "add", "addl", "addp4", "adds", "alloc", "and", + "andcm", "b", "bias", "br", "break", "brl", "brp", "bsw", "c", "call", + "cexit", "chk", "cloop", "clr", "clrrrb", "cmp", "cmp4", "cmp8xchg16", + "cmpxchg1", "cmpxchg2", "cmpxchg4", "cmpxchg8", "cond", "cover", "ctop", + "czx1", "czx2", "d", "dep", "dpnt", "dptk", "e", "epc", "eq", "excl", + "exit", "exp", "extr", "f", "fabs", "fadd", "famax", "famin", "fand", + "fandcm", "fault", "fc", "fchkf", "fclass", "fclrf", "fcmp", "fcvt", + "fetchadd4", "fetchadd8", "few", "fill", "flushrs", "fma", "fmax", + "fmerge", "fmin", "fmix", "fmpy", "fms", "fneg", "fnegabs", "fnma", + "fnmpy", "fnorm", "for", "fpabs", "fpack", "fpamax", "fpamin", "fpcmp", + "fpcvt", "fpma", "fpmax", "fpmerge", "fpmin", "fpmpy", "fpms", "fpneg", + "fpnegabs", "fpnma", "fpnmpy", "fprcpa", "fprsqrta", "frcpa", "frsqrta", + "fselect", "fsetc", "fsub", "fswap", "fsxt", "fwb", "fx", "fxor", "fxu", + "g", "ga", "ge", "getf", "geu", "gt", "gtu", "h", "hint", "hu", "i", "ia", + "imp", "invala", "itc", "itr", "l", "ld1", "ld16", "ld2", "ld4", "ld8", + "ldf", "ldf8", "ldfd", "ldfe", "ldfp8", "ldfpd", "ldfps", "ldfs", "le", + "leu", "lfetch", "loadrs", "loop", "lr", "lt", "ltu", "lu", "m", "many", + "mf", "mix1", "mix2", "mix4", "mov", "movl", "mux1", "mux2", "nc", "ne", + "neq", "nge", "ngt", "nl", "nle", "nlt", "nm", "nop", "nr", "ns", "nt1", + "nt2", "nta", "nz", "or", "orcm", "ord", "pack2", "pack4", "padd1", + "padd2", "padd4", "pavg1", "pavg2", "pavgsub1", "pavgsub2", "pcmp1", + "pcmp2", "pcmp4", "pmax1", "pmax2", "pmin1", "pmin2", "pmpy2", "pmpyshr2", + "popcnt", "pr", "probe", "psad1", "pshl2", "pshl4", "pshladd2", "pshr2", + "pshr4", "pshradd2", "psub1", "psub2", "psub4", "ptc", "ptr", "r", "raz", + "rel", "ret", "rfi", "rsm", "rum", "rw", "s", "s0", "s1", "s2", "s3", + "sa", "se", "setf", "shl", "shladd", "shladdp4", "shr", "shrp", "sig", + "spill", "spnt", "sptk", "srlz", "ssm", "sss", "st1", "st16", "st2", + "st4", "st8", "stf", "stf8", "stfd", "stfe", "stfs", "sub", "sum", "sxt1", + "sxt2", "sxt4", "sync", "tak", "tbit", "tf", "thash", "tnat", "tpa", + "trunc", "ttag", "u", "unc", "unord", "unpack1", "unpack2", "unpack4", + "uss", "uus", "uuu", "vmsw", "w", "wexit", "wtop", "x", "xchg1", "xchg2", + "xchg4", "xchg8", "xf", "xma", "xmpy", "xor", "xuf", "z", "zxt1", "zxt2", + "zxt4", +}; + +static const struct ia64_dependency +dependencies[] = { + { "ALAT", 0, 0, 0, -1, NULL, }, + { "AR[BSP]", 26, 0, 2, 17, NULL, }, + { "AR[BSPSTORE]", 26, 0, 2, 18, NULL, }, + { "AR[CCV]", 26, 0, 2, 32, NULL, }, + { "AR[CFLG]", 26, 0, 2, 27, NULL, }, + { "AR[CSD]", 26, 0, 2, 25, NULL, }, + { "AR[EC]", 26, 0, 2, 66, NULL, }, + { "AR[EFLAG]", 26, 0, 2, 24, NULL, }, + { "AR[FCR]", 26, 0, 2, 21, NULL, }, + { "AR[FDR]", 26, 0, 2, 30, NULL, }, + { "AR[FIR]", 26, 0, 2, 29, NULL, }, + { "AR[FPSR].sf0.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf1.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf2.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf3.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].traps", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].rv", 30, 0, 2, -1, NULL, }, + { "AR[FSR]", 26, 0, 2, 28, NULL, }, + { "AR[ITC]", 26, 0, 2, 44, NULL, }, + { "AR[K%], % in 0 - 7", 1, 0, 2, -1, NULL, }, + { "AR[LC]", 26, 0, 2, 65, NULL, }, + { "AR[PFS]", 26, 0, 2, 64, NULL, }, + { "AR[PFS]", 26, 0, 2, 64, NULL, }, + { "AR[PFS]", 26, 0, 0, 64, NULL, }, + { "AR[RNAT]", 26, 0, 2, 19, NULL, }, + { "AR[RSC]", 26, 0, 2, 16, NULL, }, + { "AR[SSD]", 26, 0, 2, 26, NULL, }, + { "AR[UNAT]{%}, % in 0 - 63", 2, 0, 2, -1, NULL, }, + { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 0, 0, -1, NULL, }, + { "AR%, % in 48-63, 112-127", 4, 0, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 0, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 0, -1, NULL, }, + { "CPUID#", 7, 0, 5, -1, NULL, }, + { "CR[CMCV]", 27, 0, 3, 74, NULL, }, + { "CR[DCR]", 27, 0, 3, 0, NULL, }, + { "CR[EOI]", 27, 0, 7, 67, "SC Section 5.8.3.4, \"End of External Interrupt Register (EOI Ð CR67)\" on page 2:119", }, + { "CR[GPTA]", 27, 0, 3, 9, NULL, }, + { "CR[IFA]", 27, 0, 1, 20, NULL, }, + { "CR[IFA]", 27, 0, 3, 20, NULL, }, + { "CR[IFS]", 27, 0, 3, 23, NULL, }, + { "CR[IFS]", 27, 0, 1, 23, NULL, }, + { "CR[IFS]", 27, 0, 1, 23, NULL, }, + { "CR[IHA]", 27, 0, 3, 25, NULL, }, + { "CR[IIM]", 27, 0, 3, 24, NULL, }, + { "CR[IIP]", 27, 0, 3, 19, NULL, }, + { "CR[IIP]", 27, 0, 1, 19, NULL, }, + { "CR[IIPA]", 27, 0, 3, 22, NULL, }, + { "CR[IPSR]", 27, 0, 3, 16, NULL, }, + { "CR[IPSR]", 27, 0, 1, 16, NULL, }, + { "CR[IRR%], % in 0 - 3", 8, 0, 3, -1, NULL, }, + { "CR[ISR]", 27, 0, 3, 17, NULL, }, + { "CR[ITIR]", 27, 0, 3, 21, NULL, }, + { "CR[ITIR]", 27, 0, 1, 21, NULL, }, + { "CR[ITM]", 27, 0, 3, 1, NULL, }, + { "CR[ITV]", 27, 0, 3, 72, NULL, }, + { "CR[IVA]", 27, 0, 4, 2, NULL, }, + { "CR[IVR]", 27, 0, 7, 65, "SC Section 5.8.3.2, \"External Interrupt Vector Register (IVR Ð CR65)\" on page 2:118", }, + { "CR[LID]", 27, 0, 7, 64, "SC Section 5.8.3.1, \"Local ID (LID Ð CR64)\" on page 2:117", }, + { "CR[LRR%], % in 0 - 1", 9, 0, 3, -1, NULL, }, + { "CR[PMV]", 27, 0, 3, 73, NULL, }, + { "CR[PTA]", 27, 0, 3, 8, NULL, }, + { "CR[TPR]", 27, 0, 3, 66, NULL, }, + { "CR[TPR]", 27, 0, 7, 66, "SC Section 5.8.3.3, \"Task Priority Register (TPR Ð CR66)\" on page 2:119", }, + { "CR[TPR]", 27, 0, 1, 66, NULL, }, + { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 0, 0, -1, NULL, }, + { "DBR#", 11, 0, 2, -1, NULL, }, + { "DBR#", 11, 0, 3, -1, NULL, }, + { "DTC", 0, 0, 3, -1, NULL, }, + { "DTC", 0, 0, 2, -1, NULL, }, + { "DTC", 0, 0, 0, -1, NULL, }, + { "DTC", 0, 0, 2, -1, NULL, }, + { "DTC_LIMIT*", 0, 0, 2, -1, NULL, }, + { "DTR", 0, 0, 3, -1, NULL, }, + { "DTR", 0, 0, 2, -1, NULL, }, + { "DTR", 0, 0, 3, -1, NULL, }, + { "DTR", 0, 0, 0, -1, NULL, }, + { "DTR", 0, 0, 2, -1, NULL, }, + { "FR%, % in 0 - 1", 12, 0, 0, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 0, 2, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 0, 0, -1, NULL, }, + { "GR0", 14, 0, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 0, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 0, 2, -1, NULL, }, + { "IBR#", 16, 0, 2, -1, NULL, }, + { "InService*", 17, 0, 3, -1, NULL, }, + { "InService*", 17, 0, 2, -1, NULL, }, + { "InService*", 17, 0, 2, -1, NULL, }, + { "IP", 0, 0, 0, -1, NULL, }, + { "ITC", 0, 0, 4, -1, NULL, }, + { "ITC", 0, 0, 2, -1, NULL, }, + { "ITC", 0, 0, 0, -1, NULL, }, + { "ITC", 0, 0, 4, -1, NULL, }, + { "ITC", 0, 0, 2, -1, NULL, }, + { "ITC_LIMIT*", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 4, -1, NULL, }, + { "ITR", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 0, -1, NULL, }, + { "ITR", 0, 0, 4, -1, NULL, }, + { "memory", 0, 0, 0, -1, NULL, }, + { "MSR#", 18, 0, 5, -1, NULL, }, + { "PKR#", 19, 0, 3, -1, NULL, }, + { "PKR#", 19, 0, 0, -1, NULL, }, + { "PKR#", 19, 0, 2, -1, NULL, }, + { "PKR#", 19, 0, 2, -1, NULL, }, + { "PMC#", 20, 0, 2, -1, NULL, }, + { "PMC#", 20, 0, 7, -1, "SC Section 7.2.1, \"Generic Performance Counter Registers\" for PMC[0].fr on page 2:150", }, + { "PMD#", 21, 0, 2, -1, NULL, }, + { "PR0", 0, 0, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 0, -1, NULL, }, + { "PR63", 24, 0, 2, -1, NULL, }, + { "PR63", 24, 0, 2, -1, NULL, }, + { "PR63", 24, 0, 0, -1, NULL, }, + { "PSR.ac", 28, 0, 1, 3, NULL, }, + { "PSR.ac", 28, 0, 3, 3, NULL, }, + { "PSR.ac", 28, 0, 2, 3, NULL, }, + { "PSR.ac", 28, 0, 2, 3, NULL, }, + { "PSR.be", 28, 0, 1, 1, NULL, }, + { "PSR.be", 28, 0, 3, 1, NULL, }, + { "PSR.be", 28, 0, 2, 1, NULL, }, + { "PSR.be", 28, 0, 2, 1, NULL, }, + { "PSR.bn", 28, 0, 2, 44, NULL, }, + { "PSR.cpl", 28, 0, 1, 32, NULL, }, + { "PSR.cpl", 28, 0, 2, 32, NULL, }, + { "PSR.da", 28, 0, 2, 38, NULL, }, + { "PSR.db", 28, 0, 3, 24, NULL, }, + { "PSR.db", 28, 0, 2, 24, NULL, }, + { "PSR.db", 28, 0, 2, 24, NULL, }, + { "PSR.dd", 28, 0, 2, 39, NULL, }, + { "PSR.dfh", 28, 0, 3, 19, NULL, }, + { "PSR.dfh", 28, 0, 2, 19, NULL, }, + { "PSR.dfh", 28, 0, 2, 19, NULL, }, + { "PSR.dfl", 28, 0, 3, 18, NULL, }, + { "PSR.dfl", 28, 0, 2, 18, NULL, }, + { "PSR.dfl", 28, 0, 2, 18, NULL, }, + { "PSR.di", 28, 0, 3, 22, NULL, }, + { "PSR.di", 28, 0, 2, 22, NULL, }, + { "PSR.di", 28, 0, 2, 22, NULL, }, + { "PSR.dt", 28, 0, 3, 17, NULL, }, + { "PSR.dt", 28, 0, 2, 17, NULL, }, + { "PSR.dt", 28, 0, 2, 17, NULL, }, + { "PSR.ed", 28, 0, 2, 43, NULL, }, + { "PSR.i", 28, 0, 2, 14, NULL, }, + { "PSR.ia", 28, 0, 0, 14, NULL, }, + { "PSR.ic", 28, 0, 2, 13, NULL, }, + { "PSR.ic", 28, 0, 3, 13, NULL, }, + { "PSR.ic", 28, 0, 2, 13, NULL, }, + { "PSR.id", 28, 0, 0, 14, NULL, }, + { "PSR.is", 28, 0, 0, 14, NULL, }, + { "PSR.it", 28, 0, 2, 14, NULL, }, + { "PSR.lp", 28, 0, 2, 25, NULL, }, + { "PSR.lp", 28, 0, 3, 25, NULL, }, + { "PSR.lp", 28, 0, 2, 25, NULL, }, + { "PSR.mc", 28, 0, 2, 35, NULL, }, + { "PSR.mfh", 28, 0, 2, 5, NULL, }, + { "PSR.mfl", 28, 0, 2, 4, NULL, }, + { "PSR.pk", 28, 0, 3, 15, NULL, }, + { "PSR.pk", 28, 0, 2, 15, NULL, }, + { "PSR.pk", 28, 0, 2, 15, NULL, }, + { "PSR.pp", 28, 0, 2, 21, NULL, }, + { "PSR.ri", 28, 0, 0, 41, NULL, }, + { "PSR.rt", 28, 0, 2, 27, NULL, }, + { "PSR.rt", 28, 0, 3, 27, NULL, }, + { "PSR.rt", 28, 0, 2, 27, NULL, }, + { "PSR.si", 28, 0, 2, 23, NULL, }, + { "PSR.si", 28, 0, 3, 23, NULL, }, + { "PSR.si", 28, 0, 2, 23, NULL, }, + { "PSR.sp", 28, 0, 2, 20, NULL, }, + { "PSR.sp", 28, 0, 3, 20, NULL, }, + { "PSR.sp", 28, 0, 2, 20, NULL, }, + { "PSR.ss", 28, 0, 2, 40, NULL, }, + { "PSR.tb", 28, 0, 3, 26, NULL, }, + { "PSR.tb", 28, 0, 2, 26, NULL, }, + { "PSR.tb", 28, 0, 2, 26, NULL, }, + { "PSR.up", 28, 0, 2, 2, NULL, }, + { "PSR.vm", 28, 0, 1, 46, NULL, }, + { "PSR.vm", 28, 0, 2, 46, NULL, }, + { "RR#", 25, 0, 3, -1, NULL, }, + { "RR#", 25, 0, 2, -1, NULL, }, + { "RSE", 29, 0, 2, -1, NULL, }, + { "ALAT", 0, 1, 0, -1, NULL, }, + { "AR[BSP]", 26, 1, 2, 17, NULL, }, + { "AR[BSPSTORE]", 26, 1, 2, 18, NULL, }, + { "AR[CCV]", 26, 1, 2, 32, NULL, }, + { "AR[CFLG]", 26, 1, 2, 27, NULL, }, + { "AR[CSD]", 26, 1, 2, 25, NULL, }, + { "AR[EC]", 26, 1, 2, 66, NULL, }, + { "AR[EFLAG]", 26, 1, 2, 24, NULL, }, + { "AR[FCR]", 26, 1, 2, 21, NULL, }, + { "AR[FDR]", 26, 1, 2, 30, NULL, }, + { "AR[FIR]", 26, 1, 2, 29, NULL, }, + { "AR[FPSR].sf0.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].rv", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].traps", 30, 1, 2, -1, NULL, }, + { "AR[FSR]", 26, 1, 2, 28, NULL, }, + { "AR[ITC]", 26, 1, 2, 44, NULL, }, + { "AR[K%], % in 0 - 7", 1, 1, 2, -1, NULL, }, + { "AR[LC]", 26, 1, 2, 65, NULL, }, + { "AR[PFS]", 26, 1, 0, 64, NULL, }, + { "AR[PFS]", 26, 1, 2, 64, NULL, }, + { "AR[PFS]", 26, 1, 2, 64, NULL, }, + { "AR[RNAT]", 26, 1, 2, 19, NULL, }, + { "AR[RSC]", 26, 1, 2, 16, NULL, }, + { "AR[SSD]", 26, 1, 2, 26, NULL, }, + { "AR[UNAT]{%}, % in 0 - 63", 2, 1, 2, -1, NULL, }, + { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 1, 0, -1, NULL, }, + { "AR%, % in 48 - 63, 112-127", 4, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 0, -1, NULL, }, + { "CFM", 6, 1, 2, -1, NULL, }, + { "CPUID#", 7, 1, 0, -1, NULL, }, + { "CR[CMCV]", 27, 1, 2, 74, NULL, }, + { "CR[DCR]", 27, 1, 2, 0, NULL, }, + { "CR[EOI]", 27, 1, 7, 67, "SC Section 5.8.3.4, \"End of External Interrupt Register (EOI Ð CR67)\" on page 2:119", }, + { "CR[GPTA]", 27, 1, 2, 9, NULL, }, + { "CR[IFA]", 27, 1, 2, 20, NULL, }, + { "CR[IFS]", 27, 1, 2, 23, NULL, }, + { "CR[IHA]", 27, 1, 2, 25, NULL, }, + { "CR[IIM]", 27, 1, 2, 24, NULL, }, + { "CR[IIP]", 27, 1, 2, 19, NULL, }, + { "CR[IIPA]", 27, 1, 2, 22, NULL, }, + { "CR[IPSR]", 27, 1, 2, 16, NULL, }, + { "CR[IRR%], % in 0 - 3", 8, 1, 2, -1, NULL, }, + { "CR[ISR]", 27, 1, 2, 17, NULL, }, + { "CR[ITIR]", 27, 1, 2, 21, NULL, }, + { "CR[ITM]", 27, 1, 2, 1, NULL, }, + { "CR[ITV]", 27, 1, 2, 72, NULL, }, + { "CR[IVA]", 27, 1, 2, 2, NULL, }, + { "CR[IVR]", 27, 1, 7, 65, "SC", }, + { "CR[LID]", 27, 1, 7, 64, "SC", }, + { "CR[LRR%], % in 0 - 1", 9, 1, 2, -1, NULL, }, + { "CR[PMV]", 27, 1, 2, 73, NULL, }, + { "CR[PTA]", 27, 1, 2, 8, NULL, }, + { "CR[TPR]", 27, 1, 2, 66, NULL, }, + { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 1, 0, -1, NULL, }, + { "DBR#", 11, 1, 2, -1, NULL, }, + { "DTC", 0, 1, 0, -1, NULL, }, + { "DTC", 0, 1, 2, -1, NULL, }, + { "DTC", 0, 1, 2, -1, NULL, }, + { "DTC_LIMIT*", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 0, -1, NULL, }, + { "FR%, % in 0 - 1", 12, 1, 0, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 1, 2, -1, NULL, }, + { "GR0", 14, 1, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 1, 2, -1, NULL, }, + { "IBR#", 16, 1, 2, -1, NULL, }, + { "InService*", 17, 1, 7, -1, "SC", }, + { "IP", 0, 1, 0, -1, NULL, }, + { "ITC", 0, 1, 0, -1, NULL, }, + { "ITC", 0, 1, 2, -1, NULL, }, + { "ITC", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 0, -1, NULL, }, + { "memory", 0, 1, 0, -1, NULL, }, + { "MSR#", 18, 1, 7, -1, "SC", }, + { "PKR#", 19, 1, 0, -1, NULL, }, + { "PKR#", 19, 1, 0, -1, NULL, }, + { "PKR#", 19, 1, 2, -1, NULL, }, + { "PMC#", 20, 1, 2, -1, NULL, }, + { "PMD#", 21, 1, 2, -1, NULL, }, + { "PR0", 0, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, }, + { "PR63", 24, 1, 0, -1, NULL, }, + { "PR63", 24, 1, 0, -1, NULL, }, + { "PR63", 24, 1, 2, -1, NULL, }, + { "PR63", 24, 1, 2, -1, NULL, }, + { "PSR.ac", 28, 1, 2, 3, NULL, }, + { "PSR.be", 28, 1, 2, 1, NULL, }, + { "PSR.bn", 28, 1, 2, 44, NULL, }, + { "PSR.cpl", 28, 1, 2, 32, NULL, }, + { "PSR.da", 28, 1, 2, 38, NULL, }, + { "PSR.db", 28, 1, 2, 24, NULL, }, + { "PSR.dd", 28, 1, 2, 39, NULL, }, + { "PSR.dfh", 28, 1, 2, 19, NULL, }, + { "PSR.dfl", 28, 1, 2, 18, NULL, }, + { "PSR.di", 28, 1, 2, 22, NULL, }, + { "PSR.dt", 28, 1, 2, 17, NULL, }, + { "PSR.ed", 28, 1, 2, 43, NULL, }, + { "PSR.i", 28, 1, 2, 14, NULL, }, + { "PSR.ia", 28, 1, 2, 14, NULL, }, + { "PSR.ic", 28, 1, 2, 13, NULL, }, + { "PSR.id", 28, 1, 2, 14, NULL, }, + { "PSR.is", 28, 1, 2, 14, NULL, }, + { "PSR.it", 28, 1, 2, 14, NULL, }, + { "PSR.lp", 28, 1, 2, 25, NULL, }, + { "PSR.mc", 28, 1, 2, 35, NULL, }, + { "PSR.mfh", 28, 1, 0, 5, NULL, }, + { "PSR.mfh", 28, 1, 2, 5, NULL, }, + { "PSR.mfh", 28, 1, 2, 5, NULL, }, + { "PSR.mfl", 28, 1, 0, 4, NULL, }, + { "PSR.mfl", 28, 1, 2, 4, NULL, }, + { "PSR.mfl", 28, 1, 2, 4, NULL, }, + { "PSR.pk", 28, 1, 2, 15, NULL, }, + { "PSR.pp", 28, 1, 2, 21, NULL, }, + { "PSR.ri", 28, 1, 2, 41, NULL, }, + { "PSR.rt", 28, 1, 2, 27, NULL, }, + { "PSR.si", 28, 1, 2, 23, NULL, }, + { "PSR.sp", 28, 1, 2, 20, NULL, }, + { "PSR.ss", 28, 1, 2, 40, NULL, }, + { "PSR.tb", 28, 1, 2, 26, NULL, }, + { "PSR.up", 28, 1, 2, 2, NULL, }, + { "PSR.vm", 28, 1, 2, 46, NULL, }, + { "RR#", 25, 1, 2, -1, NULL, }, + { "RSE", 29, 1, 2, -1, NULL, }, + { "PR63", 24, 2, 6, -1, NULL, }, +}; + +static const unsigned short dep0[] = { + 97, 282, 2140, 2327, +}; + +static const unsigned short dep1[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2327, 4135, 20616, +}; + +static const unsigned short dep2[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2347, 2348, 2351, + 2352, 2355, 2356, +}; + +static const unsigned short dep3[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 20616, +}; + +static const unsigned short dep4[] = { + 97, 282, 22646, 22647, 22649, 22650, 22652, 22653, 22655, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep5[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 4135, 20616, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep6[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2345, 2347, 2349, + 2351, 2353, 2355, +}; + +static const unsigned short dep7[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2345, 2348, 2349, 2352, 2353, 2356, 4135, 20616, +}; + +static const unsigned short dep8[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2346, 2348, 2350, + 2352, 2354, 2356, +}; + +static const unsigned short dep9[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2346, 2347, 2350, 2351, 2354, 2355, 4135, 20616, +}; + +static const unsigned short dep10[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2345, 2346, 2347, + 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, +}; + +static const unsigned short dep11[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, + 4135, 20616, +}; + +static const unsigned short dep12[] = { + 97, 282, 2395, +}; + +static const unsigned short dep13[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2082, 2083, 2166, 2168, + 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep14[] = { + 97, 163, 282, 325, 2395, 28866, 29018, +}; + +static const unsigned short dep15[] = { + 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, 28, 29, 30, 31, 32, 33, 40, 41, 97, 150, 152, 158, 162, + 164, 175, 185, 186, 188, 282, 325, 2082, 2083, 2166, 2168, 2169, 2171, 2172, + 2174, 2175, 4135, 28866, 29018, +}; + +static const unsigned short dep16[] = { + 1, 6, 40, 97, 137, 196, 201, 241, 282, 312, 2395, 28866, 29018, +}; + +static const unsigned short dep17[] = { + 1, 25, 27, 38, 40, 41, 97, 158, 162, 164, 166, 167, 175, 185, 186, 188, 196, + 201, 241, 282, 312, 2082, 2083, 2166, 2168, 2169, 2171, 2172, 2174, 2175, + 4135, 28866, 29018, +}; + +static const unsigned short dep18[] = { + 1, 40, 51, 97, 196, 241, 248, 282, 28866, 29018, +}; + +static const unsigned short dep19[] = { + 1, 38, 40, 41, 97, 158, 160, 161, 162, 175, 185, 190, 191, 196, 241, 248, + 282, 4135, 28866, 29018, +}; + +static const unsigned short dep20[] = { + 40, 97, 241, 282, +}; + +static const unsigned short dep21[] = { + 97, 158, 162, 175, 185, 241, 282, +}; + +static const unsigned short dep22[] = { + 1, 40, 97, 131, 135, 136, 138, 139, 142, 143, 146, 149, 152, 155, 156, 157, + 158, 161, 162, 163, 164, 167, 168, 169, 170, 173, 174, 175, 178, 181, 184, + 185, 188, 189, 191, 196, 241, 282, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 330, 331, 333, + 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 28866, 29018, +}; + +static const unsigned short dep23[] = { + 1, 38, 40, 41, 50, 51, 55, 58, 73, 97, 137, 138, 158, 162, 175, 185, 190, + 191, 196, 241, 282, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, 327, 328, 330, 331, 333, 334, 335, 336, + 337, 338, 339, 340, 341, 342, 343, 344, 4135, 28866, 29018, +}; + +static const unsigned short dep24[] = { + 97, 136, 282, 311, +}; + +static const unsigned short dep25[] = { + 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 311, +}; + +static const unsigned short dep26[] = { + 97, 137, 282, 312, +}; + +static const unsigned short dep27[] = { + 25, 26, 97, 98, 101, 105, 108, 137, 138, 158, 162, 164, 175, 185, 282, 312, + +}; + +static const unsigned short dep28[] = { + 97, 190, 282, 344, +}; + +static const unsigned short dep29[] = { + 97, 98, 101, 105, 108, 137, 138, 158, 162, 164, 175, 185, 282, 344, +}; + +static const unsigned short dep30[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2168, 2169, 2171, 2172, 2174, 2175, + 4135, +}; + +static const unsigned short dep31[] = { + 1, 25, 40, 97, 196, 228, 229, 241, 282, 2082, 2285, 2288, 2395, 28866, 29018, + +}; + +static const unsigned short dep32[] = { + 1, 6, 38, 40, 41, 97, 137, 138, 158, 162, 164, 175, 185, 186, 188, 196, 228, + 230, 241, 282, 2082, 2083, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 2286, + 2288, 4135, 28866, 29018, +}; + +static const unsigned short dep33[] = { + 97, 282, +}; + +static const unsigned short dep34[] = { + 97, 158, 162, 175, 185, 282, 2082, 2084, +}; + +static const unsigned short dep35[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2168, 2169, 2171, + 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep36[] = { + 6, 37, 38, 39, 97, 125, 126, 201, 241, 282, 307, 308, 2395, +}; + +static const unsigned short dep37[] = { + 6, 37, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 201, 241, 282, 307, + 308, 347, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep38[] = { + 24, 97, 227, 282, 2395, +}; + +static const unsigned short dep39[] = { + 24, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 227, 282, 2166, 2168, 2169, + 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep40[] = { + 6, 24, 37, 38, 39, 97, 125, 126, 201, 227, 241, 282, 307, 308, 2395, +}; + +static const unsigned short dep41[] = { + 6, 24, 37, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 201, 227, 241, 282, + 307, 308, 347, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep42[] = { + 1, 6, 38, 40, 41, 97, 137, 138, 158, 162, 164, 175, 185, 186, 188, 196, 228, + 230, 241, 282, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 2286, 2288, 4135, + 28866, 29018, +}; + +static const unsigned short dep43[] = { + 97, 158, 162, 175, 185, 282, +}; + +static const unsigned short dep44[] = { + 15, 97, 210, 211, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep45[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep46[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2136, 2325, + 18601, 18602, 18761, 18762, 18764, 18765, 22646, 22647, 22648, 22650, 22651, + 22653, 22654, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep47[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep48[] = { + 16, 97, 213, 214, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep49[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep50[] = { + 17, 97, 216, 217, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep51[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep52[] = { + 18, 97, 219, 220, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep53[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep54[] = { + 15, 97, 210, 211, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep55[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep56[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2136, 2325, + 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep57[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, +}; + +static const unsigned short dep58[] = { + 16, 97, 213, 214, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep59[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep60[] = { + 17, 97, 216, 217, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep61[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep62[] = { + 18, 97, 219, 220, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep63[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep64[] = { + 97, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep65[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, +}; + +static const unsigned short dep66[] = { + 11, 97, 206, 282, +}; + +static const unsigned short dep67[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 206, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep68[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep69[] = { + 12, 97, 207, 282, +}; + +static const unsigned short dep70[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 207, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep71[] = { + 13, 97, 208, 282, +}; + +static const unsigned short dep72[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 208, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep73[] = { + 14, 97, 209, 282, +}; + +static const unsigned short dep74[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 209, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep75[] = { + 15, 97, 211, 212, 282, +}; + +static const unsigned short dep76[] = { + 40, 41, 97, 158, 162, 175, 185, 211, 212, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep77[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep78[] = { + 16, 97, 214, 215, 282, +}; + +static const unsigned short dep79[] = { + 40, 41, 97, 158, 162, 175, 185, 214, 215, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep80[] = { + 17, 97, 217, 218, 282, +}; + +static const unsigned short dep81[] = { + 40, 41, 97, 158, 162, 175, 185, 217, 218, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep82[] = { + 18, 97, 220, 221, 282, +}; + +static const unsigned short dep83[] = { + 40, 41, 97, 158, 162, 175, 185, 220, 221, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep84[] = { + 15, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2167, + 2170, 2173, 4135, +}; + +static const unsigned short dep85[] = { + 15, 16, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep86[] = { + 15, 17, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep87[] = { + 15, 18, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep88[] = { + 15, 97, 210, 211, 282, +}; + +static const unsigned short dep89[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep90[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, +}; + +static const unsigned short dep91[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep92[] = { + 16, 97, 213, 214, 282, +}; + +static const unsigned short dep93[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep94[] = { + 17, 97, 216, 217, 282, +}; + +static const unsigned short dep95[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep96[] = { + 18, 97, 219, 220, 282, +}; + +static const unsigned short dep97[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep98[] = { + 15, 97, 210, 211, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep99[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep100[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2166, 2167, + 2168, 2170, 2171, 2173, 2174, 2344, 2347, 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep101[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, 4135, 16528, 16530, 16531, 16533, +}; + +static const unsigned short dep102[] = { + 16, 97, 213, 214, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep103[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep104[] = { + 17, 97, 216, 217, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep105[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep106[] = { + 18, 97, 219, 220, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep107[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep108[] = { + 15, 97, 210, 211, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep109[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep110[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 22646, 22647, + 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, 22832, 22835, + 22836, +}; + +static const unsigned short dep111[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 4135, 16528, + 16530, 16531, 16533, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep112[] = { + 16, 97, 213, 214, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep113[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep114[] = { + 17, 97, 216, 217, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep115[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep116[] = { + 18, 97, 219, 220, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep117[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep118[] = { + 97, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, 2348, 2351, + 2352, 2355, 2356, +}; + +static const unsigned short dep119[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, 16530, 16531, 16533, + +}; + +static const unsigned short dep120[] = { + 97, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep121[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, 22831, 22832, 22835, + 22836, +}; + +static const unsigned short dep122[] = { + 19, 20, 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, + 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, + +}; + +static const unsigned short dep123[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep124[] = { + 97, 282, 2083, 2084, 2286, 2287, +}; + +static const unsigned short dep125[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2285, 2287, 4135, 20616, +}; + +static const unsigned short dep126[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2082, 2084, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep127[] = { + 97, 282, 14455, 14457, 14458, 14460, 14461, 14463, 14635, 14636, 14639, 14640, + 14643, 14644, +}; + +static const unsigned short dep128[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 4135, 14635, 14636, + 14639, 14640, 14643, 14644, 20616, 24694, 24695, 24698, 24701, +}; + +static const unsigned short dep129[] = { + 97, 122, 124, 125, 127, 282, 303, 304, 307, 308, +}; + +static const unsigned short dep130[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 303, 304, 307, 308, 4135, 24694, 24695, + 24698, 24701, +}; + +static const unsigned short dep131[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep132[] = { + 40, 41, 97, 119, 122, 125, 158, 162, 175, 185, 282, 2327, 4135, 20616, 24694, + +}; + +static const unsigned short dep133[] = { + 6, 24, 26, 27, 97, 201, 227, 230, 282, 2081, 2284, +}; + +static const unsigned short dep134[] = { + 40, 41, 97, 158, 162, 175, 185, 201, 227, 229, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2284, 4135, 20616, +}; + +static const unsigned short dep135[] = { + 6, 24, 25, 26, 40, 41, 97, 158, 162, 175, 185, 282, 2081, 2166, 2167, 2170, + 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep136[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2347, 2348, + 2351, 2352, 2355, 2356, 4135, +}; + +static const unsigned short dep137[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep138[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2345, 2348, + 2349, 2352, 2353, 2356, 4135, +}; + +static const unsigned short dep139[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2346, 2347, + 2350, 2351, 2354, 2355, 4135, +}; + +static const unsigned short dep140[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2345, 2346, + 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 4135, +}; + +static const unsigned short dep141[] = { + 0, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2167, 2170, 2173, + 4135, +}; + +static const unsigned short dep142[] = { + 0, 97, 195, 282, +}; + +static const unsigned short dep143[] = { + 0, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 195, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep144[] = { + 40, 41, 97, 158, 162, 175, 185, 195, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep145[] = { + 2, 28, 97, 197, 231, 282, 28866, 29018, +}; + +static const unsigned short dep146[] = { + 1, 2, 28, 29, 97, 158, 162, 175, 177, 178, 185, 197, 231, 282, 28866, 29018, + +}; + +static const unsigned short dep147[] = { + 1, 28, 29, 38, 40, 41, 97, 158, 162, 175, 177, 178, 185, 197, 231, 282, 4135, + 28866, 29018, +}; + +static const unsigned short dep148[] = { + 0, 40, 41, 97, 158, 162, 175, 185, 195, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep149[] = { + 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 28, 29, 30, 31, 97, 196, 197, 198, 199, 200, 202, 203, 204, 205, 206, 207, + 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, 223, 224, 225, 231, + 232, 233, 234, 282, 2071, 2081, 2274, 2284, 28866, 29018, +}; + +static const unsigned short dep150[] = { + 29, 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 196, 197, 198, 199, + 200, 202, 203, 204, 205, 206, 207, 208, 209, 211, 212, 214, 215, 217, 218, + 220, 221, 222, 223, 224, 225, 231, 232, 233, 234, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2274, 2284, 4135, 20616, 28866, 29018, +}; + +static const unsigned short dep151[] = { + 97, 282, 14464, 14466, 14468, 14470, 14505, 14506, 14525, 14645, 14646, 14666, + 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep152[] = { + 40, 41, 97, 158, 162, 175, 183, 184, 185, 282, 2166, 2167, 2170, 2173, 4135, + 14645, 14646, 14666, 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep153[] = { + 14464, 14466, 14468, 14470, 14505, 14506, 14525, 14645, 14646, 14666, 14667, + 14669, 14670, 14679, +}; + +static const unsigned short dep154[] = { + 183, 184, 14645, 14646, 14666, 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep155[] = { + 97, 282, 14465, 14466, 14469, 14470, 14480, 14481, 14483, 14484, 14486, 14487, + 14489, 14490, 14493, 14495, 14496, 14505, 14506, 14507, 14508, 14510, 14515, + 14516, 14518, 14519, 14525, 14645, 14646, 14652, 14653, 14654, 14655, 14657, + 14659, 14666, 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, +}; + +static const unsigned short dep156[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2166, 2167, 2170, + 2173, 4135, 14645, 14646, 14652, 14653, 14654, 14655, 14657, 14659, 14666, + 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, 34888, +}; + +static const unsigned short dep157[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2166, 2167, 2170, + 2173, 4135, 14645, 14646, 14652, 14653, 14654, 14655, 14657, 14659, 14666, + 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, +}; + +static const unsigned short dep158[] = { + 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 28, 29, 30, 31, 40, 41, 97, 137, 138, 158, 162, 175, 180, 181, 185, 190, 191, + 282, 2071, 2081, 2166, 2167, 2170, 2173, 2327, 4135, 20616, 28866, +}; + +static const unsigned short dep159[] = { + 43, 44, 45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63, + 64, 65, 67, 69, 70, 71, 72, 73, 94, 96, 97, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 261, 263, 264, 265, 281, + 282, 2116, 2310, +}; + +static const unsigned short dep160[] = { + 40, 41, 96, 97, 137, 138, 158, 160, 161, 162, 175, 185, 190, 191, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 261, + 263, 264, 265, 281, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2310, 4135, + 20616, +}; + +static const unsigned short dep161[] = { + 59, 95, 97, 254, 281, 282, 2140, 2327, +}; + +static const unsigned short dep162[] = { + 40, 41, 43, 44, 46, 48, 49, 51, 52, 53, 54, 56, 57, 60, 61, 63, 64, 65, 66, + 67, 69, 70, 71, 94, 95, 97, 137, 138, 158, 160, 161, 162, 175, 185, 190, 191, + 254, 281, 282, 2107, 2116, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep163[] = { + 2, 28, 41, 97, 197, 231, 241, 282, 2140, 2327, 28866, 29018, +}; + +static const unsigned short dep164[] = { + 2, 25, 26, 28, 29, 38, 40, 41, 97, 158, 162, 175, 177, 178, 185, 197, 231, + 241, 282, 2327, 4135, 20616, 28866, 29018, +}; + +static const unsigned short dep165[] = { + 97, 129, 130, 133, 134, 140, 141, 144, 145, 147, 148, 150, 151, 153, 154, + 157, 159, 160, 165, 166, 169, 170, 171, 172, 174, 176, 177, 179, 180, 182, + 183, 186, 187, 189, 282, 309, 310, 314, 316, 317, 318, 319, 321, 323, 327, + 330, 331, 333, 334, 335, 336, 338, 339, 340, 342, 343, +}; + +static const unsigned short dep166[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 309, 310, 314, 316, + 317, 318, 319, 321, 323, 327, 330, 331, 333, 334, 335, 336, 338, 339, 340, + 342, 343, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, 34888, +}; + +static const unsigned short dep167[] = { + 97, 128, 130, 132, 134, 169, 170, 189, 282, 309, 310, 330, 331, 333, 334, + 343, +}; + +static const unsigned short dep168[] = { + 40, 41, 97, 158, 162, 175, 183, 184, 185, 282, 309, 310, 330, 331, 333, 334, + 343, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep169[] = { + 40, 41, 97, 130, 131, 134, 135, 137, 138, 141, 142, 145, 146, 148, 149, 151, + 152, 154, 155, 157, 158, 159, 161, 162, 164, 165, 167, 168, 169, 170, 172, + 173, 174, 175, 176, 178, 179, 181, 182, 184, 185, 187, 188, 189, 190, 191, + 282, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep170[] = { + 40, 41, 97, 130, 131, 134, 135, 158, 162, 169, 170, 175, 185, 189, 282, 2166, + 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep171[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 111, 137, 138, 153, 155, 158, 162, 171, 173, + 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep172[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 111, 137, 138, 139, 140, 142, 143, 153, 155, + 158, 162, 171, 173, 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep173[] = { + 77, 78, 97, 101, 102, 269, 270, 282, 284, 285, +}; + +static const unsigned short dep174[] = { + 40, 41, 47, 62, 78, 80, 86, 97, 99, 102, 137, 138, 158, 160, 161, 162, 175, + 185, 190, 191, 192, 269, 270, 282, 284, 285, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep175[] = { + 40, 41, 47, 62, 78, 80, 97, 99, 102, 104, 106, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 282, 284, 285, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep176[] = { + 97, 282, 12480, 12481, 12633, +}; + +static const unsigned short dep177[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 12633, 20616, +}; + +static const unsigned short dep178[] = { + 97, 282, 6219, 6220, 6411, +}; + +static const unsigned short dep179[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6411, 20616, +}; + +static const unsigned short dep180[] = { + 97, 282, 6237, 6424, +}; + +static const unsigned short dep181[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6424, 20616, +}; + +static const unsigned short dep182[] = { + 97, 282, 6255, 6256, 6257, 6258, 6435, 6437, 8484, +}; + +static const unsigned short dep183[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6258, 6436, 6437, 8304, 8483, 20616, +}; + +static const unsigned short dep184[] = { + 97, 282, 6259, 6260, 6438, +}; + +static const unsigned short dep185[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6438, 20616, +}; + +static const unsigned short dep186[] = { + 97, 282, 6261, 6439, +}; + +static const unsigned short dep187[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6439, 20616, +}; + +static const unsigned short dep188[] = { + 97, 282, 10350, 10530, +}; + +static const unsigned short dep189[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 10530, 20616, +}; + +static const unsigned short dep190[] = { + 77, 78, 82, 83, 97, 101, 102, 269, 270, 272, 273, 282, 284, 285, +}; + +static const unsigned short dep191[] = { + 40, 41, 47, 62, 78, 80, 83, 86, 97, 99, 102, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 272, 274, 282, 284, 285, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep192[] = { + 77, 78, 97, 101, 102, 104, 105, 269, 270, 282, 284, 285, 286, 287, +}; + +static const unsigned short dep193[] = { + 40, 41, 47, 62, 78, 80, 97, 99, 102, 104, 106, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 282, 284, 285, 286, 287, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep194[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 12481, 20616, +}; + +static const unsigned short dep195[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6219, 20616, +}; + +static const unsigned short dep196[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6237, 20616, +}; + +static const unsigned short dep197[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6257, 8303, 20616, +}; + +static const unsigned short dep198[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6259, 20616, +}; + +static const unsigned short dep199[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 183, 184, 185, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6260, 6261, 20616, +}; + +static const unsigned short dep200[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2327, 4135, 10350, 20616, +}; + +static const unsigned short dep201[] = { + 40, 41, 97, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 2327, 4135, 6186, 20616, +}; + +static const unsigned short dep202[] = { + 77, 79, 80, 97, 98, 99, 100, 268, 269, 282, 283, 284, +}; + +static const unsigned short dep203[] = { + 40, 41, 78, 79, 83, 85, 97, 100, 102, 104, 107, 137, 138, 158, 162, 175, 185, + 190, 191, 192, 268, 270, 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep204[] = { + 77, 79, 80, 81, 97, 98, 99, 100, 103, 268, 269, 271, 282, 283, 284, +}; + +static const unsigned short dep205[] = { + 40, 41, 78, 79, 81, 83, 85, 97, 100, 102, 103, 104, 107, 137, 138, 158, 162, + 175, 185, 190, 191, 192, 268, 270, 271, 282, 283, 285, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep206[] = { + 77, 79, 80, 84, 85, 86, 97, 98, 99, 100, 268, 269, 274, 275, 282, 283, 284, + +}; + +static const unsigned short dep207[] = { + 40, 41, 78, 79, 83, 85, 97, 100, 102, 137, 138, 158, 162, 175, 185, 190, 191, + 192, 268, 270, 273, 275, 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep208[] = { + 77, 79, 80, 97, 98, 99, 100, 106, 107, 108, 268, 269, 282, 283, 284, 287, + 288, +}; + +static const unsigned short dep209[] = { + 40, 41, 78, 79, 97, 100, 102, 104, 107, 137, 138, 158, 162, 175, 185, 190, + 191, 192, 268, 270, 282, 283, 285, 286, 288, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep210[] = { + 40, 41, 46, 70, 97, 158, 162, 175, 185, 190, 191, 192, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep211[] = { + 40, 41, 97, 158, 162, 175, 185, 190, 191, 192, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep212[] = { + 40, 41, 70, 77, 82, 84, 97, 137, 138, 153, 155, 158, 162, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep213[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2135, 2136, 2137, 2138, + 2139, 2140, 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 20616, + +}; + +static const unsigned short dep214[] = { + 40, 41, 70, 77, 82, 84, 97, 153, 155, 158, 162, 175, 185, 192, 282, 2138, + 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep215[] = { + 40, 41, 78, 79, 97, 100, 137, 138, 158, 162, 175, 185, 190, 191, 268, 270, + 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep216[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep217[] = { + 5, 97, 200, 282, 2140, 2327, +}; + +static const unsigned short dep218[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 200, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep219[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep220[] = { + 0, 97, 195, 282, 2140, 2327, +}; + +static const unsigned short dep221[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep222[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep223[] = { + 31, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep224[] = { + 0, 97, 195, 282, 2327, 26715, +}; + +static const unsigned short dep225[] = { + 0, 97, 109, 195, 282, 289, +}; + +static const unsigned short dep226[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep227[] = { + 0, 5, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep228[] = { + 0, 31, 97, 109, 195, 234, 282, 289, +}; + +static const unsigned short dep229[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 234, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep230[] = { + 0, 97, 109, 195, 282, 289, 2140, 2327, +}; + +static const unsigned short dep231[] = { + 0, 3, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep232[] = { + 0, 3, 5, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep233[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep234[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2327, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep235[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep236[] = { + 0, 31, 97, 109, 195, 234, 282, 289, 2140, 2327, +}; + +static const unsigned short dep237[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 234, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep238[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, + 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep239[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep240[] = { + 0, 97, 195, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep241[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep242[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep243[] = { + 0, 97, 195, 282, 2137, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep244[] = { + 97, 282, 2136, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep245[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, 4135, 16528, + 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep246[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep247[] = { + 0, 97, 195, 282, 2136, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, + 18765, +}; + +static const unsigned short dep248[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep249[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, + 2327, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, + +}; + +static const unsigned short dep250[] = { + 0, 97, 195, 282, 2137, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, + 18765, +}; + +static const unsigned short dep251[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2135, 2136, 2137, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep252[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 137, 138, 139, 140, 142, 143, 153, 155, 156, + 158, 162, 171, 173, 175, 185, 192, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep253[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 137, 138, 139, 140, 142, 143, 153, 155, 156, + 158, 162, 171, 173, 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep254[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, + +}; + +static const unsigned short dep255[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2135, 2136, 2137, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 2327, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep256[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 24, 26, 27, 28, 29, 30, 31, 97, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, + 223, 224, 225, 227, 230, 231, 232, 233, 234, 282, 2071, 2081, 2140, 2274, + 2284, 2327, 28866, 29018, +}; + +static const unsigned short dep257[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 24, 25, 26, 28, 29, 30, 31, 40, 41, 97, 137, 138, 158, 162, 175, 180, + 181, 185, 190, 191, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, 223, 224, 225, + 227, 229, 231, 232, 233, 234, 282, 2071, 2081, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 2274, 2284, 2327, 4135, 20616, 28866, 29018, +}; + +#define NELS(X) (sizeof(X)/sizeof(X[0])) +static const struct ia64_opcode_dependency +op_dependencies[] = { + { NELS(dep1), dep1, NELS(dep0), dep0, }, + { NELS(dep3), dep3, NELS(dep2), dep2, }, + { NELS(dep5), dep5, NELS(dep4), dep4, }, + { NELS(dep7), dep7, NELS(dep6), dep6, }, + { NELS(dep9), dep9, NELS(dep8), dep8, }, + { NELS(dep11), dep11, NELS(dep10), dep10, }, + { NELS(dep13), dep13, NELS(dep12), dep12, }, + { NELS(dep15), dep15, NELS(dep14), dep14, }, + { NELS(dep17), dep17, NELS(dep16), dep16, }, + { NELS(dep19), dep19, NELS(dep18), dep18, }, + { NELS(dep21), dep21, NELS(dep20), dep20, }, + { NELS(dep23), dep23, NELS(dep22), dep22, }, + { NELS(dep25), dep25, NELS(dep24), dep24, }, + { NELS(dep27), dep27, NELS(dep26), dep26, }, + { NELS(dep29), dep29, NELS(dep28), dep28, }, + { NELS(dep30), dep30, NELS(dep12), dep12, }, + { NELS(dep32), dep32, NELS(dep31), dep31, }, + { NELS(dep34), dep34, NELS(dep33), dep33, }, + { NELS(dep35), dep35, NELS(dep12), dep12, }, + { NELS(dep37), dep37, NELS(dep36), dep36, }, + { NELS(dep39), dep39, NELS(dep38), dep38, }, + { NELS(dep41), dep41, NELS(dep40), dep40, }, + { NELS(dep42), dep42, NELS(dep31), dep31, }, + { NELS(dep43), dep43, NELS(dep33), dep33, }, + { NELS(dep45), dep45, NELS(dep44), dep44, }, + { NELS(dep47), dep47, NELS(dep46), dep46, }, + { NELS(dep49), dep49, NELS(dep48), dep48, }, + { NELS(dep51), dep51, NELS(dep50), dep50, }, + { NELS(dep53), dep53, NELS(dep52), dep52, }, + { NELS(dep55), dep55, NELS(dep54), dep54, }, + { NELS(dep57), dep57, NELS(dep56), dep56, }, + { NELS(dep59), dep59, NELS(dep58), dep58, }, + { NELS(dep61), dep61, NELS(dep60), dep60, }, + { NELS(dep63), dep63, NELS(dep62), dep62, }, + { NELS(dep65), dep65, NELS(dep64), dep64, }, + { NELS(dep67), dep67, NELS(dep66), dep66, }, + { NELS(dep68), dep68, NELS(dep33), dep33, }, + { NELS(dep70), dep70, NELS(dep69), dep69, }, + { NELS(dep72), dep72, NELS(dep71), dep71, }, + { NELS(dep74), dep74, NELS(dep73), dep73, }, + { NELS(dep76), dep76, NELS(dep75), dep75, }, + { NELS(dep77), dep77, NELS(dep33), dep33, }, + { NELS(dep79), dep79, NELS(dep78), dep78, }, + { NELS(dep81), dep81, NELS(dep80), dep80, }, + { NELS(dep83), dep83, NELS(dep82), dep82, }, + { NELS(dep84), dep84, NELS(dep33), dep33, }, + { NELS(dep85), dep85, NELS(dep33), dep33, }, + { NELS(dep86), dep86, NELS(dep33), dep33, }, + { NELS(dep87), dep87, NELS(dep33), dep33, }, + { NELS(dep89), dep89, NELS(dep88), dep88, }, + { NELS(dep91), dep91, NELS(dep90), dep90, }, + { NELS(dep93), dep93, NELS(dep92), dep92, }, + { NELS(dep95), dep95, NELS(dep94), dep94, }, + { NELS(dep97), dep97, NELS(dep96), dep96, }, + { NELS(dep99), dep99, NELS(dep98), dep98, }, + { NELS(dep101), dep101, NELS(dep100), dep100, }, + { NELS(dep103), dep103, NELS(dep102), dep102, }, + { NELS(dep105), dep105, NELS(dep104), dep104, }, + { NELS(dep107), dep107, NELS(dep106), dep106, }, + { NELS(dep109), dep109, NELS(dep108), dep108, }, + { NELS(dep111), dep111, NELS(dep110), dep110, }, + { NELS(dep113), dep113, NELS(dep112), dep112, }, + { NELS(dep115), dep115, NELS(dep114), dep114, }, + { NELS(dep117), dep117, NELS(dep116), dep116, }, + { NELS(dep119), dep119, NELS(dep118), dep118, }, + { NELS(dep121), dep121, NELS(dep120), dep120, }, + { NELS(dep122), dep122, NELS(dep64), dep64, }, + { NELS(dep123), dep123, NELS(dep33), dep33, }, + { NELS(dep125), dep125, NELS(dep124), dep124, }, + { NELS(dep126), dep126, NELS(dep0), dep0, }, + { NELS(dep128), dep128, NELS(dep127), dep127, }, + { NELS(dep130), dep130, NELS(dep129), dep129, }, + { NELS(dep131), dep131, NELS(dep0), dep0, }, + { NELS(dep132), dep132, NELS(dep0), dep0, }, + { NELS(dep134), dep134, NELS(dep133), dep133, }, + { NELS(dep135), dep135, NELS(dep0), dep0, }, + { NELS(dep136), dep136, NELS(dep2), dep2, }, + { NELS(dep137), dep137, NELS(dep4), dep4, }, + { NELS(dep138), dep138, NELS(dep6), dep6, }, + { NELS(dep139), dep139, NELS(dep8), dep8, }, + { NELS(dep140), dep140, NELS(dep10), dep10, }, + { NELS(dep141), dep141, NELS(dep33), dep33, }, + { NELS(dep143), dep143, NELS(dep142), dep142, }, + { NELS(dep144), dep144, NELS(dep142), dep142, }, + { NELS(dep146), dep146, NELS(dep145), dep145, }, + { NELS(dep147), dep147, NELS(dep145), dep145, }, + { NELS(dep148), dep148, NELS(dep142), dep142, }, + { NELS(dep150), dep150, NELS(dep149), dep149, }, + { NELS(dep152), dep152, NELS(dep151), dep151, }, + { NELS(dep154), dep154, NELS(dep153), dep153, }, + { NELS(dep156), dep156, NELS(dep155), dep155, }, + { NELS(dep157), dep157, NELS(dep155), dep155, }, + { NELS(dep158), dep158, NELS(dep0), dep0, }, + { NELS(dep160), dep160, NELS(dep159), dep159, }, + { NELS(dep162), dep162, NELS(dep161), dep161, }, + { NELS(dep164), dep164, NELS(dep163), dep163, }, + { NELS(dep166), dep166, NELS(dep165), dep165, }, + { NELS(dep168), dep168, NELS(dep167), dep167, }, + { NELS(dep169), dep169, NELS(dep0), dep0, }, + { NELS(dep170), dep170, NELS(dep0), dep0, }, + { NELS(dep171), dep171, NELS(dep0), dep0, }, + { NELS(dep172), dep172, NELS(dep33), dep33, }, + { NELS(dep174), dep174, NELS(dep173), dep173, }, + { NELS(dep175), dep175, NELS(dep173), dep173, }, + { NELS(dep177), dep177, NELS(dep176), dep176, }, + { NELS(dep179), dep179, NELS(dep178), dep178, }, + { NELS(dep181), dep181, NELS(dep180), dep180, }, + { NELS(dep183), dep183, NELS(dep182), dep182, }, + { NELS(dep185), dep185, NELS(dep184), dep184, }, + { NELS(dep187), dep187, NELS(dep186), dep186, }, + { NELS(dep189), dep189, NELS(dep188), dep188, }, + { NELS(dep191), dep191, NELS(dep190), dep190, }, + { NELS(dep193), dep193, NELS(dep192), dep192, }, + { NELS(dep194), dep194, NELS(dep0), dep0, }, + { NELS(dep195), dep195, NELS(dep0), dep0, }, + { NELS(dep196), dep196, NELS(dep0), dep0, }, + { NELS(dep197), dep197, NELS(dep0), dep0, }, + { NELS(dep198), dep198, NELS(dep0), dep0, }, + { NELS(dep199), dep199, NELS(dep0), dep0, }, + { NELS(dep200), dep200, NELS(dep0), dep0, }, + { NELS(dep201), dep201, NELS(dep0), dep0, }, + { NELS(dep203), dep203, NELS(dep202), dep202, }, + { NELS(dep205), dep205, NELS(dep204), dep204, }, + { NELS(dep207), dep207, NELS(dep206), dep206, }, + { NELS(dep209), dep209, NELS(dep208), dep208, }, + { NELS(dep210), dep210, NELS(dep0), dep0, }, + { NELS(dep211), dep211, NELS(dep0), dep0, }, + { NELS(dep212), dep212, NELS(dep0), dep0, }, + { NELS(dep213), dep213, NELS(dep33), dep33, }, + { NELS(dep214), dep214, NELS(dep33), dep33, }, + { NELS(dep215), dep215, NELS(dep202), dep202, }, + { NELS(dep216), dep216, NELS(dep0), dep0, }, + { NELS(dep218), dep218, NELS(dep217), dep217, }, + { NELS(dep219), dep219, NELS(dep0), dep0, }, + { NELS(dep221), dep221, NELS(dep220), dep220, }, + { NELS(dep222), dep222, NELS(dep220), dep220, }, + { NELS(dep223), dep223, NELS(dep0), dep0, }, + { NELS(dep221), dep221, NELS(dep224), dep224, }, + { NELS(dep226), dep226, NELS(dep225), dep225, }, + { NELS(dep227), dep227, NELS(dep225), dep225, }, + { NELS(dep229), dep229, NELS(dep228), dep228, }, + { NELS(dep231), dep231, NELS(dep230), dep230, }, + { NELS(dep232), dep232, NELS(dep230), dep230, }, + { NELS(dep233), dep233, NELS(dep230), dep230, }, + { NELS(dep234), dep234, NELS(dep0), dep0, }, + { NELS(dep235), dep235, NELS(dep230), dep230, }, + { NELS(dep237), dep237, NELS(dep236), dep236, }, + { NELS(dep238), dep238, NELS(dep64), dep64, }, + { NELS(dep239), dep239, NELS(dep64), dep64, }, + { NELS(dep241), dep241, NELS(dep240), dep240, }, + { NELS(dep242), dep242, NELS(dep240), dep240, }, + { NELS(dep241), dep241, NELS(dep243), dep243, }, + { NELS(dep245), dep245, NELS(dep244), dep244, }, + { NELS(dep246), dep246, NELS(dep244), dep244, }, + { NELS(dep248), dep248, NELS(dep247), dep247, }, + { NELS(dep249), dep249, NELS(dep247), dep247, }, + { NELS(dep248), dep248, NELS(dep250), dep250, }, + { NELS(dep251), dep251, NELS(dep225), dep225, }, + { NELS(dep252), dep252, NELS(dep33), dep33, }, + { NELS(dep253), dep253, NELS(dep0), dep0, }, + { NELS(dep254), dep254, NELS(dep64), dep64, }, + { NELS(dep255), dep255, NELS(dep230), dep230, }, + { 0, NULL, 0, NULL, }, + { NELS(dep257), dep257, NELS(dep256), dep256, }, +}; + +static const struct ia64_completer_table +completer_table[] = { + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 95 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 95 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 594, -1, 0, 1, 6 }, + { 0x0, 0x0, 0, 657, -1, 0, 1, 18 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, 756, -1, 0, 1, 18 }, + { 0x0, 0x0, 0, 2198, -1, 0, 1, 10 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 9 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 13 }, + { 0x1, 0x1, 0, -1, -1, 13, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 2406, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 1140, -1, 0, 1, 129 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 45 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 84 }, + { 0x0, 0x0, 0, 2246, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2473, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2250, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 2252, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2482, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2485, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2507, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2510, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 36 }, + { 0x0, 0x0, 0, 2518, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 1409, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 83 }, + { 0x0, 0x0, 0, 1457, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1466, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1475, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1477, -1, 0, 1, 132 }, + { 0x0, 0x0, 0, 1479, -1, 0, 1, 132 }, + { 0x0, 0x0, 0, 1488, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1497, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1506, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1515, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1524, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1533, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1543, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1553, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1563, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1572, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1578, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1584, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1590, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1596, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1602, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1608, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1614, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1620, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1626, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1632, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1638, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1644, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1650, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1656, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1662, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1668, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1674, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1678, -1, 0, 1, 158 }, + { 0x0, 0x0, 0, 1682, -1, 0, 1, 159 }, + { 0x0, 0x0, 0, 1686, -1, 0, 1, 159 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 85 }, + { 0x0, 0x0, 0, 258, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 68 }, + { 0x1, 0x1, 0, 1166, -1, 20, 1, 68 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 69 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 70 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 70 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 71 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 73 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 93 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 94 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 96 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 97 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 98 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 99 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 104 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 105 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 106 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 107 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 108 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 109 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 110 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 113 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 114 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 115 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 116 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 117 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 118 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 119 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 120 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2858, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2859, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2210, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2211, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2873, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2874, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2875, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2876, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2877, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2860, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2861, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 11 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 91 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 89 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x1, 0x1, 0, -1, -1, 13, 1, 0 }, + { 0x0, 0x0, 0, 2879, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 90 }, + { 0x0, 0x0, 0, 1966, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1968, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1970, -1, 0, 1, 139 }, + { 0x0, 0x0, 0, 1972, -1, 0, 1, 139 }, + { 0x0, 0x0, 0, 1974, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1976, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1978, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1980, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1983, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1986, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1989, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1990, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1991, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1992, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1993, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1994, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1995, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1996, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 88 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 127 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 125 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 127 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 126 }, + { 0x0, 0x0, 0, 1687, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1688, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1689, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1690, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 1, 224, -1, 0, 1, 12 }, + { 0x0, 0x0, 1, 225, -1, 0, 1, 14 }, + { 0x1, 0x1, 2, -1, -1, 27, 1, 12 }, + { 0x1, 0x1, 2, -1, -1, 27, 1, 14 }, + { 0x0, 0x0, 3, -1, 1340, 0, 0, -1 }, + { 0x0, 0x0, 3, -1, 1341, 0, 0, -1 }, + { 0x1, 0x1, 3, 2749, 1450, 33, 1, 134 }, + { 0x1, 0x1, 3, 2750, 1459, 33, 1, 134 }, + { 0x1, 0x1, 3, 2751, 1468, 33, 1, 134 }, + { 0x1, 0x1, 3, 2752, 1481, 33, 1, 134 }, + { 0x1, 0x1, 3, 2753, 1490, 33, 1, 134 }, + { 0x1, 0x1, 3, 2754, 1499, 33, 1, 134 }, + { 0x1, 0x1, 3, 2755, 1508, 33, 1, 134 }, + { 0x1, 0x1, 3, 2756, 1517, 33, 1, 134 }, + { 0x1, 0x1, 3, 2757, 1526, 33, 1, 134 }, + { 0x1, 0x1, 3, 2758, 1535, 33, 1, 134 }, + { 0x1, 0x1, 3, 2759, 1545, 33, 1, 134 }, + { 0x1, 0x1, 3, 2760, 1555, 33, 1, 134 }, + { 0x1, 0x1, 3, 2761, 1568, 33, 1, 149 }, + { 0x1, 0x1, 3, 2762, 1574, 33, 1, 154 }, + { 0x1, 0x1, 3, 2763, 1580, 33, 1, 154 }, + { 0x1, 0x1, 3, 2764, 1586, 33, 1, 149 }, + { 0x1, 0x1, 3, 2765, 1592, 33, 1, 154 }, + { 0x1, 0x1, 3, 2766, 1598, 33, 1, 154 }, + { 0x1, 0x1, 3, 2767, 1604, 33, 1, 149 }, + { 0x1, 0x1, 3, 2768, 1610, 33, 1, 154 }, + { 0x1, 0x1, 3, 2769, 1616, 33, 1, 154 }, + { 0x1, 0x1, 3, 2770, 1622, 33, 1, 149 }, + { 0x1, 0x1, 3, 2771, 1628, 33, 1, 154 }, + { 0x1, 0x1, 3, 2772, 1634, 33, 1, 149 }, + { 0x1, 0x1, 3, 2773, 1640, 33, 1, 154 }, + { 0x1, 0x1, 3, 2774, 1646, 33, 1, 149 }, + { 0x1, 0x1, 3, 2775, 1652, 33, 1, 154 }, + { 0x1, 0x1, 3, 2776, 1658, 33, 1, 149 }, + { 0x1, 0x1, 3, 2777, 1664, 33, 1, 154 }, + { 0x1, 0x1, 3, 2778, 1670, 33, 1, 154 }, + { 0x1, 0x1, 3, -1, -1, 27, 1, 41 }, + { 0x0, 0x0, 4, 2212, 1425, 0, 1, 142 }, + { 0x0, 0x0, 4, 2213, 1427, 0, 1, 142 }, + { 0x0, 0x0, 4, 2214, 1429, 0, 1, 141 }, + { 0x0, 0x0, 4, 2215, 1431, 0, 1, 141 }, + { 0x0, 0x0, 4, 2216, 1433, 0, 1, 141 }, + { 0x0, 0x0, 4, 2217, 1435, 0, 1, 141 }, + { 0x0, 0x0, 4, 2218, 1437, 0, 1, 141 }, + { 0x0, 0x0, 4, 2219, 1439, 0, 1, 141 }, + { 0x0, 0x0, 4, 2220, 1441, 0, 1, 141 }, + { 0x0, 0x0, 4, 2221, 1443, 0, 1, 141 }, + { 0x0, 0x0, 4, 2222, 1445, 0, 1, 143 }, + { 0x0, 0x0, 4, 2223, 1447, 0, 1, 143 }, + { 0x1, 0x1, 4, -1, 1454, 33, 1, 137 }, + { 0x5, 0x5, 4, 552, 1453, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1463, 33, 1, 137 }, + { 0x5, 0x5, 4, 553, 1462, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1472, 33, 1, 137 }, + { 0x5, 0x5, 4, 554, 1471, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1476, 32, 1, 132 }, + { 0x1, 0x1, 4, -1, 1478, 32, 1, 132 }, + { 0x1, 0x1, 4, -1, 1485, 33, 1, 137 }, + { 0x5, 0x5, 4, 555, 1484, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1494, 33, 1, 137 }, + { 0x5, 0x5, 4, 556, 1493, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1503, 33, 1, 137 }, + { 0x5, 0x5, 4, 557, 1502, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1512, 33, 1, 137 }, + { 0x5, 0x5, 4, 558, 1511, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1521, 33, 1, 137 }, + { 0x5, 0x5, 4, 559, 1520, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1530, 33, 1, 137 }, + { 0x5, 0x5, 4, 560, 1529, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1540, 33, 1, 137 }, + { 0x5, 0x5, 4, 1036, 1538, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1550, 33, 1, 137 }, + { 0x5, 0x5, 4, 1037, 1548, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1560, 33, 1, 137 }, + { 0x5, 0x5, 4, 1038, 1558, 32, 1, 131 }, + { 0x1, 0x21, 10, 2013, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2014, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 420, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2074, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2075, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2076, 0, 0, -1 }, + { 0x0, 0x0, 10, 2017, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2018, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2019, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2020, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 430, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2080, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 434, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2082, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 438, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2084, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 442, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2086, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 446, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2088, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 450, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2090, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 2033, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2034, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 460, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2096, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2097, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2098, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2101, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2102, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2103, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2104, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2105, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2106, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2107, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2108, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2109, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2110, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2111, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2112, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2113, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2114, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2115, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2116, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2117, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2118, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2119, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2120, 0, 0, -1 }, + { 0x1, 0x21, 10, 2037, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2038, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 468, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2122, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2123, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2124, 0, 0, -1 }, + { 0x0, 0x0, 10, 2041, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2042, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2043, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2044, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 478, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2128, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 482, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2130, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 486, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2132, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 490, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2134, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 494, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2136, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 498, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2138, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 2057, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2058, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 508, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2144, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2145, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2146, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2149, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2150, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2151, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2152, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2153, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2154, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2155, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2156, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2157, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2158, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2159, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2160, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2161, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2162, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2163, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2164, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2165, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2166, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2167, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2168, 0, 0, -1 }, + { 0x1, 0x1, 10, 2061, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2062, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2063, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2064, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2169, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2171, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2173, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2175, 0, 0, -1 }, + { 0x1, 0x1, 10, 2065, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 10, 2066, -1, 12, 1, 78 }, + { 0x1, 0x1, 10, 2067, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 10, 2068, -1, 12, 1, 78 }, + { 0x0, 0x0, 10, -1, 2177, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2179, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2181, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2183, 0, 0, -1 }, + { 0x1, 0x1, 10, 2069, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2070, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2071, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2072, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2185, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2187, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2189, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2191, 0, 0, -1 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2015, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 300, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2077, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2078, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2021, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 308, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2023, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 310, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2025, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 312, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2027, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 314, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2029, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 316, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2031, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 318, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2091, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2092, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2093, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 11, 2094, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2035, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 322, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2099, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2100, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2039, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 348, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2125, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2126, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2045, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 356, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2047, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 358, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2049, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 360, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2051, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 362, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2053, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 364, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2055, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 366, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2139, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2140, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2141, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 11, 2142, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2059, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 370, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2147, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2148, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, 2170, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2172, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2174, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2176, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, 2178, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 11, 2180, -1, 12, 1, 78 }, + { 0x1, 0x1, 11, 2182, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 11, 2184, -1, 12, 1, 78 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, 2186, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2188, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2190, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2192, -1, 12, 1, 3 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x1, 0x1, 13, 272, 1452, 34, 1, 131 }, + { 0x1, 0x1, 13, 274, 1461, 34, 1, 131 }, + { 0x1, 0x1, 13, 276, 1470, 34, 1, 131 }, + { 0x1, 0x1, 13, 280, 1483, 34, 1, 131 }, + { 0x1, 0x1, 13, 282, 1492, 34, 1, 131 }, + { 0x1, 0x1, 13, 284, 1501, 34, 1, 131 }, + { 0x1, 0x1, 13, 286, 1510, 34, 1, 131 }, + { 0x1, 0x1, 13, 288, 1519, 34, 1, 131 }, + { 0x1, 0x1, 13, 290, 1528, 34, 1, 131 }, + { 0x1, 0x1, 13, 292, 1537, 34, 1, 131 }, + { 0x1, 0x1, 13, 294, 1547, 34, 1, 131 }, + { 0x1, 0x1, 13, 296, 1557, 34, 1, 131 }, + { 0x0, 0x0, 19, -1, 795, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 796, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 797, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 798, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 799, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 800, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 801, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 802, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 803, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 804, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 805, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 806, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 807, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 808, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 809, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 810, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 811, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 812, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 813, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 814, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 815, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 816, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 817, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 818, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 819, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 820, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 821, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 822, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 823, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 824, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2827, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2828, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2843, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2844, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2849, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2850, 0, 0, -1 }, + { 0x0, 0x0, 21, 831, 2839, 0, 0, -1 }, + { 0x0, 0x0, 21, 832, 2841, 0, 0, -1 }, + { 0x0, 0x0, 23, -1, 2837, 0, 0, -1 }, + { 0x0, 0x0, 23, -1, 2838, 0, 0, -1 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, 1272, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, 1293, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, 1326, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 33, 1, 82 }, + { 0x1, 0x1, 24, -1, -1, 33, 1, 82 }, + { 0x1, 0x1, 24, 1342, 1455, 35, 1, 137 }, + { 0x1, 0x1, 24, 1343, 1464, 35, 1, 137 }, + { 0x1, 0x1, 24, 1344, 1473, 35, 1, 137 }, + { 0x1, 0x1, 24, 1345, 1486, 35, 1, 137 }, + { 0x1, 0x1, 24, 1346, 1495, 35, 1, 137 }, + { 0x1, 0x1, 24, 1347, 1504, 35, 1, 137 }, + { 0x1, 0x1, 24, 1348, 1513, 35, 1, 137 }, + { 0x1, 0x1, 24, 1349, 1522, 35, 1, 137 }, + { 0x1, 0x1, 24, 1350, 1531, 35, 1, 137 }, + { 0x1, 0x1, 24, 1351, 1541, 35, 1, 137 }, + { 0x1, 0x1, 24, 1352, 1551, 35, 1, 137 }, + { 0x1, 0x1, 24, 1353, 1561, 35, 1, 137 }, + { 0x1, 0x1, 24, 1354, 1570, 35, 1, 151 }, + { 0x1, 0x1, 24, 1355, 1576, 35, 1, 156 }, + { 0x1, 0x1, 24, 1356, 1582, 35, 1, 156 }, + { 0x1, 0x1, 24, 1357, 1588, 35, 1, 151 }, + { 0x1, 0x1, 24, 1358, 1594, 35, 1, 156 }, + { 0x1, 0x1, 24, 1359, 1600, 35, 1, 156 }, + { 0x1, 0x1, 24, 1360, 1606, 35, 1, 151 }, + { 0x1, 0x1, 24, 1361, 1612, 35, 1, 156 }, + { 0x1, 0x1, 24, 1362, 1618, 35, 1, 156 }, + { 0x1, 0x1, 24, 1363, 1624, 35, 1, 151 }, + { 0x1, 0x1, 24, 1364, 1630, 35, 1, 156 }, + { 0x1, 0x1, 24, 1365, 1636, 35, 1, 151 }, + { 0x1, 0x1, 24, 1366, 1642, 35, 1, 156 }, + { 0x1, 0x1, 24, 1367, 1648, 35, 1, 151 }, + { 0x1, 0x1, 24, 1368, 1654, 35, 1, 156 }, + { 0x1, 0x1, 24, 1369, 1660, 35, 1, 151 }, + { 0x1, 0x1, 24, 1370, 1666, 35, 1, 156 }, + { 0x1, 0x1, 24, 1371, 1672, 35, 1, 156 }, + { 0x0, 0x0, 33, 2821, 2819, 0, 0, -1 }, + { 0x0, 0x0, 33, 2824, 2822, 0, 0, -1 }, + { 0x0, 0x0, 33, 2830, 2829, 0, 0, -1 }, + { 0x0, 0x0, 33, 2832, 2831, 0, 0, -1 }, + { 0x0, 0x0, 33, 2846, 2845, 0, 0, -1 }, + { 0x0, 0x0, 33, 2848, 2847, 0, 0, -1 }, + { 0x0, 0x0, 35, -1, 2840, 0, 0, -1 }, + { 0x0, 0x0, 35, -1, 2842, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, 2290, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2349, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2352, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2357, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2360, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2363, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2366, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2369, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2402, 37, 1, 30 }, + { 0x3, 0x3, 38, -1, -1, 30, 1, 144 }, + { 0x0, 0x0, 38, 1142, -1, 0, 1, 102 }, + { 0x0, 0x0, 38, -1, -1, 0, 1, 111 }, + { 0x0, 0x0, 38, 1148, -1, 0, 1, 123 }, + { 0x3, 0x3, 38, -1, -1, 30, 1, 160 }, + { 0x0, 0x0, 38, 1149, -1, 0, 1, 41 }, + { 0x0, 0x0, 40, -1, 973, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 981, 0, 0, -1 }, + { 0x0, 0x0, 40, 1151, 977, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 622, 33, 1, 6 }, + { 0x18000001, 0x18000001, 40, -1, 630, 6, 1, 7 }, + { 0x3, 0x3, 40, 1152, 626, 33, 1, 6 }, + { 0x0, 0x0, 40, -1, 985, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 642, 33, 1, 8 }, + { 0x0, 0x0, 40, -1, 989, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 654, 33, 1, 16 }, + { 0x0, 0x0, 40, -1, 994, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 998, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 677, 33, 1, 18 }, + { 0x3, 0x3, 40, -1, 681, 33, 1, 18 }, + { 0x0, 0x0, 40, -1, 1002, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1006, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 701, 33, 1, 19 }, + { 0x18000001, 0x18000001, 40, -1, 705, 6, 1, 19 }, + { 0x0, 0x0, 40, -1, 1010, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 717, 33, 1, 20 }, + { 0x0, 0x0, 40, -1, 1014, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1018, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 737, 33, 1, 21 }, + { 0x18000001, 0x18000001, 40, -1, 741, 6, 1, 21 }, + { 0x0, 0x0, 40, -1, 1022, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 753, 33, 1, 22 }, + { 0x0, 0x0, 40, -1, 1027, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1031, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 776, 33, 1, 18 }, + { 0x3, 0x3, 40, -1, 780, 33, 1, 18 }, + { 0x0, 0x0, 40, -1, 1035, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 792, 33, 1, 22 }, + { 0x0, 0x0, 41, 851, 972, 0, 0, -1 }, + { 0x0, 0x0, 41, 852, 980, 0, 0, -1 }, + { 0x0, 0x0, 41, 853, 976, 0, 0, -1 }, + { 0x1, 0x1, 41, 854, 621, 34, 1, 6 }, + { 0x10000001, 0x10000001, 41, 855, 629, 6, 1, 7 }, + { 0x1, 0x1, 41, 856, 625, 34, 1, 6 }, + { 0x0, 0x0, 41, 857, 984, 0, 0, -1 }, + { 0x1, 0x1, 41, 858, 641, 34, 1, 8 }, + { 0x0, 0x0, 41, 859, 988, 0, 0, -1 }, + { 0x1, 0x1, 41, 860, 653, 34, 1, 16 }, + { 0x0, 0x0, 41, 861, 993, 0, 0, -1 }, + { 0x0, 0x0, 41, 862, 997, 0, 0, -1 }, + { 0x1, 0x1, 41, 863, 676, 34, 1, 18 }, + { 0x1, 0x1, 41, 864, 680, 34, 1, 18 }, + { 0x0, 0x0, 41, 865, 1001, 0, 0, -1 }, + { 0x0, 0x0, 41, 866, 1005, 0, 0, -1 }, + { 0x1, 0x1, 41, 867, 700, 34, 1, 19 }, + { 0x10000001, 0x10000001, 41, 868, 704, 6, 1, 19 }, + { 0x0, 0x0, 41, 869, 1009, 0, 0, -1 }, + { 0x1, 0x1, 41, 870, 716, 34, 1, 20 }, + { 0x0, 0x0, 41, 871, 1013, 0, 0, -1 }, + { 0x0, 0x0, 41, 872, 1017, 0, 0, -1 }, + { 0x1, 0x1, 41, 873, 736, 34, 1, 21 }, + { 0x10000001, 0x10000001, 41, 874, 740, 6, 1, 21 }, + { 0x0, 0x0, 41, 875, 1021, 0, 0, -1 }, + { 0x1, 0x1, 41, 876, 752, 34, 1, 22 }, + { 0x0, 0x0, 41, 877, 1026, 0, 0, -1 }, + { 0x0, 0x0, 41, 878, 1030, 0, 0, -1 }, + { 0x1, 0x1, 41, 879, 775, 34, 1, 18 }, + { 0x1, 0x1, 41, 880, 779, 34, 1, 18 }, + { 0x0, 0x0, 41, 881, 1034, 0, 0, -1 }, + { 0x1, 0x1, 41, 882, 791, 34, 1, 22 }, + { 0x800001, 0x800001, 41, -1, 1156, 4, 1, 17 }, + { 0x1, 0x1, 41, 2236, 1154, 4, 1, 17 }, + { 0x1, 0x1, 41, 957, 1159, 4, 1, 23 }, + { 0x2, 0x3, 41, -1, 1164, 20, 1, 68 }, + { 0x1, 0x1, 41, 2237, 1162, 21, 1, 68 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 86 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 86 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 130 }, + { 0x1, 0x1, 44, 1372, 297, 38, 1, 1 }, + { 0x1, 0x1, 44, 1373, 299, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 302, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 424, 0, 0, -1 }, + { 0x1, 0x1, 44, 1377, 319, 38, 1, 1 }, + { 0x1, 0x1, 44, 1378, 321, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 324, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 464, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 326, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 344, 0, 0, -1 }, + { 0x1, 0x1, 44, 1384, 345, 38, 1, 1 }, + { 0x1, 0x1, 44, 1385, 347, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 350, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 472, 0, 0, -1 }, + { 0x1, 0x1, 44, 1389, 367, 38, 1, 1 }, + { 0x1, 0x1, 44, 1390, 369, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 372, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 512, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 374, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 392, 0, 0, -1 }, + { 0x0, 0x0, 44, 1248, 2297, 0, 0, -1 }, + { 0x0, 0x0, 44, 1249, 2305, 0, 1, 55 }, + { 0x0, 0x0, 44, 1250, 2972, 0, 1, 55 }, + { 0x0, 0x0, 44, 1251, 2373, 0, 0, -1 }, + { 0x0, 0x0, 44, 1252, -1, 0, 1, 50 }, + { 0x0, 0x0, 44, 1120, -1, 0, 1, 0 }, + { 0x0, 0x0, 44, 1121, -1, 0, 1, 0 }, + { 0x0, 0x0, 44, 1122, -1, 0, 1, 0 }, + { 0x1, 0x1, 45, -1, 1676, 30, 1, 158 }, + { 0x1, 0x1, 45, 963, 1675, 30, 1, 158 }, + { 0x1, 0x1, 45, -1, 1680, 30, 1, 159 }, + { 0x1, 0x1, 45, 964, 1679, 30, 1, 159 }, + { 0x1, 0x1, 45, -1, 1684, 30, 1, 159 }, + { 0x1, 0x1, 45, 965, 1683, 30, 1, 159 }, + { 0x3, 0x3, 46, -1, 1160, 3, 1, 23 }, + { 0x1, 0x1, 47, 2257, -1, 30, 1, 144 }, + { 0x1, 0x1, 47, 2288, -1, 30, 1, 160 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 56, -1, 1677, 31, 1, 158 }, + { 0x1, 0x1, 56, -1, 1681, 31, 1, 159 }, + { 0x1, 0x1, 56, -1, 1685, 31, 1, 159 }, + { 0x0, 0x0, 56, -1, -1, 0, 1, 101 }, + { 0x2, 0x3, 56, -1, -1, 27, 1, 101 }, + { 0x1, 0x1, 56, -1, -1, 28, 1, 101 }, + { 0x0, 0x0, 65, 14, 592, 0, 1, 6 }, + { 0x0, 0x0, 65, 1273, 595, 0, 1, 6 }, + { 0x1, 0x1, 65, 1274, 597, 33, 1, 6 }, + { 0x1, 0x1, 65, 1275, 599, 34, 1, 6 }, + { 0x3, 0x3, 65, 1276, 601, 33, 1, 6 }, + { 0x0, 0x0, 65, 1277, 603, 0, 1, 6 }, + { 0x1, 0x1, 65, 1278, 605, 33, 1, 6 }, + { 0x1, 0x1, 65, 1279, 607, 34, 1, 6 }, + { 0x3, 0x3, 65, 1280, 609, 33, 1, 6 }, + { 0x1, 0x1, 65, 1281, 611, 6, 1, 7 }, + { 0x8000001, 0x8000001, 65, 1282, 613, 6, 1, 7 }, + { 0x10000001, 0x10000001, 65, 1283, 615, 6, 1, 7 }, + { 0x18000001, 0x18000001, 65, 1284, 617, 6, 1, 7 }, + { 0x0, 0x0, 65, 1285, 631, 0, 1, 8 }, + { 0x1, 0x1, 65, 1286, 633, 33, 1, 8 }, + { 0x1, 0x1, 65, 1287, 635, 34, 1, 8 }, + { 0x3, 0x3, 65, 1288, 637, 33, 1, 8 }, + { 0x0, 0x0, 65, 1289, 643, 0, 1, 16 }, + { 0x1, 0x1, 65, 1290, 645, 33, 1, 16 }, + { 0x1, 0x1, 65, 1291, 647, 34, 1, 16 }, + { 0x3, 0x3, 65, 1292, 649, 33, 1, 16 }, + { 0x0, 0x0, 65, 15, 655, 0, 1, 18 }, + { 0x0, 0x0, 65, 1294, 658, 0, 1, 18 }, + { 0x1, 0x1, 65, 1295, 660, 33, 1, 18 }, + { 0x1, 0x1, 65, 1296, 662, 34, 1, 18 }, + { 0x3, 0x3, 65, 1297, 664, 33, 1, 18 }, + { 0x0, 0x0, 65, 1298, 666, 0, 1, 18 }, + { 0x1, 0x1, 65, 1299, 668, 33, 1, 18 }, + { 0x1, 0x1, 65, 1300, 670, 34, 1, 18 }, + { 0x3, 0x3, 65, 1301, 672, 33, 1, 18 }, + { 0x0, 0x0, 65, 1302, 682, 0, 1, 19 }, + { 0x1, 0x1, 65, 1303, 684, 33, 1, 19 }, + { 0x1, 0x1, 65, 1304, 686, 34, 1, 19 }, + { 0x3, 0x3, 65, 1305, 688, 33, 1, 19 }, + { 0x1, 0x1, 65, 1306, 690, 6, 1, 19 }, + { 0x8000001, 0x8000001, 65, 1307, 692, 6, 1, 19 }, + { 0x10000001, 0x10000001, 65, 1308, 694, 6, 1, 19 }, + { 0x18000001, 0x18000001, 65, 1309, 696, 6, 1, 19 }, + { 0x0, 0x0, 65, 1310, 706, 0, 1, 20 }, + { 0x1, 0x1, 65, 1311, 708, 33, 1, 20 }, + { 0x1, 0x1, 65, 1312, 710, 34, 1, 20 }, + { 0x3, 0x3, 65, 1313, 712, 33, 1, 20 }, + { 0x0, 0x0, 65, 1314, 718, 0, 1, 21 }, + { 0x1, 0x1, 65, 1315, 720, 33, 1, 21 }, + { 0x1, 0x1, 65, 1316, 722, 34, 1, 21 }, + { 0x3, 0x3, 65, 1317, 724, 33, 1, 21 }, + { 0x1, 0x1, 65, 1318, 726, 6, 1, 21 }, + { 0x8000001, 0x8000001, 65, 1319, 728, 6, 1, 21 }, + { 0x10000001, 0x10000001, 65, 1320, 730, 6, 1, 21 }, + { 0x18000001, 0x18000001, 65, 1321, 732, 6, 1, 21 }, + { 0x0, 0x0, 65, 1322, 742, 0, 1, 22 }, + { 0x1, 0x1, 65, 1323, 744, 33, 1, 22 }, + { 0x1, 0x1, 65, 1324, 746, 34, 1, 22 }, + { 0x3, 0x3, 65, 1325, 748, 33, 1, 22 }, + { 0x0, 0x0, 65, 17, 754, 0, 1, 18 }, + { 0x0, 0x0, 65, 1327, 757, 0, 1, 18 }, + { 0x1, 0x1, 65, 1328, 759, 33, 1, 18 }, + { 0x1, 0x1, 65, 1329, 761, 34, 1, 18 }, + { 0x3, 0x3, 65, 1330, 763, 33, 1, 18 }, + { 0x0, 0x0, 65, 1331, 765, 0, 1, 18 }, + { 0x1, 0x1, 65, 1332, 767, 33, 1, 18 }, + { 0x1, 0x1, 65, 1333, 769, 34, 1, 18 }, + { 0x3, 0x3, 65, 1334, 771, 33, 1, 18 }, + { 0x0, 0x0, 65, 1335, 781, 0, 1, 22 }, + { 0x1, 0x1, 65, 1336, 783, 33, 1, 22 }, + { 0x1, 0x1, 65, 1337, 785, 34, 1, 22 }, + { 0x3, 0x3, 65, 1338, 787, 33, 1, 22 }, + { 0x3, 0x3, 66, 561, 1539, 33, 1, 136 }, + { 0x3, 0x3, 66, 562, 1549, 33, 1, 136 }, + { 0x3, 0x3, 66, 563, 1559, 33, 1, 136 }, + { 0x0, 0x0, 66, -1, 1564, 0, 1, 147 }, + { 0x0, 0x0, 66, -1, 1565, 0, 1, 152 }, + { 0x0, 0x0, 66, -1, 1566, 0, 1, 152 }, + { 0x0, 0x0, 107, 1046, 2345, 0, 0, -1 }, + { 0x0, 0x0, 107, 1047, 2864, 0, 1, 30 }, + { 0x0, 0x0, 107, 1048, 2386, 0, 0, -1 }, + { 0x0, 0x0, 107, 1049, 2868, 0, 1, 30 }, + { 0x0, 0x0, 109, -1, 2347, 0, 0, -1 }, + { 0x1, 0x1, 109, -1, 2865, 27, 1, 30 }, + { 0x0, 0x0, 109, -1, 2388, 0, 0, -1 }, + { 0x1, 0x1, 109, -1, 2869, 27, 1, 30 }, + { 0x0, 0x0, 110, 1051, -1, 0, 1, 122 }, + { 0x1, 0x1, 111, -1, -1, 27, 1, 122 }, + { 0x0, 0x0, 112, 1082, 2894, 0, 1, 1 }, + { 0x0, 0x0, 112, 1083, 2897, 0, 1, 1 }, + { 0x0, 0x0, 112, 1224, 305, 0, 0, -1 }, + { 0x0, 0x0, 112, 1225, 309, 0, 0, -1 }, + { 0x0, 0x0, 112, 1185, 440, 0, 0, -1 }, + { 0x0, 0x0, 112, 1186, 448, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 456, 0, 0, -1 }, + { 0x0, 0x0, 112, 1084, 2910, 0, 1, 1 }, + { 0x0, 0x0, 112, 1085, 2913, 0, 1, 1 }, + { 0x0, 0x0, 112, -1, 330, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 334, 0, 0, -1 }, + { 0x0, 0x0, 112, 1233, 335, 0, 0, -1 }, + { 0x0, 0x0, 112, 1234, 339, 0, 0, -1 }, + { 0x0, 0x0, 112, 1086, 2934, 0, 1, 1 }, + { 0x0, 0x0, 112, 1087, 2937, 0, 1, 1 }, + { 0x0, 0x0, 112, 1237, 353, 0, 0, -1 }, + { 0x0, 0x0, 112, 1238, 357, 0, 0, -1 }, + { 0x0, 0x0, 112, 1198, 488, 0, 0, -1 }, + { 0x0, 0x0, 112, 1199, 496, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 504, 0, 0, -1 }, + { 0x0, 0x0, 112, 1391, 2948, 0, 1, 1 }, + { 0x0, 0x0, 112, 1392, 2950, 0, 1, 1 }, + { 0x0, 0x0, 112, -1, 378, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 382, 0, 0, -1 }, + { 0x0, 0x0, 112, 1246, 383, 0, 0, -1 }, + { 0x0, 0x0, 112, 1247, 387, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 2315, 0, 0, -1 }, + { 0x1, 0x9, 112, -1, 2319, 33, 1, 55 }, + { 0x1, 0x9, 112, -1, 2981, 33, 1, 55 }, + { 0x2, 0x3, 112, 1408, 2382, 27, 1, 50 }, + { 0x1, 0x1, 114, 1374, 2895, 37, 1, 1 }, + { 0x1, 0x1, 114, 1375, 2898, 37, 1, 1 }, + { 0x1, 0x1, 114, 1379, 2911, 37, 1, 1 }, + { 0x1, 0x1, 114, 1380, 2914, 37, 1, 1 }, + { 0x1, 0x1, 114, 1386, 2935, 37, 1, 1 }, + { 0x1, 0x1, 114, 1387, 2938, 37, 1, 1 }, + { 0x0, 0x0, 114, -1, 2958, 0, 1, 1 }, + { 0x0, 0x0, 114, -1, 2959, 0, 1, 1 }, + { 0x0, 0x0, 115, 1123, 2890, 0, 1, 1 }, + { 0x0, 0x0, 115, 1124, 2892, 0, 1, 1 }, + { 0x0, 0x0, 115, 1183, 303, 0, 0, -1 }, + { 0x0, 0x0, 115, 1184, 307, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 444, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 452, 0, 0, -1 }, + { 0x0, 0x0, 115, 1228, 454, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 2908, 0, 1, 1 }, + { 0x0, 0x0, 115, -1, 2909, 0, 1, 1 }, + { 0x0, 0x0, 115, 1231, 328, 0, 0, -1 }, + { 0x0, 0x0, 115, 1232, 332, 0, 0, -1 }, + { 0x0, 0x0, 115, 1192, 337, 0, 0, -1 }, + { 0x0, 0x0, 115, 1193, 341, 0, 0, -1 }, + { 0x0, 0x0, 115, 1127, 2930, 0, 1, 1 }, + { 0x0, 0x0, 115, 1128, 2932, 0, 1, 1 }, + { 0x0, 0x0, 115, 1196, 351, 0, 0, -1 }, + { 0x0, 0x0, 115, 1197, 355, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 492, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 500, 0, 0, -1 }, + { 0x0, 0x0, 115, 1241, 502, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 2946, 0, 1, 1 }, + { 0x0, 0x0, 115, -1, 2947, 0, 1, 1 }, + { 0x0, 0x0, 115, 1244, 376, 0, 0, -1 }, + { 0x0, 0x0, 115, 1245, 380, 0, 0, -1 }, + { 0x0, 0x0, 115, 1205, 385, 0, 0, -1 }, + { 0x0, 0x0, 115, 1206, 389, 0, 0, -1 }, + { 0x0, 0x0, 115, 1078, 2313, 0, 0, -1 }, + { 0x0, 0x0, 115, 1079, 2317, 0, 1, 55 }, + { 0x0, 0x0, 115, 1080, 2980, 0, 1, 55 }, + { 0x0, 0x0, 115, 1081, 2381, 0, 1, 50 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 116, -1, 2891, 37, 1, 1 }, + { 0x1, 0x1, 116, -1, 2893, 37, 1, 1 }, + { 0x0, 0x0, 116, -1, 2918, 0, 1, 1 }, + { 0x0, 0x0, 116, -1, 2919, 0, 1, 1 }, + { 0x1, 0x1, 116, -1, 2931, 37, 1, 1 }, + { 0x1, 0x1, 116, -1, 2933, 37, 1, 1 }, + { 0x0, 0x0, 116, -1, 2956, 0, 1, 1 }, + { 0x0, 0x0, 116, -1, 2957, 0, 1, 1 }, + { 0x0, 0x0, 117, 1176, -1, 0, 1, 0 }, + { 0x0, 0x0, 117, 1177, -1, 0, 1, 0 }, + { 0x0, 0x0, 117, 1178, -1, 0, 1, 0 }, + { 0x3, 0x3, 117, 1136, -1, 34, 1, 34 }, + { 0x3, 0x3, 117, 1137, -1, 34, 1, 41 }, + { 0x1, 0x1, 119, -1, -1, 35, 1, 34 }, + { 0x1, 0x1, 119, -1, -1, 35, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 67 }, + { 0x1, 0x1, 120, -1, -1, 36, 1, 129 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 103 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 112 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 74 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 74 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 75 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 124 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 121, -1, 2820, 0, 0, -1 }, + { 0x0, 0x0, 121, -1, 2823, 0, 0, -1 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, 918, -1, 23, 1, 68 }, + { 0x9, 0x9, 122, 919, -1, 20, 1, 68 }, + { 0x0, 0x0, 126, 2199, -1, 0, 1, 0 }, + { 0x0, 0x0, 126, 2200, -1, 0, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 126, -1, -1, 27, 1, 34 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 126, -1, -1, 0, 1, 121 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 126, 1134, -1, 0, 1, 34 }, + { 0x0, 0x0, 126, 1262, -1, 0, 1, 41 }, + { 0x0, 0x0, 140, 1212, 2886, 0, 1, 1 }, + { 0x0, 0x0, 140, 1213, 2888, 0, 1, 1 }, + { 0x0, 0x0, 140, 1054, 304, 0, 0, -1 }, + { 0x0, 0x0, 140, 1055, 432, 0, 0, -1 }, + { 0x0, 0x0, 140, 1094, 313, 0, 0, -1 }, + { 0x0, 0x0, 140, 1095, 317, 0, 0, -1 }, + { 0x0, 0x0, 140, 1096, 453, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 2906, 0, 1, 1 }, + { 0x0, 0x0, 140, -1, 2907, 0, 1, 1 }, + { 0x0, 0x0, 140, 1099, 327, 0, 0, -1 }, + { 0x0, 0x0, 140, 1100, 331, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 338, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 342, 0, 0, -1 }, + { 0x0, 0x0, 140, 1216, 2926, 0, 1, 1 }, + { 0x0, 0x0, 140, 1217, 2928, 0, 1, 1 }, + { 0x0, 0x0, 140, 1067, 352, 0, 0, -1 }, + { 0x0, 0x0, 140, 1068, 480, 0, 0, -1 }, + { 0x0, 0x0, 140, 1107, 361, 0, 0, -1 }, + { 0x0, 0x0, 140, 1108, 365, 0, 0, -1 }, + { 0x0, 0x0, 140, 1109, 501, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 2944, 0, 1, 1 }, + { 0x0, 0x0, 140, -1, 2945, 0, 1, 1 }, + { 0x0, 0x0, 140, 1112, 375, 0, 0, -1 }, + { 0x0, 0x0, 140, 1113, 379, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 386, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 390, 0, 0, -1 }, + { 0x0, 0x0, 140, 3012, 2301, 0, 0, -1 }, + { 0x1, 0x1, 140, 3013, 2309, 33, 1, 55 }, + { 0x1, 0x1, 140, 3014, 2974, 33, 1, 55 }, + { 0x0, 0x0, 140, 3015, 2375, 0, 0, -1 }, + { 0x1, 0x1, 140, 3016, -1, 28, 1, 50 }, + { 0x1, 0x1, 141, -1, 2887, 37, 1, 1 }, + { 0x1, 0x1, 141, -1, 2889, 37, 1, 1 }, + { 0x0, 0x0, 141, -1, 2916, 0, 1, 1 }, + { 0x0, 0x0, 141, -1, 2917, 0, 1, 1 }, + { 0x1, 0x1, 141, -1, 2927, 37, 1, 1 }, + { 0x1, 0x1, 141, -1, 2929, 37, 1, 1 }, + { 0x0, 0x0, 141, -1, 2954, 0, 1, 1 }, + { 0x0, 0x0, 141, -1, 2955, 0, 1, 1 }, + { 0x1, 0x1, 144, 917, 1158, 3, 1, 23 }, + { 0x0, 0x0, 145, 2201, -1, 0, 1, 34 }, + { 0x0, 0x0, 146, 923, 2880, 0, 1, 1 }, + { 0x0, 0x0, 146, 924, 2883, 0, 1, 1 }, + { 0x0, 0x0, 146, -1, 306, 0, 0, -1 }, + { 0x0, 0x0, 146, -1, 436, 0, 0, -1 }, + { 0x0, 0x0, 146, 1056, 311, 0, 0, -1 }, + { 0x0, 0x0, 146, 1057, 315, 0, 0, -1 }, + { 0x0, 0x0, 146, 1058, 455, 0, 0, -1 }, + { 0x0, 0x0, 146, 927, 2900, 0, 1, 1 }, + { 0x0, 0x0, 146, 928, 2903, 0, 1, 1 }, + { 0x0, 0x0, 146, 1061, 329, 0, 0, -1 }, + { 0x0, 0x0, 146, 1062, 333, 0, 0, -1 }, + { 0x0, 0x0, 146, 1101, 336, 0, 0, -1 }, + { 0x0, 0x0, 146, 1102, 340, 0, 0, -1 }, + { 0x0, 0x0, 146, 933, 2920, 0, 1, 1 }, + { 0x0, 0x0, 146, 934, 2923, 0, 1, 1 }, + { 0x0, 0x0, 146, -1, 354, 0, 0, -1 }, + { 0x0, 0x0, 146, -1, 484, 0, 0, -1 }, + { 0x0, 0x0, 146, 1069, 359, 0, 0, -1 }, + { 0x0, 0x0, 146, 1070, 363, 0, 0, -1 }, + { 0x0, 0x0, 146, 1071, 503, 0, 0, -1 }, + { 0x0, 0x0, 146, 937, 2940, 0, 1, 1 }, + { 0x0, 0x0, 146, 938, 2942, 0, 1, 1 }, + { 0x0, 0x0, 146, 1074, 377, 0, 0, -1 }, + { 0x0, 0x0, 146, 1075, 381, 0, 0, -1 }, + { 0x0, 0x0, 146, 1114, 384, 0, 0, -1 }, + { 0x0, 0x0, 146, 1115, 388, 0, 0, -1 }, + { 0x0, 0x0, 146, 1207, 2299, 0, 0, -1 }, + { 0x1, 0x1, 146, 1208, 2307, 36, 1, 55 }, + { 0x1, 0x1, 146, 1209, 2973, 36, 1, 55 }, + { 0x0, 0x0, 146, 1210, 2374, 0, 0, -1 }, + { 0x1, 0x1, 146, 1211, -1, 27, 1, 50 }, + { 0x1, 0x1, 147, -1, 2882, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2885, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2902, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2905, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2922, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2925, 37, 1, 1 }, + { 0x0, 0x0, 147, -1, 2952, 0, 1, 1 }, + { 0x0, 0x0, 147, -1, 2953, 0, 1, 1 }, + { 0x0, 0x0, 148, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 148, 1135, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 67 }, + { 0x0, 0x0, 149, -1, 2960, 0, 1, 64 }, + { 0x0, 0x0, 149, -1, 2961, 0, 1, 64 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 87 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 87 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 92 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 150, -1, 593, 12, 1, 6 }, + { 0x1, 0x1, 150, -1, 596, 12, 1, 6 }, + { 0x200001, 0x200001, 150, -1, 598, 12, 1, 6 }, + { 0x400001, 0x400001, 150, -1, 600, 12, 1, 6 }, + { 0x600001, 0x600001, 150, -1, 602, 12, 1, 6 }, + { 0x1, 0x1, 150, -1, 604, 12, 1, 6 }, + { 0x200001, 0x200001, 150, -1, 606, 12, 1, 6 }, + { 0x400001, 0x400001, 150, -1, 608, 12, 1, 6 }, + { 0x600001, 0x600001, 150, -1, 610, 12, 1, 6 }, + { 0x41, 0x41, 150, -1, 612, 6, 1, 7 }, + { 0x8000041, 0x8000041, 150, -1, 614, 6, 1, 7 }, + { 0x10000041, 0x10000041, 150, -1, 616, 6, 1, 7 }, + { 0x18000041, 0x18000041, 150, -1, 618, 6, 1, 7 }, + { 0x1, 0x1, 150, -1, 632, 12, 1, 8 }, + { 0x200001, 0x200001, 150, -1, 634, 12, 1, 8 }, + { 0x400001, 0x400001, 150, -1, 636, 12, 1, 8 }, + { 0x600001, 0x600001, 150, -1, 638, 12, 1, 8 }, + { 0x1, 0x1, 150, -1, 644, 12, 1, 16 }, + { 0x200001, 0x200001, 150, -1, 646, 12, 1, 16 }, + { 0x400001, 0x400001, 150, -1, 648, 12, 1, 16 }, + { 0x600001, 0x600001, 150, -1, 650, 12, 1, 16 }, + { 0x1, 0x1, 150, -1, 656, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 659, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 661, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 663, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 665, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 667, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 669, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 671, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 673, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 683, 12, 1, 19 }, + { 0x200001, 0x200001, 150, -1, 685, 12, 1, 19 }, + { 0x400001, 0x400001, 150, -1, 687, 12, 1, 19 }, + { 0x600001, 0x600001, 150, -1, 689, 12, 1, 19 }, + { 0x41, 0x41, 150, -1, 691, 6, 1, 19 }, + { 0x8000041, 0x8000041, 150, -1, 693, 6, 1, 19 }, + { 0x10000041, 0x10000041, 150, -1, 695, 6, 1, 19 }, + { 0x18000041, 0x18000041, 150, -1, 697, 6, 1, 19 }, + { 0x1, 0x1, 150, -1, 707, 12, 1, 20 }, + { 0x200001, 0x200001, 150, -1, 709, 12, 1, 20 }, + { 0x400001, 0x400001, 150, -1, 711, 12, 1, 20 }, + { 0x600001, 0x600001, 150, -1, 713, 12, 1, 20 }, + { 0x1, 0x1, 150, -1, 719, 12, 1, 21 }, + { 0x200001, 0x200001, 150, -1, 721, 12, 1, 21 }, + { 0x400001, 0x400001, 150, -1, 723, 12, 1, 21 }, + { 0x600001, 0x600001, 150, -1, 725, 12, 1, 21 }, + { 0x41, 0x41, 150, -1, 727, 6, 1, 21 }, + { 0x8000041, 0x8000041, 150, -1, 729, 6, 1, 21 }, + { 0x10000041, 0x10000041, 150, -1, 731, 6, 1, 21 }, + { 0x18000041, 0x18000041, 150, -1, 733, 6, 1, 21 }, + { 0x1, 0x1, 150, -1, 743, 12, 1, 22 }, + { 0x200001, 0x200001, 150, -1, 745, 12, 1, 22 }, + { 0x400001, 0x400001, 150, -1, 747, 12, 1, 22 }, + { 0x600001, 0x600001, 150, -1, 749, 12, 1, 22 }, + { 0x1, 0x1, 150, -1, 755, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 758, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 760, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 762, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 764, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 766, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 768, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 770, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 772, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 782, 12, 1, 22 }, + { 0x200001, 0x200001, 150, -1, 784, 12, 1, 22 }, + { 0x400001, 0x400001, 150, -1, 786, 12, 1, 22 }, + { 0x600001, 0x600001, 150, -1, 788, 12, 1, 22 }, + { 0x0, 0x0, 155, -1, -1, 0, 1, 131 }, + { 0x0, 0x0, 159, 793, -1, 0, 1, 81 }, + { 0x0, 0x0, 159, 794, -1, 0, 1, 81 }, + { 0x9, 0x9, 159, -1, 1456, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1465, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1474, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1487, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1496, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1505, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1514, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1523, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1532, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1542, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1552, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1562, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1571, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1577, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1583, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1589, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1595, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1601, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1607, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1613, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1619, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1625, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1631, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1637, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1643, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1649, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1655, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1661, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1667, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1673, 32, 1, 156 }, + { 0x0, 0x0, 160, 1253, 298, 0, 0, -1 }, + { 0x0, 0x0, 160, 1254, 422, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2896, 38, 1, 1 }, + { 0x1, 0x1, 160, 925, 2899, 38, 1, 1 }, + { 0x0, 0x0, 160, 926, 423, 0, 0, -1 }, + { 0x0, 0x0, 160, 1255, 320, 0, 0, -1 }, + { 0x0, 0x0, 160, 1256, 462, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2912, 38, 1, 1 }, + { 0x1, 0x1, 160, 929, 2915, 38, 1, 1 }, + { 0x0, 0x0, 160, 930, 463, 0, 0, -1 }, + { 0x0, 0x0, 160, 931, 325, 0, 0, -1 }, + { 0x0, 0x0, 160, 932, 343, 0, 0, -1 }, + { 0x0, 0x0, 160, 1257, 346, 0, 0, -1 }, + { 0x0, 0x0, 160, 1258, 470, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2936, 38, 1, 1 }, + { 0x1, 0x1, 160, 935, 2939, 38, 1, 1 }, + { 0x0, 0x0, 160, 936, 471, 0, 0, -1 }, + { 0x0, 0x0, 160, -1, 368, 0, 0, -1 }, + { 0x0, 0x0, 160, -1, 510, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2949, 38, 1, 1 }, + { 0x1, 0x1, 160, 939, 2951, 38, 1, 1 }, + { 0x0, 0x0, 160, 940, 511, 0, 0, -1 }, + { 0x0, 0x0, 160, 941, 373, 0, 0, -1 }, + { 0x0, 0x0, 160, 942, 391, 0, 0, -1 }, + { 0x0, 0x0, 161, 1415, 2321, 0, 0, -1 }, + { 0x0, 0x0, 161, 1416, 2329, 0, 1, 55 }, + { 0x0, 0x0, 161, 1417, 2990, 0, 1, 55 }, + { 0x0, 0x0, 161, 1418, 2377, 0, 0, -1 }, + { 0x1, 0x1, 161, 1419, -1, 29, 1, 50 }, + { 0x0, 0x0, 162, -1, 2339, 0, 0, -1 }, + { 0x1, 0x9, 162, -1, 2343, 33, 1, 55 }, + { 0x1, 0x9, 162, -1, 2999, 33, 1, 55 }, + { 0x6, 0x7, 162, -1, 2384, 27, 1, 50 }, + { 0x0, 0x0, 163, 1401, 2337, 0, 0, -1 }, + { 0x0, 0x0, 163, 1402, 2341, 0, 1, 55 }, + { 0x0, 0x0, 163, 1403, 2998, 0, 1, 55 }, + { 0x1, 0x1, 163, 1404, 2383, 29, 1, 50 }, + { 0x1, 0x1, 164, 1422, -1, 27, 1, 34 }, + { 0x0, 0x0, 165, 2193, 2325, 0, 0, -1 }, + { 0x1, 0x1, 165, 2194, 2333, 33, 1, 55 }, + { 0x1, 0x1, 165, 2195, 2992, 33, 1, 55 }, + { 0x0, 0x0, 165, 2196, 2379, 0, 0, -1 }, + { 0x3, 0x3, 165, 2197, -1, 28, 1, 50 }, + { 0x0, 0x0, 166, 1410, 2323, 0, 0, -1 }, + { 0x1, 0x1, 166, 1411, 2331, 36, 1, 55 }, + { 0x1, 0x1, 166, 1412, 2991, 36, 1, 55 }, + { 0x0, 0x0, 166, 1413, 2378, 0, 0, -1 }, + { 0x5, 0x5, 166, 1414, -1, 27, 1, 50 }, + { 0x0, 0x0, 167, -1, 2962, 0, 1, 64 }, + { 0x0, 0x0, 167, -1, 2963, 0, 1, 64 }, + { 0x1, 0x1, 169, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 170, 2779, -1, 27, 1, 34 }, + { 0x1, 0x1, 170, 2780, -1, 27, 1, 34 }, + { 0x1, 0x1, 171, 1703, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1704, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1705, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1706, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1707, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1708, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1709, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1710, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1711, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1712, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1713, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1714, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1715, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1716, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1717, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1718, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1719, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1720, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1721, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1722, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1723, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1724, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1725, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1726, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1727, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1728, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1729, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1730, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1731, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1732, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1733, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1734, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1735, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1736, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1737, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1738, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1739, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1740, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1741, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1742, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1743, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1744, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1745, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1746, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1747, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1748, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1749, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1750, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1751, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1752, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1753, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1754, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1755, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1756, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1757, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1758, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1759, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1760, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1761, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1762, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1763, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1764, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1765, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1766, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1767, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1768, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1769, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1770, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1771, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1772, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1773, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1774, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1775, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1776, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1777, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1778, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1779, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1780, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1781, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1782, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1783, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1784, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1785, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1786, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1787, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1788, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1789, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1790, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1791, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1792, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1793, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1794, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1795, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1796, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1797, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1798, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1799, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1800, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1801, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1802, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1803, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1804, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1805, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1806, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1807, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1808, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1809, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1810, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1811, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1812, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1813, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1814, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1815, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1816, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1817, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1818, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1819, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1820, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1821, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1822, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1823, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1824, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1825, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1826, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1827, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1828, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1829, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1830, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1831, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1832, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1833, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1834, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1835, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1836, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1837, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1838, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1839, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1840, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1841, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1842, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1843, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1844, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1845, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1846, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1847, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1848, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1849, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1850, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1851, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1852, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1853, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1854, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1855, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1856, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1857, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1858, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1859, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1860, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1861, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1862, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1863, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1864, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1865, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1866, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1867, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1868, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1869, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1870, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1871, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1872, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1873, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1874, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1875, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1876, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1877, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1878, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1879, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1880, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1881, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1882, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1883, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1884, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1885, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1886, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1887, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1888, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1889, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1890, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1891, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1892, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1893, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1894, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1895, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1896, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1897, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1898, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1899, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1900, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1901, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1902, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1903, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1904, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1905, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1906, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1907, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1908, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1909, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1910, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1911, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1912, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1913, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1914, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1915, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1916, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1917, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1918, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1919, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1920, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1921, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1922, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1923, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1924, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1925, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1926, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1927, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1928, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1929, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1930, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1931, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1932, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1933, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1934, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1935, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1936, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1937, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1938, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1939, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1940, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1941, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1942, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1943, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1944, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1945, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1946, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1947, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1948, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1949, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1950, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1951, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1952, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1691, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1692, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1693, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1694, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1695, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1696, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1697, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1698, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1699, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1700, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1701, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1702, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1997, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1998, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1999, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 2000, -1, 28, 1, 143 }, + { 0x1, 0x1, 172, 1953, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1954, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1955, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1956, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1957, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1958, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1959, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1960, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1961, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1962, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1963, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1964, -1, 29, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 271, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2258, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 273, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2259, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 275, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2260, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, 277, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, 278, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 279, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2261, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 281, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2262, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 283, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2263, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 285, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2264, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 287, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2265, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 289, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2266, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 291, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2267, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 293, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2268, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 295, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2269, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2270, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2271, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2272, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2273, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2274, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2275, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2276, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2277, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2278, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2279, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2280, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2281, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2282, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2283, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2284, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2285, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2286, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2287, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, 951, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, 952, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 953, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 954, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 955, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 956, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2224, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2225, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, 2226, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, 2227, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2228, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2229, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2230, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2231, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 140 }, + { 0x3, 0x3, 173, 2232, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, + { 0x3, 0x3, 173, 2233, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x0, 0x0, 174, -1, 394, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 396, 0, 0, -1 }, + { 0x0, 0x0, 174, 3042, 3002, 0, 1, 1 }, + { 0x0, 0x0, 174, 3043, 3003, 0, 1, 1 }, + { 0x0, 0x0, 174, -1, 402, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 404, 0, 0, -1 }, + { 0x0, 0x0, 174, 3046, 3006, 0, 1, 76 }, + { 0x0, 0x0, 174, 3047, 3007, 0, 1, 76 }, + { 0x0, 0x0, 174, -1, 410, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 412, 0, 0, -1 }, + { 0x0, 0x0, 174, 3050, 3010, 0, 1, 1 }, + { 0x0, 0x0, 174, 3051, 3011, 0, 1, 1 }, + { 0x11, 0x31, 175, 2881, 417, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 418, 12, 1, 4 }, + { 0x11, 0x31, 175, 2073, 419, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 421, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 425, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 426, 12, 1, 4 }, + { 0x11, 0x11, 175, -1, 427, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 428, 12, 1, 4 }, + { 0x1, 0x1, 175, 2079, 429, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 431, 12, 1, 4 }, + { 0x11, 0x11, 175, 2081, 433, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 435, 12, 1, 4 }, + { 0x1, 0x1, 175, 2083, 437, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 439, 12, 1, 4 }, + { 0x11, 0x11, 175, 2085, 441, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 443, 12, 1, 4 }, + { 0x1, 0x1, 175, 2087, 445, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 447, 12, 1, 4 }, + { 0x11, 0x11, 175, 2089, 449, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 451, 12, 1, 4 }, + { 0x11, 0x31, 175, 2901, 457, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 458, 12, 1, 4 }, + { 0x11, 0x31, 175, 2095, 459, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 461, 12, 1, 4 }, + { 0x11, 0x31, 175, 2921, 465, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 466, 12, 1, 4 }, + { 0x11, 0x31, 175, 2121, 467, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 469, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 473, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 474, 12, 1, 4 }, + { 0x11, 0x11, 175, -1, 475, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 476, 12, 1, 4 }, + { 0x1, 0x1, 175, 2127, 477, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 479, 12, 1, 4 }, + { 0x11, 0x11, 175, 2129, 481, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 483, 12, 1, 4 }, + { 0x1, 0x1, 175, 2131, 485, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 487, 12, 1, 4 }, + { 0x11, 0x11, 175, 2133, 489, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 491, 12, 1, 4 }, + { 0x1, 0x1, 175, 2135, 493, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 495, 12, 1, 4 }, + { 0x11, 0x11, 175, 2137, 497, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 499, 12, 1, 4 }, + { 0x11, 0x31, 175, 2941, 505, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 506, 12, 1, 4 }, + { 0x11, 0x31, 175, 2143, 507, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 509, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 513, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 514, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 515, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 516, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 521, 33, 1, 79 }, + { 0x200001, 0x200001, 175, -1, 522, 12, 1, 79 }, + { 0x1, 0x1, 175, -1, 523, 33, 1, 79 }, + { 0x200001, 0x200001, 175, -1, 524, 12, 1, 79 }, + { 0x1, 0x1, 175, -1, 529, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 530, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 531, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 532, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2884, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2016, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2022, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2024, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2026, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2028, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2030, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2032, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, -1, -1, 33, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2904, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2036, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x2200001, 0x6200001, 176, 2924, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2040, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2046, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2048, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2050, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2052, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2054, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2056, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, -1, -1, 33, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2943, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2060, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 397, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 398, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 399, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 400, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 80 }, + { 0x1, 0x1, 176, 405, -1, 33, 1, 79 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 80 }, + { 0x200001, 0x200001, 176, 406, -1, 12, 1, 79 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 80 }, + { 0x1, 0x1, 176, 407, -1, 33, 1, 79 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 80 }, + { 0x200001, 0x200001, 176, 408, -1, 12, 1, 79 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 413, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 414, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 415, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 416, -1, 12, 1, 4 }, + { 0x0, 0x0, 177, -1, 2327, 0, 0, -1 }, + { 0x9, 0x9, 177, -1, 2335, 33, 1, 50 }, + { 0x9, 0x9, 177, -1, 2993, 33, 1, 50 }, + { 0x0, 0x0, 177, -1, 2380, 0, 0, -1 }, + { 0x7, 0x7, 177, -1, -1, 27, 1, 50 }, + { 0x1, 0x1, 197, -1, -1, 27, 1, 10 }, + { 0x1, 0x1, 211, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 211, -1, -1, 29, 1, 0 }, + { 0x2, 0x3, 211, 1169, -1, 27, 1, 34 }, + { 0x0, 0x0, 211, 1170, -1, 0, 1, 34 }, + { 0x0, 0x0, 211, 1171, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1172, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1173, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1174, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 3026, -1, 0, 1, 100 }, + { 0x0, 0x0, 211, 3027, -1, 0, 1, 100 }, + { 0x0, 0x0, 211, 3028, 967, 0, 0, -1 }, + { 0x1, 0x1, 212, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 212, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 213, -1, 1426, 32, 1, 142 }, + { 0x1, 0x1, 213, -1, 1428, 32, 1, 142 }, + { 0x1, 0x1, 213, -1, 1430, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1432, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1434, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1436, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1438, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1440, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1442, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1444, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1446, 32, 1, 143 }, + { 0x1, 0x1, 213, -1, 1448, 32, 1, 143 }, + { 0x1, 0x1, 213, -1, 1965, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1967, 32, 1, 145 }, + { 0x1, 0x1, 213, -1, 1969, 32, 1, 139 }, + { 0x1, 0x1, 213, -1, 1971, 32, 1, 139 }, + { 0x1, 0x1, 213, -1, 1973, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1975, 32, 1, 145 }, + { 0x1, 0x1, 213, -1, 1977, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1979, 32, 1, 145 }, + { 0x1, 0x1, 213, 2783, 1981, 32, 1, 138 }, + { 0x1, 0x1, 213, 2784, 1984, 32, 1, 145 }, + { 0x0, 0x0, 214, -1, 2825, 0, 0, -1 }, + { 0x0, 0x0, 214, -1, 2826, 0, 0, -1 }, + { 0x0, 0x0, 214, -1, 2851, 0, 0, -1 }, + { 0x5, 0x5, 214, -1, 2854, 20, 1, 68 }, + { 0x0, 0x0, 218, 2209, 966, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, 1139, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, 1264, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, -1, 0, 1, 128 }, + { 0x0, 0x0, 219, -1, -1, 0, 1, 67 }, + { 0x1, 0x1, 219, 833, 2289, 36, 1, 66 }, + { 0x1, 0x1, 219, 834, 2348, 36, 1, 66 }, + { 0x0, 0x0, 219, 835, 2351, 0, 0, -1 }, + { 0x1, 0x1, 219, 836, -1, 36, 1, 66 }, + { 0x0, 0x0, 219, 1423, -1, 0, 1, 34 }, + { 0x1, 0x1, 219, 837, 2356, 36, 1, 66 }, + { 0x0, 0x0, 219, 838, 2359, 0, 0, -1 }, + { 0x1, 0x1, 219, 839, -1, 36, 1, 66 }, + { 0x0, 0x0, 219, 840, 2362, 0, 0, -1 }, + { 0x1, 0x1, 219, 841, -1, 36, 1, 66 }, + { 0x1, 0x1, 219, 842, 2365, 36, 1, 66 }, + { 0x1, 0x1, 219, 843, 2368, 36, 1, 66 }, + { 0x0, 0x0, 219, 1424, -1, 0, 1, 34 }, + { 0x1, 0x1, 219, 844, 2401, 36, 1, 66 }, + { 0x1, 0x1, 219, 845, -1, 31, 1, 144 }, + { 0x1, 0x1, 219, 228, 1449, 32, 1, 133 }, + { 0x1, 0x1, 219, 229, 1458, 32, 1, 133 }, + { 0x1, 0x1, 219, 230, 1467, 32, 1, 133 }, + { 0x1, 0x1, 219, 231, 1480, 32, 1, 133 }, + { 0x1, 0x1, 219, 232, 1489, 32, 1, 133 }, + { 0x1, 0x1, 219, 233, 1498, 32, 1, 133 }, + { 0x1, 0x1, 219, 234, 1507, 32, 1, 133 }, + { 0x1, 0x1, 219, 235, 1516, 32, 1, 133 }, + { 0x1, 0x1, 219, 236, 1525, 32, 1, 133 }, + { 0x1, 0x1, 219, 237, 1534, 32, 1, 133 }, + { 0x1, 0x1, 219, 238, 1544, 32, 1, 133 }, + { 0x1, 0x1, 219, 239, 1554, 32, 1, 133 }, + { 0x1, 0x1, 219, 240, 1567, 32, 1, 148 }, + { 0x1, 0x1, 219, 241, 1573, 32, 1, 153 }, + { 0x1, 0x1, 219, 242, 1579, 32, 1, 153 }, + { 0x1, 0x1, 219, 243, 1585, 32, 1, 148 }, + { 0x1, 0x1, 219, 244, 1591, 32, 1, 153 }, + { 0x1, 0x1, 219, 245, 1597, 32, 1, 153 }, + { 0x1, 0x1, 219, 246, 1603, 32, 1, 148 }, + { 0x1, 0x1, 219, 247, 1609, 32, 1, 153 }, + { 0x1, 0x1, 219, 248, 1615, 32, 1, 153 }, + { 0x1, 0x1, 219, 249, 1621, 32, 1, 148 }, + { 0x1, 0x1, 219, 250, 1627, 32, 1, 153 }, + { 0x1, 0x1, 219, 251, 1633, 32, 1, 148 }, + { 0x1, 0x1, 219, 252, 1639, 32, 1, 153 }, + { 0x1, 0x1, 219, 253, 1645, 32, 1, 148 }, + { 0x1, 0x1, 219, 254, 1651, 32, 1, 153 }, + { 0x1, 0x1, 219, 255, 1657, 32, 1, 148 }, + { 0x1, 0x1, 219, 256, 1663, 32, 1, 153 }, + { 0x1, 0x1, 219, 257, 1669, 32, 1, 153 }, + { 0x1, 0x1, 219, 849, -1, 31, 1, 160 }, + { 0x0, 0x0, 220, 2404, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2405, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 25, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2407, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2408, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2409, -1, 0, 1, 45 }, + { 0x0, 0x0, 220, 2410, -1, 0, 1, 40 }, + { 0x1, 0x1, 220, 2411, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2412, -1, 0, 1, 54 }, + { 0x1000001, 0x1000001, 220, 2413, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2414, -1, 36, 1, 54 }, + { 0x200001, 0x200001, 220, 2415, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2416, -1, 33, 1, 54 }, + { 0x1200001, 0x1200001, 220, 2417, -1, 12, 1, 49 }, + { 0x9, 0x9, 220, 2418, -1, 33, 1, 49 }, + { 0x0, 0x0, 220, 2419, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2420, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2421, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2422, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2423, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2424, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2425, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2426, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2427, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2428, -1, 0, 1, 54 }, + { 0x200001, 0x1200001, 220, 2429, -1, 12, 1, 59 }, + { 0x1, 0x9, 220, 2430, -1, 33, 1, 54 }, + { 0x0, 0x0, 220, 2431, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2432, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2433, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2434, -1, 0, 1, 54 }, + { 0x1, 0x1, 220, 2435, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2436, -1, 0, 1, 54 }, + { 0x1000001, 0x1000001, 220, 2437, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2438, -1, 36, 1, 54 }, + { 0x200001, 0x200001, 220, 2439, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2440, -1, 33, 1, 54 }, + { 0x1200001, 0x1200001, 220, 2441, -1, 12, 1, 49 }, + { 0x9, 0x9, 220, 2442, -1, 33, 1, 49 }, + { 0x0, 0x0, 220, 2443, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2444, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2445, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2446, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2447, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2448, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2449, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2450, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2451, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2452, -1, 0, 1, 54 }, + { 0x200001, 0x1200001, 220, 2453, -1, 12, 1, 59 }, + { 0x1, 0x9, 220, 2454, -1, 33, 1, 54 }, + { 0x0, 0x0, 220, 2455, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2456, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2457, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2458, -1, 0, 1, 54 }, + { 0x1, 0x1, 220, 2459, -1, 28, 1, 29 }, + { 0x0, 0x0, 220, 2460, -1, 0, 1, 29 }, + { 0x3, 0x3, 220, 2461, -1, 27, 1, 29 }, + { 0x1, 0x1, 220, 2462, -1, 27, 1, 29 }, + { 0x0, 0x0, 220, 2463, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2464, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2465, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2466, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2467, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2468, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2469, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2470, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2471, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2472, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 37, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2474, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2475, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2476, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2477, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2478, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2479, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2480, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2481, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 42, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2483, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2484, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 43, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2486, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2487, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2488, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2489, -1, 27, 1, 49 }, + { 0x1, 0x1, 220, 2490, -1, 28, 1, 49 }, + { 0x3, 0x3, 220, 2491, -1, 27, 1, 49 }, + { 0x1, 0x1, 220, 2492, -1, 29, 1, 49 }, + { 0x5, 0x5, 220, 2493, -1, 27, 1, 49 }, + { 0x3, 0x3, 220, 2494, -1, 28, 1, 49 }, + { 0x7, 0x7, 220, 2495, -1, 27, 1, 49 }, + { 0x0, 0x0, 220, 2496, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2497, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2498, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2499, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2500, -1, 28, 1, 29 }, + { 0x0, 0x0, 220, 2501, -1, 0, 1, 29 }, + { 0x3, 0x3, 220, 2502, -1, 27, 1, 29 }, + { 0x1, 0x1, 220, 2503, -1, 27, 1, 29 }, + { 0x0, 0x0, 220, 2504, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2505, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2506, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 52, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2508, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2509, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 57, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2511, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2512, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2513, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2514, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2515, -1, 0, 1, 35 }, + { 0x0, 0x0, 220, 2516, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2517, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 64, -1, 0, 1, 29 }, + { 0x1, 0x1, 221, 2519, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2520, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2521, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2522, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2523, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2524, -1, 34, 1, 46 }, + { 0x1, 0x1, 221, 2525, -1, 34, 1, 42 }, + { 0x400001, 0x400001, 221, 2526, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2527, -1, 34, 1, 56 }, + { 0x1400001, 0x1400001, 221, 2528, -1, 12, 1, 61 }, + { 0x5, 0x5, 221, 2529, -1, 34, 1, 56 }, + { 0x600001, 0x600001, 221, 2530, -1, 12, 1, 61 }, + { 0x3, 0x3, 221, 2531, -1, 33, 1, 56 }, + { 0x1600001, 0x1600001, 221, 2532, -1, 12, 1, 51 }, + { 0xb, 0xb, 221, 2533, -1, 33, 1, 51 }, + { 0x1, 0x1, 221, 2534, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2535, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2536, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2537, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2538, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2539, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2540, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2541, -1, 34, 1, 51 }, + { 0x400001, 0x400001, 221, 2542, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2543, -1, 34, 1, 56 }, + { 0x600001, 0x1600001, 221, 2544, -1, 12, 1, 61 }, + { 0x3, 0xb, 221, 2545, -1, 33, 1, 56 }, + { 0x1, 0x1, 221, 2546, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2547, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2548, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2549, -1, 34, 1, 56 }, + { 0x400001, 0x400001, 221, 2550, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2551, -1, 34, 1, 56 }, + { 0x1400001, 0x1400001, 221, 2552, -1, 12, 1, 61 }, + { 0x5, 0x5, 221, 2553, -1, 34, 1, 56 }, + { 0x600001, 0x600001, 221, 2554, -1, 12, 1, 61 }, + { 0x3, 0x3, 221, 2555, -1, 33, 1, 56 }, + { 0x1600001, 0x1600001, 221, 2556, -1, 12, 1, 51 }, + { 0xb, 0xb, 221, 2557, -1, 33, 1, 51 }, + { 0x1, 0x1, 221, 2558, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2559, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2560, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2561, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2562, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2563, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2564, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2565, -1, 34, 1, 51 }, + { 0x400001, 0x400001, 221, 2566, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2567, -1, 34, 1, 56 }, + { 0x600001, 0x1600001, 221, 2568, -1, 12, 1, 61 }, + { 0x3, 0xb, 221, 2569, -1, 33, 1, 56 }, + { 0x1, 0x1, 221, 2570, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2571, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2572, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2573, -1, 34, 1, 56 }, + { 0x41, 0x41, 221, 2574, -1, 28, 1, 31 }, + { 0x1, 0x1, 221, 2575, -1, 34, 1, 31 }, + { 0x83, 0x83, 221, 2576, -1, 27, 1, 31 }, + { 0x81, 0x81, 221, 2577, -1, 27, 1, 31 }, + { 0x1, 0x1, 221, 2578, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2579, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2580, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2581, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2582, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2583, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2584, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2585, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2586, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2587, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2588, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2589, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2590, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2591, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2592, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2593, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2594, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2595, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2596, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2597, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2598, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2599, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2600, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2601, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2602, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2603, -1, 34, 1, 51 }, + { 0x81, 0x81, 221, 2604, -1, 27, 1, 51 }, + { 0x41, 0x41, 221, 2605, -1, 28, 1, 51 }, + { 0x83, 0x83, 221, 2606, -1, 27, 1, 51 }, + { 0x21, 0x21, 221, 2607, -1, 29, 1, 51 }, + { 0x85, 0x85, 221, 2608, -1, 27, 1, 51 }, + { 0x43, 0x43, 221, 2609, -1, 28, 1, 51 }, + { 0x87, 0x87, 221, 2610, -1, 27, 1, 51 }, + { 0x1, 0x1, 221, 2611, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2612, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2613, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2614, -1, 34, 1, 51 }, + { 0x41, 0x41, 221, 2615, -1, 28, 1, 31 }, + { 0x1, 0x1, 221, 2616, -1, 34, 1, 31 }, + { 0x83, 0x83, 221, 2617, -1, 27, 1, 31 }, + { 0x81, 0x81, 221, 2618, -1, 27, 1, 31 }, + { 0x1, 0x1, 221, 2619, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2620, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2621, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2622, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2623, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2624, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2625, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2626, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2627, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2628, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2629, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2630, -1, 34, 1, 37 }, + { 0x1, 0x1, 221, 2631, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2632, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2633, -1, 34, 1, 31 }, + { 0x1, 0x1, 222, 2634, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2635, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2636, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2637, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2638, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2639, -1, 35, 1, 47 }, + { 0x1, 0x1, 222, 2640, -1, 35, 1, 43 }, + { 0x800001, 0x800001, 222, 2641, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2642, -1, 35, 1, 57 }, + { 0x1800001, 0x1800001, 222, 2643, -1, 12, 1, 62 }, + { 0x3, 0x3, 222, 2644, -1, 35, 1, 57 }, + { 0xa00001, 0xa00001, 222, 2645, -1, 12, 1, 62 }, + { 0x5, 0x5, 222, 2646, -1, 33, 1, 57 }, + { 0x1a00001, 0x1a00001, 222, 2647, -1, 12, 1, 52 }, + { 0xd, 0xd, 222, 2648, -1, 33, 1, 52 }, + { 0x1, 0x1, 222, 2649, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2650, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2651, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2652, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2653, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2654, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2655, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2656, -1, 35, 1, 52 }, + { 0x800001, 0x800001, 222, 2657, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2658, -1, 35, 1, 57 }, + { 0xa00001, 0x1a00001, 222, 2659, -1, 12, 1, 62 }, + { 0x5, 0xd, 222, 2660, -1, 33, 1, 57 }, + { 0x1, 0x1, 222, 2661, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2662, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2663, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2664, -1, 35, 1, 57 }, + { 0x800001, 0x800001, 222, 2665, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2666, -1, 35, 1, 57 }, + { 0x1800001, 0x1800001, 222, 2667, -1, 12, 1, 62 }, + { 0x3, 0x3, 222, 2668, -1, 35, 1, 57 }, + { 0xa00001, 0xa00001, 222, 2669, -1, 12, 1, 62 }, + { 0x5, 0x5, 222, 2670, -1, 33, 1, 57 }, + { 0x1a00001, 0x1a00001, 222, 2671, -1, 12, 1, 52 }, + { 0xd, 0xd, 222, 2672, -1, 33, 1, 52 }, + { 0x1, 0x1, 222, 2673, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2674, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2675, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2676, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2677, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2678, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2679, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2680, -1, 35, 1, 52 }, + { 0x800001, 0x800001, 222, 2681, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2682, -1, 35, 1, 57 }, + { 0xa00001, 0x1a00001, 222, 2683, -1, 12, 1, 62 }, + { 0x5, 0xd, 222, 2684, -1, 33, 1, 57 }, + { 0x1, 0x1, 222, 2685, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2686, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2687, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2688, -1, 35, 1, 57 }, + { 0x81, 0x81, 222, 2689, -1, 28, 1, 32 }, + { 0x1, 0x1, 222, 2690, -1, 35, 1, 32 }, + { 0x103, 0x103, 222, 2691, -1, 27, 1, 32 }, + { 0x101, 0x101, 222, 2692, -1, 27, 1, 32 }, + { 0x1, 0x1, 222, 2693, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2694, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2695, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2696, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2697, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2698, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2699, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2700, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2701, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2702, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2703, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2704, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2705, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2706, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2707, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2708, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2709, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2710, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2711, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2712, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2713, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2714, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2715, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2716, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2717, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2718, -1, 35, 1, 52 }, + { 0x101, 0x101, 222, 2719, -1, 27, 1, 52 }, + { 0x81, 0x81, 222, 2720, -1, 28, 1, 52 }, + { 0x103, 0x103, 222, 2721, -1, 27, 1, 52 }, + { 0x41, 0x41, 222, 2722, -1, 29, 1, 52 }, + { 0x105, 0x105, 222, 2723, -1, 27, 1, 52 }, + { 0x83, 0x83, 222, 2724, -1, 28, 1, 52 }, + { 0x107, 0x107, 222, 2725, -1, 27, 1, 52 }, + { 0x1, 0x1, 222, 2726, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2727, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2728, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2729, -1, 35, 1, 52 }, + { 0x81, 0x81, 222, 2730, -1, 28, 1, 32 }, + { 0x1, 0x1, 222, 2731, -1, 35, 1, 32 }, + { 0x103, 0x103, 222, 2732, -1, 27, 1, 32 }, + { 0x101, 0x101, 222, 2733, -1, 27, 1, 32 }, + { 0x1, 0x1, 222, 2734, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2735, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2736, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2737, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2738, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2739, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2740, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2741, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2742, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2743, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2744, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2745, -1, 35, 1, 38 }, + { 0x1, 0x1, 222, 2746, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2747, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2748, -1, 35, 1, 32 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2243, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 48 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 44 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2964, -1, 34, 1, 58 }, + { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2965, -1, 34, 1, 58 }, + { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2966, -1, 33, 1, 58 }, + { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 53 }, + { 0xf, 0xf, 223, 2967, -1, 33, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2968, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2969, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2970, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, 2971, -1, 34, 1, 53 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2976, -1, 34, 1, 58 }, + { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0xf, 223, 2977, -1, 33, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2978, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2979, -1, 34, 1, 58 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2982, -1, 34, 1, 58 }, + { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2983, -1, 34, 1, 58 }, + { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2984, -1, 33, 1, 58 }, + { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 53 }, + { 0xf, 0xf, 223, 2985, -1, 33, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2986, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2987, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2988, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, 2989, -1, 34, 1, 53 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2994, -1, 34, 1, 58 }, + { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0xf, 223, 2995, -1, 33, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2996, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2997, -1, 34, 1, 58 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 33 }, + { 0x3, 0x3, 223, 2862, -1, 34, 1, 33 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 33 }, + { 0x181, 0x181, 223, 2863, -1, 27, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2244, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2245, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2248, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2249, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2251, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2253, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2254, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x181, 0x181, 223, -1, -1, 27, 1, 53 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 53 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 53 }, + { 0x61, 0x61, 223, -1, -1, 29, 1, 53 }, + { 0x185, 0x185, 223, -1, -1, 27, 1, 53 }, + { 0xc3, 0xc3, 223, -1, -1, 28, 1, 53 }, + { 0x187, 0x187, 223, -1, -1, 27, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 33 }, + { 0x3, 0x3, 223, 2866, -1, 34, 1, 33 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 33 }, + { 0x181, 0x181, 223, 2867, -1, 27, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 39 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2256, -1, 34, 1, 33 }, + { 0x3, 0x3, 224, 540, 1451, 32, 1, 135 }, + { 0x3, 0x3, 224, 541, 1460, 32, 1, 135 }, + { 0x3, 0x3, 224, 542, 1469, 32, 1, 135 }, + { 0x3, 0x3, 224, 543, 1482, 32, 1, 135 }, + { 0x3, 0x3, 224, 544, 1491, 32, 1, 135 }, + { 0x3, 0x3, 224, 545, 1500, 32, 1, 135 }, + { 0x3, 0x3, 224, 546, 1509, 32, 1, 135 }, + { 0x3, 0x3, 224, 547, 1518, 32, 1, 135 }, + { 0x3, 0x3, 224, 548, 1527, 32, 1, 135 }, + { 0x3, 0x3, 224, 549, 1536, 32, 1, 135 }, + { 0x3, 0x3, 224, 550, 1546, 32, 1, 135 }, + { 0x3, 0x3, 224, 551, 1556, 32, 1, 135 }, + { 0x3, 0x3, 224, 564, 1569, 32, 1, 150 }, + { 0x3, 0x3, 224, 565, 1575, 32, 1, 155 }, + { 0x3, 0x3, 224, 566, 1581, 32, 1, 155 }, + { 0x3, 0x3, 224, 567, 1587, 32, 1, 150 }, + { 0x3, 0x3, 224, 568, 1593, 32, 1, 155 }, + { 0x3, 0x3, 224, 569, 1599, 32, 1, 155 }, + { 0x3, 0x3, 224, 570, 1605, 32, 1, 150 }, + { 0x3, 0x3, 224, 571, 1611, 32, 1, 155 }, + { 0x3, 0x3, 224, 572, 1617, 32, 1, 155 }, + { 0x3, 0x3, 224, 573, 1623, 32, 1, 150 }, + { 0x3, 0x3, 224, 574, 1629, 32, 1, 155 }, + { 0x3, 0x3, 224, 575, 1635, 32, 1, 150 }, + { 0x3, 0x3, 224, 576, 1641, 32, 1, 155 }, + { 0x3, 0x3, 224, 577, 1647, 32, 1, 150 }, + { 0x3, 0x3, 224, 578, 1653, 32, 1, 155 }, + { 0x3, 0x3, 224, 579, 1659, 32, 1, 150 }, + { 0x3, 0x3, 224, 580, 1665, 32, 1, 155 }, + { 0x3, 0x3, 224, 581, 1671, 32, 1, 155 }, + { 0x1, 0x1, 225, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 225, -1, -1, 28, 1, 34 }, + { 0x0, 0x0, 232, 958, -1, 0, 1, 144 }, + { 0x0, 0x0, 232, 959, -1, 0, 1, 160 }, + { 0x1, 0x1, 233, -1, 1982, 33, 1, 140 }, + { 0x1, 0x1, 233, -1, 1985, 33, 1, 146 }, + { 0x0, 0x0, 233, -1, 1987, 0, 1, 157 }, + { 0x0, 0x0, 233, -1, 1988, 0, 1, 161 }, + { 0x0, 0x0, 234, 883, 971, 0, 0, -1 }, + { 0x0, 0x0, 234, 884, 979, 0, 0, -1 }, + { 0x0, 0x0, 234, 885, 975, 0, 0, -1 }, + { 0x1, 0x1, 234, 886, 620, 33, 1, 6 }, + { 0x8000001, 0x8000001, 234, 887, 628, 6, 1, 7 }, + { 0x1, 0x1, 234, 888, 624, 33, 1, 6 }, + { 0x0, 0x0, 234, 889, 983, 0, 0, -1 }, + { 0x1, 0x1, 234, 890, 640, 33, 1, 8 }, + { 0x0, 0x0, 234, 891, 987, 0, 0, -1 }, + { 0x1, 0x1, 234, 892, 652, 33, 1, 16 }, + { 0x0, 0x0, 234, 893, 992, 0, 0, -1 }, + { 0x0, 0x0, 234, 894, 996, 0, 0, -1 }, + { 0x1, 0x1, 234, 895, 675, 33, 1, 18 }, + { 0x1, 0x1, 234, 896, 679, 33, 1, 18 }, + { 0x0, 0x0, 234, 897, 1000, 0, 0, -1 }, + { 0x0, 0x0, 234, 898, 1004, 0, 0, -1 }, + { 0x1, 0x1, 234, 899, 699, 33, 1, 19 }, + { 0x8000001, 0x8000001, 234, 900, 703, 6, 1, 19 }, + { 0x0, 0x0, 234, 901, 1008, 0, 0, -1 }, + { 0x1, 0x1, 234, 902, 715, 33, 1, 20 }, + { 0x0, 0x0, 234, 903, 1012, 0, 0, -1 }, + { 0x0, 0x0, 234, 904, 1016, 0, 0, -1 }, + { 0x1, 0x1, 234, 905, 735, 33, 1, 21 }, + { 0x8000001, 0x8000001, 234, 906, 739, 6, 1, 21 }, + { 0x0, 0x0, 234, 907, 1020, 0, 0, -1 }, + { 0x1, 0x1, 234, 908, 751, 33, 1, 22 }, + { 0x0, 0x0, 234, 909, 1025, 0, 0, -1 }, + { 0x0, 0x0, 234, 910, 1029, 0, 0, -1 }, + { 0x1, 0x1, 234, 911, 774, 33, 1, 18 }, + { 0x1, 0x1, 234, 912, 778, 33, 1, 18 }, + { 0x0, 0x0, 234, 913, 1033, 0, 0, -1 }, + { 0x1, 0x1, 234, 914, 790, 33, 1, 22 }, + { 0x0, 0x0, 235, 2787, 970, 0, 0, -1 }, + { 0x0, 0x0, 235, 2788, 978, 0, 0, -1 }, + { 0x0, 0x0, 235, 2789, 974, 0, 0, -1 }, + { 0x0, 0x0, 235, 2790, 619, 0, 1, 6 }, + { 0x1, 0x1, 235, 2791, 627, 6, 1, 7 }, + { 0x0, 0x0, 235, 2792, 623, 0, 1, 6 }, + { 0x0, 0x0, 235, 2793, 982, 0, 0, -1 }, + { 0x0, 0x0, 235, 2794, 639, 0, 1, 8 }, + { 0x0, 0x0, 235, 2795, 986, 0, 0, -1 }, + { 0x0, 0x0, 235, 2796, 651, 0, 1, 16 }, + { 0x0, 0x0, 235, 2797, 991, 0, 0, -1 }, + { 0x0, 0x0, 235, 2798, 995, 0, 0, -1 }, + { 0x0, 0x0, 235, 2799, 674, 0, 1, 18 }, + { 0x0, 0x0, 235, 2800, 678, 0, 1, 18 }, + { 0x0, 0x0, 235, 2801, 999, 0, 0, -1 }, + { 0x0, 0x0, 235, 2802, 1003, 0, 0, -1 }, + { 0x0, 0x0, 235, 2803, 698, 0, 1, 19 }, + { 0x1, 0x1, 235, 2804, 702, 6, 1, 19 }, + { 0x0, 0x0, 235, 2805, 1007, 0, 0, -1 }, + { 0x0, 0x0, 235, 2806, 714, 0, 1, 20 }, + { 0x0, 0x0, 235, 2807, 1011, 0, 0, -1 }, + { 0x0, 0x0, 235, 2808, 1015, 0, 0, -1 }, + { 0x0, 0x0, 235, 2809, 734, 0, 1, 21 }, + { 0x1, 0x1, 235, 2810, 738, 6, 1, 21 }, + { 0x0, 0x0, 235, 2811, 1019, 0, 0, -1 }, + { 0x0, 0x0, 235, 2812, 750, 0, 1, 22 }, + { 0x0, 0x0, 235, 2813, 1024, 0, 0, -1 }, + { 0x0, 0x0, 235, 2814, 1028, 0, 0, -1 }, + { 0x0, 0x0, 235, 2815, 773, 0, 1, 18 }, + { 0x0, 0x0, 235, 2816, 777, 0, 1, 18 }, + { 0x0, 0x0, 235, 2817, 1032, 0, 0, -1 }, + { 0x0, 0x0, 235, 2818, 789, 0, 1, 22 }, + { 0x1, 0x1, 235, 915, 1155, 27, 1, 17 }, + { 0x0, 0x0, 235, 916, 1153, 0, 1, 17 }, + { 0x0, 0x0, 235, 1220, 1157, 0, 1, 23 }, + { 0x0, 0x1, 235, 1165, 1163, 20, 1, 68 }, + { 0x0, 0x0, 235, 111, 1161, 0, 1, 68 }, + { 0x1, 0x1, 238, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 238, -1, -1, 0, 1, 0 }, + { 0x1, 0x1, 238, 3022, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3023, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3024, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3025, -1, 27, 1, 0 }, + { 0x0, 0x0, 261, -1, 2344, 0, 0, -1 }, + { 0x0, 0x0, 261, -1, 2346, 0, 0, -1 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x0, 0x0, 261, -1, 2385, 0, 0, -1 }, + { 0x0, 0x0, 261, -1, 2387, 0, 0, -1 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x0, 0x0, 263, 23, -1, 0, 1, 0 }, + { 0x0, 0x0, 263, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 263, -1, -1, 0, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 263, 180, -1, 0, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 301, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 323, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 349, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 371, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x0, 0x0, 264, -1, 2296, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2298, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2300, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2302, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2304, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2306, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2308, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2310, 12, 1, 50 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 50 }, + { 0x0, 0x0, 264, -1, 2312, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2314, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2316, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2318, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x0, 0x0, 264, -1, 2320, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2322, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2324, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2326, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2328, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2330, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2332, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2334, 12, 1, 50 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 50 }, + { 0x0, 0x0, 264, -1, 2336, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2338, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2340, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2342, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, 393, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 395, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 517, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 519, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 401, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 403, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 525, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 527, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 409, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 411, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 533, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 535, -1, 12, 1, 2 }, + { 0x0, 0x0, 265, -1, 2303, 0, 0, -1 }, + { 0x9, 0x9, 265, -1, 2311, 33, 1, 50 }, + { 0x9, 0x9, 265, -1, 2975, 33, 1, 50 }, + { 0x0, 0x0, 265, 1399, 2376, 0, 0, -1 }, + { 0x3, 0x3, 265, 1400, -1, 27, 1, 50 }, + { 0x0, 0x0, 269, 2856, -1, 0, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 271, 3018, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3019, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3020, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3021, -1, 28, 1, 0 }, + { 0x1, 0x1, 273, -1, -1, 27, 1, 100 }, + { 0x1, 0x1, 273, -1, -1, 27, 1, 100 }, + { 0x0, 0x0, 273, -1, 968, 0, 0, -1 }, + { 0x0, 0x0, 274, 3031, 2833, 0, 0, -1 }, + { 0x0, 0x0, 274, 3032, 2835, 0, 0, -1 }, + { 0x0, 0x0, 275, -1, 2834, 0, 0, -1 }, + { 0x0, 0x0, 275, -1, 2836, 0, 0, -1 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 281, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 285, -1, 2350, 0, 1, 30 }, + { 0x0, 0x0, 286, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 286, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 286, 2001, 3000, 0, 1, 1 }, + { 0x0, 0x0, 286, 2002, 3001, 0, 1, 1 }, + { 0x0, 0x0, 286, -1, 518, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 520, 0, 0, -1 }, + { 0x0, 0x0, 286, 2005, 3004, 0, 1, 76 }, + { 0x0, 0x0, 286, 2006, 3005, 0, 1, 76 }, + { 0x0, 0x0, 286, -1, 526, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 528, 0, 0, -1 }, + { 0x0, 0x0, 286, 2009, 3008, 0, 1, 1 }, + { 0x0, 0x0, 286, 2010, 3009, 0, 1, 1 }, + { 0x0, 0x0, 286, -1, 534, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 536, 0, 0, -1 }, +}; + +static const struct ia64_main_table +main_table[] = { + { 5, 1, 1, 0x0000010000000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 0, }, + { 5, 1, 1, 0x0000010008000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 1, }, + { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 67, 27, 0, 0 }, 0x0, 2, }, + { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 64, 26, 0, 0 }, 0x0, 3, }, + { 6, 1, 1, 0x0000012000000000ull, 0x000001e000000000ull, { 24, 67, 27, 0, 0 }, 0x0, 4, }, + { 7, 1, 1, 0x0000010040000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 5, }, + { 7, 1, 1, 0x0000010c00000000ull, 0x000001ee00000000ull, { 24, 64, 26, 0, 0 }, 0x0, 6, }, + { 8, 1, 1, 0x0000010800000000ull, 0x000001ee00000000ull, { 24, 64, 26, 0, 0 }, 0x0, 7, }, + { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 3, 53, 54, 55 }, 0x221, 8, }, + { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 53, 54, 55, 0 }, 0x261, 9, }, + { 10, 1, 1, 0x0000010060000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 10, }, + { 10, 1, 1, 0x0000010160000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 11, }, + { 11, 1, 1, 0x0000010068000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 12, }, + { 11, 1, 1, 0x0000010168000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 13, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011ffull, { 16, 0, 0, 0, 0 }, 0x40, 969, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x0, 825, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x40, 826, }, + { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x200, 2234, }, + { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x240, 2235, }, + { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x0, 582, }, + { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x40, 583, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011ffull, { 82, 0, 0, 0, 0 }, 0x40, 990, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x0, 827, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x40, 828, }, + { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x210, 3029, }, + { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x250, 3030, }, + { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x30, 590, }, + { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x70, 591, }, + { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x230, 588, }, + { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x270, 589, }, + { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x0, 584, }, + { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x40, 585, }, + { 15, 4, 0, 0x0000000000000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 537, }, + { 15, 5, 0, 0x0000000000000000ull, 0x000001e3f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 960, }, + { 15, 2, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1138, }, + { 15, 3, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1263, }, + { 15, 6, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3033, }, + { 15, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 16, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011ffull, { 83, 0, 0, 0, 0 }, 0x40, 1023, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 83, 0, 0, 0, 0 }, 0x0, 829, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 83, 0, 0, 0, 0 }, 0x40, 830, }, + { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 83, 0, 0, 0 }, 0x0, 586, }, + { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 83, 0, 0, 0 }, 0x40, 587, }, + { 17, 4, 0, 0x0000004080000000ull, 0x000001e9f8000018ull, { 16, 78, 0, 0, 0 }, 0x20, 2852, }, + { 17, 4, 0, 0x000000e000000000ull, 0x000001e800000018ull, { 82, 78, 0, 0, 0 }, 0x20, 2853, }, + { 18, 4, 0, 0x0000000060000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x2c, 222, }, + { 22, 2, 0, 0x0000000200000000ull, 0x000001ee00000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2239, }, + { 22, 3, 0, 0x0000000800000000ull, 0x000001ee00000000ull, { 24, 82, 0, 0, 0 }, 0x0, 226, }, + { 22, 3, 0, 0x0000000c00000000ull, 0x000001ee00000000ull, { 18, 82, 0, 0, 0 }, 0x0, 227, }, + { 22, 3, 0, 0x0000002200000000ull, 0x000001ee00000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2240, }, + { 22, 3, 0, 0x0000002600000000ull, 0x000001ee00000000ull, { 19, 81, 0, 0, 0 }, 0x0, 2241, }, + { 22, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2242, }, + { 25, 4, 0, 0x0000000020000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 18, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1222, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1223, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1181, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1182, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1090, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1091, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1052, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1053, }, + { 26, 1, 2, 0x0000018200000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1376, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1092, }, + { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1093, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1226, }, + { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1227, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1187, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1229, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1230, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1188, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1189, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1097, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1098, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1059, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1060, }, + { 26, 1, 2, 0x0000018a00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x40, 1381, }, + { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 23, 60, 26, 0 }, 0x0, 1214, }, + { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 60, 26, 0, 0 }, 0x40, 1215, }, + { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 22, 60, 26, 0 }, 0x0, 1125, }, + { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 60, 26, 0, 0 }, 0x40, 1126, }, + { 26, 1, 2, 0x000001c200000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1382, }, + { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1190, }, + { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1191, }, + { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1063, }, + { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1064, }, + { 26, 1, 2, 0x000001ca00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x40, 1383, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1235, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1236, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1194, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1195, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1103, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1104, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1065, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1066, }, + { 27, 1, 2, 0x0000018600000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1388, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1105, }, + { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1106, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1239, }, + { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1240, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1200, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1242, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1243, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1201, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1202, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1110, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1111, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1072, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1073, }, + { 27, 1, 2, 0x0000018e00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x40, 1393, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 57, 26, 0 }, 0x0, 1259, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 57, 26, 0, 0 }, 0x40, 1260, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 59, 26, 0 }, 0x0, 1218, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 59, 26, 0, 0 }, 0x40, 1219, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 59, 26, 0 }, 0x0, 1129, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 59, 26, 0, 0 }, 0x40, 1130, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 57, 26, 0 }, 0x0, 1088, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 57, 26, 0, 0 }, 0x40, 1089, }, + { 27, 1, 2, 0x000001c600000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1394, }, + { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1203, }, + { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1204, }, + { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1076, }, + { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1077, }, + { 27, 1, 2, 0x000001ce00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x40, 1395, }, + { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 28, 25, 1, 2 }, 0x0, 259, }, + { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 260, }, + { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 261, }, + { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 262, }, + { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 263, }, + { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 264, }, + { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 265, }, + { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 266, }, + { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 267, }, + { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 268, }, + { 34, 4, 0, 0x0000000010000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 19, }, + { 36, 2, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1167, }, + { 37, 2, 1, 0x00000000c8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1168, }, + { 39, 2, 1, 0x0000008000000000ull, 0x000001e000000000ull, { 24, 25, 26, 47, 73 }, 0x0, 20, }, + { 39, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 74, 0 }, 0x0, 3038, }, + { 39, 2, 1, 0x000000a604000000ull, 0x000001ee04000000ull, { 24, 56, 45, 74, 0 }, 0x0, 3039, }, + { 39, 2, 1, 0x000000ae00000000ull, 0x000001ee00000000ull, { 24, 48, 26, 46, 74 }, 0x0, 21, }, + { 43, 4, 0, 0x0000000080000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x20, 22, }, + { 48, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 77, 74, 0 }, 0x0, 2870, }, + { 50, 5, 1, 0x0000000080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 24, }, + { 51, 5, 1, 0x0000010008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2291, }, + { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2292, }, + { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 26, }, + { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2293, }, + { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 27, }, + { 54, 5, 1, 0x0000000160000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 28, }, + { 55, 5, 1, 0x0000000168000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 29, }, + { 57, 3, 0, 0x0000002180000000ull, 0x000001fff8000000ull, { 26, 0, 0, 0, 0 }, 0x0, 30, }, + { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 80, 0, 0, 0, 0 }, 0x0, 2294, }, + { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 80, 0, 0, 0, 0 }, 0x40, 31, }, + { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 23, 19, 61, 0 }, 0x0, 1265, }, + { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 19, 61, 0, 0 }, 0x40, 1266, }, + { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 22, 19, 61, 0 }, 0x40, 1420, }, + { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 19, 61, 0, 0 }, 0x40, 1421, }, + { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 2295, }, + { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x40, 32, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x0, 943, }, + { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 19, 20, 0, 0 }, 0x40, 944, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x40, 945, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x0, 1116, }, + { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 20, 19, 0, 0 }, 0x40, 1117, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x40, 1118, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x0, 1396, }, + { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 19, 20, 0, 0 }, 0x40, 1397, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x40, 1398, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x0, 1405, }, + { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 20, 19, 0, 0 }, 0x40, 1406, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x40, 1407, }, + { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1042, }, + { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1043, }, + { 62, 5, 1, 0x00000000e0000000ull, 0x000001e3f8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 3036, }, + { 62, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 3037, }, + { 63, 3, 1, 0x0000008488000000ull, 0x000001fff8000000ull, { 24, 28, 72, 0, 0 }, 0x0, 269, }, + { 64, 3, 1, 0x00000084c8000000ull, 0x000001fff8000000ull, { 24, 28, 72, 0, 0 }, 0x0, 270, }, + { 67, 3, 0, 0x0000000060000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 33, }, + { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2353, }, + { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 34, }, + { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2354, }, + { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 35, }, + { 70, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2247, }, + { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2355, }, + { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 36, }, + { 72, 5, 1, 0x00000001c8000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 1221, }, + { 73, 5, 1, 0x0000010000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2358, }, + { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2361, }, + { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 38, }, + { 75, 5, 1, 0x0000000088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 39, }, + { 76, 5, 1, 0x0000000088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 40, }, + { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2364, }, + { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 41, }, + { 78, 5, 1, 0x0000018000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2367, }, + { 79, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 2370, }, + { 80, 5, 1, 0x0000000170000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 44, }, + { 81, 5, 1, 0x0000002080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 45, }, + { 82, 5, 1, 0x0000000140000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 46, }, + { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2371, }, + { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 47, }, + { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2372, }, + { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 48, }, + { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 946, }, + { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 947, }, + { 85, 5, 1, 0x0000002188000000ull, 0x000001eff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 1119, }, + { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1044, }, + { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1045, }, + { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2389, }, + { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 49, }, + { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2390, }, + { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 50, }, + { 89, 5, 1, 0x0000002080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2255, }, + { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2391, }, + { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 51, }, + { 91, 5, 1, 0x0000013000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2392, }, + { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2393, }, + { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 53, }, + { 93, 5, 1, 0x0000002088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 54, }, + { 94, 5, 1, 0x0000002088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 55, }, + { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2394, }, + { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 56, }, + { 96, 5, 1, 0x000001b000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2395, }, + { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2396, }, + { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 58, }, + { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2397, }, + { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 59, }, + { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2398, }, + { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 60, }, + { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2399, }, + { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 61, }, + { 101, 5, 1, 0x000001c000000000ull, 0x000001f000000000ull, { 18, 20, 21, 19, 0 }, 0x0, 62, }, + { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 51, 52, 0, 0, 0 }, 0x0, 2400, }, + { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 51, 52, 0, 0, 0 }, 0x40, 63, }, + { 103, 5, 1, 0x0000014008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2403, }, + { 104, 5, 1, 0x00000001a0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 65, }, + { 105, 5, 1, 0x00000001e0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2202, }, + { 106, 3, 0, 0x0000000100000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 66, }, + { 108, 5, 1, 0x0000000178000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 67, }, + { 113, 3, 1, 0x0000008708000000ull, 0x000001ffc8000000ull, { 24, 19, 0, 0, 0 }, 0x0, 2781, }, + { 118, 4, 0, 0x0000004008000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 538, }, + { 118, 5, 0, 0x000000000c000000ull, 0x000001e3fc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 961, }, + { 118, 2, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1141, }, + { 118, 3, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1267, }, + { 118, 6, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3034, }, + { 118, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 68, }, + { 123, 3, 0, 0x0000000080000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 69, }, + { 123, 3, 0, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 0, 0, 0, 0 }, 0x0, 920, }, + { 123, 3, 0, 0x0000000098000000ull, 0x000001eff8000000ull, { 18, 0, 0, 0, 0 }, 0x0, 921, }, + { 124, 3, 0, 0x0000002170000000ull, 0x000001eff8000000ull, { 25, 0, 0, 0, 0 }, 0xc, 846, }, + { 125, 3, 1, 0x0000002070000000ull, 0x000001eff8000000ull, { 31, 25, 0, 0, 0 }, 0x8, 847, }, + { 125, 3, 1, 0x0000002078000000ull, 0x000001eff8000000ull, { 32, 25, 0, 0, 0 }, 0x8, 1143, }, + { 127, 3, 1, 0x0000008000000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 70, }, + { 127, 3, 1, 0x0000009000000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 71, }, + { 127, 3, 1, 0x000000a000000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 72, }, + { 128, 3, 2, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 1, 28, 0, 0 }, 0x0, 73, }, + { 128, 3, 1, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x40, 74, }, + { 129, 3, 1, 0x0000008040000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 75, }, + { 129, 3, 1, 0x0000009040000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 76, }, + { 129, 3, 1, 0x000000a040000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 77, }, + { 130, 3, 1, 0x0000008080000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 78, }, + { 130, 3, 1, 0x0000009080000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 79, }, + { 130, 3, 1, 0x000000a080000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 80, }, + { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 81, }, + { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 28, 84, 0, 0 }, 0x0, 1339, }, + { 131, 3, 1, 0x00000090c0000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 82, }, + { 131, 3, 1, 0x000000a0c0000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 83, }, + { 132, 3, 1, 0x000000c6c0000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 1039, }, + { 132, 3, 1, 0x000000d6c0000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 1040, }, + { 132, 3, 1, 0x000000e6c0000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 1041, }, + { 133, 3, 1, 0x000000c040000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 84, }, + { 133, 3, 1, 0x000000d040000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 85, }, + { 133, 3, 1, 0x000000e040000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 86, }, + { 134, 3, 1, 0x000000c0c0000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 87, }, + { 134, 3, 1, 0x000000d0c0000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 88, }, + { 134, 3, 1, 0x000000e0c0000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 89, }, + { 135, 3, 1, 0x000000c000000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 90, }, + { 135, 3, 1, 0x000000d000000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 91, }, + { 135, 3, 1, 0x000000e000000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 92, }, + { 136, 3, 2, 0x000000c048000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 93, }, + { 136, 3, 2, 0x000000d048000000ull, 0x000001fff8000000ull, { 18, 19, 28, 6, 0 }, 0x400, 94, }, + { 137, 3, 2, 0x000000c0c8000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 95, }, + { 137, 3, 2, 0x000000d0c8000000ull, 0x000001fff8000000ull, { 18, 19, 28, 6, 0 }, 0x400, 96, }, + { 138, 3, 2, 0x000000c088000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 97, }, + { 138, 3, 2, 0x000000d088000000ull, 0x000001fff8000000ull, { 18, 19, 28, 5, 0 }, 0x400, 98, }, + { 139, 3, 1, 0x000000c080000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 99, }, + { 139, 3, 1, 0x000000d080000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 100, }, + { 139, 3, 1, 0x000000e080000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 101, }, + { 142, 3, 0, 0x000000cb00000000ull, 0x000001fff8000000ull, { 28, 0, 0, 0, 0 }, 0x0, 102, }, + { 142, 3, 0, 0x000000db00000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x400, 103, }, + { 142, 3, 0, 0x000000eb00000000ull, 0x000001eff0000000ull, { 28, 63, 0, 0, 0 }, 0x400, 104, }, + { 143, 3, 0, 0x0000000050000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 105, }, + { 151, 3, 0, 0x0000000110000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 106, }, + { 152, 2, 1, 0x000000e880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2203, }, + { 153, 2, 1, 0x000000ea80000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2204, }, + { 154, 2, 1, 0x000000f880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2205, }, + { 155, 1, 1, 0x0000010800000000ull, 0x000001fff80fe000ull, { 24, 26, 0, 0, 0 }, 0x0, 107, }, + { 155, 1, 1, 0x0000012000000000ull, 0x000001e000300000ull, { 24, 67, 0, 0, 0 }, 0x40, 108, }, + { 155, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 109, }, + { 155, 2, 1, 0x0000000e00100000ull, 0x000001ee00f00000ull, { 15, 25, 0, 0, 0 }, 0x40, 110, }, + { 155, 2, 1, 0x0000000e00000000ull, 0x000001ee00f00000ull, { 15, 25, 79, 0, 0 }, 0x0, 2855, }, + { 155, 2, 1, 0x0000000188000000ull, 0x000001eff8000000ull, { 24, 16, 0, 0, 0 }, 0x0, 112, }, + { 155, 2, 1, 0x0000000600000000ull, 0x000001ee00000000ull, { 9, 25, 65, 0, 0 }, 0x0, 113, }, + { 155, 2, 1, 0x00000016ff001fc0ull, 0x000001feff001fc0ull, { 9, 25, 0, 0, 0 }, 0x40, 114, }, + { 155, 2, 1, 0x0000000400000000ull, 0x000001ee00000000ull, { 10, 69, 0, 0, 0 }, 0x0, 115, }, + { 155, 2, 1, 0x0000000180000000ull, 0x000001eff8000000ull, { 24, 8, 0, 0, 0 }, 0x0, 116, }, + { 155, 2, 1, 0x0000000198000000ull, 0x000001eff8000000ull, { 24, 9, 0, 0, 0 }, 0x0, 117, }, + { 155, 2, 1, 0x0000000150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1144, }, + { 155, 2, 1, 0x0000000050000000ull, 0x000001eff8000000ull, { 14, 56, 0, 0, 0 }, 0x0, 1145, }, + { 155, 2, 1, 0x0000000190000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1146, }, + { 155, 3, 1, 0x0000000140000000ull, 0x000001eff8000000ull, { 14, 56, 0, 0, 0 }, 0x0, 1268, }, + { 155, 3, 1, 0x0000002150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1269, }, + { 155, 3, 1, 0x0000002110000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1270, }, + { 155, 3, 1, 0x0000002160000000ull, 0x000001eff8000000ull, { 17, 25, 0, 0, 0 }, 0x8, 118, }, + { 155, 3, 1, 0x0000002120000000ull, 0x000001eff8000000ull, { 24, 17, 0, 0, 0 }, 0x8, 119, }, + { 155, 3, 1, 0x0000002168000000ull, 0x000001eff8000000ull, { 12, 25, 0, 0, 0 }, 0x8, 120, }, + { 155, 3, 1, 0x0000002148000000ull, 0x000001eff8000000ull, { 13, 25, 0, 0, 0 }, 0x0, 121, }, + { 155, 3, 1, 0x0000002128000000ull, 0x000001eff8000000ull, { 24, 11, 0, 0, 0 }, 0x8, 122, }, + { 155, 3, 1, 0x0000002108000000ull, 0x000001eff8000000ull, { 24, 13, 0, 0, 0 }, 0x0, 123, }, + { 155, 3, 1, 0x0000002000000000ull, 0x000001eff8000000ull, { 38, 25, 0, 0, 0 }, 0x8, 124, }, + { 155, 3, 1, 0x0000002008000000ull, 0x000001eff8000000ull, { 30, 25, 0, 0, 0 }, 0x8, 125, }, + { 155, 3, 1, 0x0000002010000000ull, 0x000001eff8000000ull, { 33, 25, 0, 0, 0 }, 0x8, 126, }, + { 155, 3, 1, 0x0000002018000000ull, 0x000001eff8000000ull, { 35, 25, 0, 0, 0 }, 0x8, 127, }, + { 155, 3, 1, 0x0000002020000000ull, 0x000001eff8000000ull, { 36, 25, 0, 0, 0 }, 0x8, 128, }, + { 155, 3, 1, 0x0000002028000000ull, 0x000001eff8000000ull, { 37, 25, 0, 0, 0 }, 0x8, 129, }, + { 155, 3, 1, 0x0000002030000000ull, 0x000001eff8000000ull, { 34, 25, 0, 0, 0 }, 0x8, 130, }, + { 155, 3, 1, 0x0000002080000000ull, 0x000001eff8000000ull, { 24, 38, 0, 0, 0 }, 0x8, 131, }, + { 155, 3, 1, 0x0000002088000000ull, 0x000001eff8000000ull, { 24, 30, 0, 0, 0 }, 0x8, 132, }, + { 155, 3, 1, 0x0000002090000000ull, 0x000001eff8000000ull, { 24, 33, 0, 0, 0 }, 0x8, 133, }, + { 155, 3, 1, 0x0000002098000000ull, 0x000001eff8000000ull, { 24, 35, 0, 0, 0 }, 0x8, 134, }, + { 155, 3, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 24, 36, 0, 0, 0 }, 0x8, 135, }, + { 155, 3, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 24, 37, 0, 0, 0 }, 0x0, 136, }, + { 155, 3, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 24, 34, 0, 0, 0 }, 0x8, 137, }, + { 155, 3, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 24, 29, 0, 0, 0 }, 0x0, 138, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 14, 0, 0, 0 }, 0x0, 139, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 56, 0, 0, 0 }, 0x0, 140, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 25, 0, 0, 0 }, 0x0, 141, }, + { 156, 6, 1, 0x000000c000000000ull, 0x000001e000100000ull, { 24, 71, 0, 0, 0 }, 0x0, 142, }, + { 157, 2, 1, 0x000000eca0000000ull, 0x000001fff0000000ull, { 24, 25, 75, 0, 0 }, 0x0, 143, }, + { 158, 2, 1, 0x000000eea0000000ull, 0x000001fff0000000ull, { 24, 25, 76, 0, 0 }, 0x0, 144, }, + { 168, 4, 0, 0x0000004000000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 539, }, + { 168, 5, 0, 0x0000000008000000ull, 0x000001e3fc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 962, }, + { 168, 2, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1147, }, + { 168, 3, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1271, }, + { 168, 6, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3035, }, + { 168, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 145, }, + { 175, 1, 1, 0x0000010070000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 146, }, + { 175, 1, 1, 0x0000010170000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 147, }, + { 178, 2, 1, 0x000000ea00000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 3017, }, + { 179, 2, 1, 0x000000f820000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2857, }, + { 180, 1, 1, 0x0000010400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 148, }, + { 181, 1, 1, 0x0000010600000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 149, }, + { 182, 1, 1, 0x0000011400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 150, }, + { 183, 1, 1, 0x0000010450000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 151, }, + { 184, 1, 1, 0x0000010650000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 152, }, + { 185, 1, 1, 0x0000010470000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 153, }, + { 186, 1, 1, 0x0000010670000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 154, }, + { 187, 1, 1, 0x0000010520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 948, }, + { 188, 1, 1, 0x0000010720000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 949, }, + { 189, 1, 1, 0x0000011520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 950, }, + { 190, 2, 1, 0x000000e850000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2871, }, + { 191, 2, 1, 0x000000ea70000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 155, }, + { 192, 2, 1, 0x000000e810000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2872, }, + { 193, 2, 1, 0x000000ea30000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 156, }, + { 194, 2, 1, 0x000000ead0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2206, }, + { 195, 2, 1, 0x000000e230000000ull, 0x000001ff30000000ull, { 24, 25, 26, 42, 0 }, 0x0, 157, }, + { 196, 2, 1, 0x000000e690000000ull, 0x000001fff0000000ull, { 24, 26, 0, 0, 0 }, 0x0, 158, }, + { 198, 3, 1, 0x00000021c0000000ull, 0x000001eff8000000ull, { 24, 26, 25, 0, 0 }, 0x0, 2207, }, + { 198, 3, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 24, 26, 49, 0, 0 }, 0x0, 2208, }, + { 198, 3, 0, 0x0000002188000000ull, 0x000001eff8000000ull, { 26, 49, 0, 0, 0 }, 0x0, 2238, }, + { 199, 2, 1, 0x000000e8b0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 159, }, + { 200, 2, 1, 0x000000e240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 160, }, + { 200, 2, 1, 0x000000ee50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 161, }, + { 201, 2, 1, 0x000000f040000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 162, }, + { 201, 2, 1, 0x000000fc50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 163, }, + { 202, 1, 1, 0x0000010680000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 164, }, + { 203, 2, 1, 0x000000e220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 165, }, + { 203, 2, 1, 0x000000e630000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 166, }, + { 204, 2, 1, 0x000000f020000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 167, }, + { 204, 2, 1, 0x000000f430000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 168, }, + { 205, 1, 1, 0x00000106c0000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 169, }, + { 206, 1, 1, 0x0000010420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 170, }, + { 207, 1, 1, 0x0000010620000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 171, }, + { 208, 1, 1, 0x0000011420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 172, }, + { 209, 3, 0, 0x0000002048000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 1175, }, + { 209, 3, 0, 0x0000002050000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0xc, 1050, }, + { 209, 3, 0, 0x00000021a0000000ull, 0x000001eff8000000ull, { 26, 0, 0, 0, 0 }, 0x8, 922, }, + { 210, 3, 0, 0x0000002060000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 848, }, + { 215, 4, 0, 0x0000000040000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x22c, 173, }, + { 216, 3, 0, 0x0000000038000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x8, 174, }, + { 217, 3, 0, 0x0000000028000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x0, 175, }, + { 226, 3, 1, 0x000000c708000000ull, 0x000001ffc8000000ull, { 18, 25, 0, 0, 0 }, 0x0, 2782, }, + { 227, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 0, 0 }, 0x140, 176, }, + { 227, 2, 1, 0x000000f240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 177, }, + { 228, 1, 1, 0x0000010080000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 178, }, + { 229, 1, 1, 0x00000100c0000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 179, }, + { 230, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 77, 0, 0 }, 0x140, 2878, }, + { 230, 2, 1, 0x000000f220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 181, }, + { 231, 2, 1, 0x000000ac00000000ull, 0x000001ee00000000ull, { 24, 25, 26, 44, 0 }, 0x0, 182, }, + { 236, 3, 0, 0x0000000180000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 850, }, + { 237, 3, 0, 0x0000000030000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x8, 183, }, + { 239, 3, 1, 0x0000008c00000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 184, }, + { 239, 3, 1, 0x000000ac00000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 185, }, + { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 28, 25, 1, 0, 0 }, 0x0, 186, }, + { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x40, 187, }, + { 241, 3, 1, 0x0000008c40000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 188, }, + { 241, 3, 1, 0x000000ac40000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 189, }, + { 242, 3, 1, 0x0000008c80000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 190, }, + { 242, 3, 1, 0x000000ac80000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 191, }, + { 243, 3, 1, 0x0000008cc0000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 192, }, + { 243, 3, 1, 0x000000acc0000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 193, }, + { 244, 3, 1, 0x000000cec0000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 2785, }, + { 244, 3, 1, 0x000000eec0000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 2786, }, + { 245, 3, 1, 0x000000cc40000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 194, }, + { 245, 3, 1, 0x000000ec40000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 195, }, + { 246, 3, 1, 0x000000ccc0000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 196, }, + { 246, 3, 1, 0x000000ecc0000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 197, }, + { 247, 3, 1, 0x000000cc00000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 198, }, + { 247, 3, 1, 0x000000ec00000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 199, }, + { 248, 3, 1, 0x000000cc80000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 200, }, + { 248, 3, 1, 0x000000ec80000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 201, }, + { 249, 1, 1, 0x0000010028000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 202, }, + { 249, 1, 1, 0x0000010020000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 203, }, + { 249, 1, 1, 0x0000010128000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 204, }, + { 250, 3, 0, 0x0000000020000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x0, 205, }, + { 251, 2, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 206, }, + { 252, 2, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 207, }, + { 253, 2, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 208, }, + { 254, 3, 0, 0x0000000198000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 1150, }, + { 255, 3, 1, 0x00000020f8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 209, }, + { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 23, 26, 77, 0 }, 0x0, 3040, }, + { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 26, 77, 0, 0 }, 0x40, 3041, }, + { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 22, 26, 77, 0 }, 0x40, 2003, }, + { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 26, 77, 0, 0 }, 0x40, 2004, }, + { 257, 2, 2, 0x000000a000082000ull, 0x000001fe00083000ull, { 22, 23, 50, 0, 0 }, 0x0, 3044, }, + { 257, 2, 1, 0x000000a000082000ull, 0x000001fe00083000ull, { 22, 50, 0, 0, 0 }, 0x40, 3045, }, + { 257, 2, 2, 0x000000a000082000ull, 0x000001fe00083000ull, { 23, 22, 50, 0, 0 }, 0x40, 2007, }, + { 257, 2, 1, 0x000000a000082000ull, 0x000001fe00083000ull, { 23, 50, 0, 0, 0 }, 0x40, 2008, }, + { 258, 3, 1, 0x00000020d0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 210, }, + { 259, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 23, 26, 0, 0 }, 0x0, 3048, }, + { 259, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 26, 0, 0, 0 }, 0x40, 3049, }, + { 259, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 22, 26, 0, 0 }, 0x40, 2011, }, + { 259, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 26, 0, 0, 0 }, 0x40, 2012, }, + { 260, 3, 1, 0x00000020f0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 211, }, + { 262, 3, 1, 0x00000020d8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 212, }, + { 266, 2, 1, 0x000000e840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1131, }, + { 267, 2, 1, 0x000000ea40000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1132, }, + { 268, 2, 1, 0x000000f840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1133, }, + { 272, 4, 0, 0x00000000c0000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x28, 223, }, + { 277, 3, 1, 0x0000008208000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 213, }, + { 278, 3, 1, 0x0000008248000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 214, }, + { 279, 3, 1, 0x0000008288000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 215, }, + { 280, 3, 1, 0x00000082c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 216, }, + { 282, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 1179, }, + { 282, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 1261, }, + { 283, 5, 1, 0x000001d000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 1180, }, + { 284, 1, 1, 0x0000010078000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 217, }, + { 284, 1, 1, 0x0000010178000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 218, }, + { 287, 2, 1, 0x0000000080000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 219, }, + { 288, 2, 1, 0x0000000088000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 220, }, + { 289, 2, 1, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 221, }, +}; + +static const char dis_table[] = { +0xa0, 0xc7, 0xc8, 0xa0, 0x2e, 0xd8, 0xa0, 0x2c, 0xc0, 0xa0, 0x1c, 0x00, +0x98, 0xb0, 0x02, 0x50, 0x90, 0x50, 0x90, 0x28, 0x24, 0x39, 0x28, 0x24, +0x39, 0x20, 0x90, 0x28, 0x24, 0x39, 0x18, 0x24, 0x39, 0x10, 0x91, 0x60, +0x90, 0x28, 0x24, 0x39, 0x00, 0x10, 0x10, 0x58, 0x41, 0x61, 0xc7, 0xc0, +0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +0x10, 0x10, 0x52, 0xc0, 0xc0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +0x10, 0x10, 0x10, 0x24, 0x24, 0x70, 0x90, 0x28, 0x24, 0x38, 0xf0, 0x24, +0x38, 0xe8, 0xa8, 0x0b, 0x48, 0x15, 0x20, 0x97, 0x20, 0x95, 0xc8, 0x9a, +0xb8, 0x05, 0x38, 0x91, 0x18, 0x90, 0xa0, 0x90, 0x60, 0x80, 0x90, 0x20, +0x34, 0xa6, 0xa4, 0x25, 0x00, 0x34, 0xa3, 0x80, 0xa4, 0x36, 0xa0, 0x36, +0xd9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x36, 0xcf, 0x80, 0x34, 0x86, 0x81, +0x33, 0xe2, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x24, 0x10, 0x34, +0x83, 0xa4, 0x1f, 0x08, 0x34, 0x80, 0x90, 0x38, 0xa4, 0x38, 0xa0, 0x37, +0x1a, 0xa4, 0x38, 0x48, 0x37, 0x0e, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x37, +0x20, 0x36, 0xef, 0xa4, 0x36, 0xf8, 0x36, 0xea, 0x80, 0xa4, 0x23, 0xf0, +0x34, 0x7f, 0x92, 0x18, 0x91, 0xc0, 0x80, 0x91, 0x80, 0x90, 0xf8, 0xdb, +0x84, 0x60, 0xf9, 0x40, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x68, 0x8c, 0x43, +0xc8, 0x84, 0x38, 0x83, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x58, 0x8c, 0x43, +0xa8, 0x84, 0x38, 0x81, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, +0x35, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x33, 0xa4, 0x1f, 0x18, 0x33, 0xe4, +0x80, 0x90, 0x28, 0x80, 0x33, 0xe0, 0x80, 0x34, 0x88, 0x81, 0x90, 0x38, +0xa4, 0x24, 0x80, 0x34, 0x8b, 0xa4, 0x24, 0x48, 0x34, 0x85, 0xc0, 0x40, +0x10, 0x10, 0x90, 0x38, 0xa4, 0x1e, 0xf0, 0x33, 0xdf, 0xa4, 0x1e, 0xe0, +0x33, 0xdd, 0x18, 0x24, 0x24, 0xf8, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, +0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x38, 0x38, 0x6d, 0xc0, 0xc0, 0x80, 0xa4, +0x42, 0x28, 0x38, 0x69, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, +0x2f, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2d, 0x92, 0xb8, 0x99, 0x84, 0x24, +0x68, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, 0x98, 0x36, +0xd8, 0x82, 0x36, 0xce, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x38, +0x98, 0x37, 0x19, 0xa4, 0x38, 0x40, 0x37, 0x0d, 0x80, 0x90, 0x38, 0xa4, +0x37, 0x18, 0x36, 0xee, 0xa4, 0x36, 0xf0, 0x36, 0xe9, 0x83, 0x90, 0xa8, +0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x08, 0x38, 0x61, 0xc0, +0xc0, 0x80, 0xa4, 0x41, 0xf8, 0x38, 0x5d, 0xd3, 0x82, 0x40, 0x50, 0xc0, +0xc0, 0x81, 0x38, 0x29, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x27, 0x18, 0x24, +0x24, 0x78, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, +0x41, 0xd8, 0x38, 0x55, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0xc8, 0x38, 0x51, +0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x23, 0x50, 0xc0, 0xc0, +0x81, 0x38, 0x21, 0x94, 0x50, 0x92, 0xf8, 0x99, 0x84, 0x1f, 0x48, 0x90, +0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, 0x90, 0x36, 0xd7, 0x82, +0x36, 0xcd, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x38, 0x90, 0x37, +0x18, 0xa4, 0x38, 0x38, 0x37, 0x0c, 0x80, 0x90, 0x38, 0xa4, 0x37, 0x10, +0x36, 0xed, 0xa4, 0x36, 0xe8, 0x36, 0xe8, 0x83, 0x90, 0xe8, 0xd3, 0x83, +0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x78, 0x8c, 0x43, 0xe8, 0x84, 0x38, +0x85, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x60, 0x8c, 0x43, 0xb8, 0x84, 0x38, +0x82, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x37, 0x50, 0xc0, +0xc0, 0x81, 0x38, 0x34, 0x18, 0x24, 0x1f, 0x40, 0x83, 0x90, 0xa8, 0xd3, +0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x48, 0x38, 0x71, 0xc0, 0xc0, +0x80, 0xa4, 0x42, 0x30, 0x38, 0x6b, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, +0x81, 0x38, 0x31, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2e, 0x92, 0xb8, 0x99, +0x84, 0x1f, 0x38, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, +0x88, 0x36, 0xd6, 0x82, 0x36, 0xcc, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, +0xa4, 0x38, 0x88, 0x37, 0x17, 0xa4, 0x38, 0x30, 0x37, 0x0b, 0x80, 0x90, +0x38, 0xa4, 0x37, 0x08, 0x36, 0xec, 0xa4, 0x36, 0xe0, 0x36, 0xe7, 0x83, +0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x18, 0x38, +0x65, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x00, 0x38, 0x5f, 0xd3, 0x82, 0x40, +0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2b, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x28, +0x18, 0x20, 0x01, 0x48, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, +0x80, 0xa4, 0x41, 0xe8, 0x38, 0x59, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0xd0, +0x38, 0x53, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x25, 0x50, +0xc0, 0xc0, 0x81, 0x38, 0x22, 0xda, 0x06, 0xe0, 0xf9, 0x80, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x24, 0xe8, 0x34, 0x9b, 0x80, 0x34, 0x98, 0x90, 0x38, +0xa4, 0x24, 0x90, 0x34, 0x96, 0x80, 0x34, 0x93, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x24, 0xd0, 0x34, 0x9c, 0x80, 0x34, 0x99, 0x90, 0x38, 0xa4, 0x24, +0xa8, 0x34, 0x97, 0x80, 0x34, 0x94, 0xc8, 0x40, 0x19, 0x00, 0x91, 0x58, +0x90, 0x60, 0x82, 0x90, 0x20, 0x36, 0xcb, 0xa4, 0x36, 0x48, 0x36, 0xca, +0x90, 0xc0, 0x80, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0xc1, 0x00, 0x85, +0x37, 0x03, 0xc9, 0xe1, 0xc0, 0x40, 0x85, 0x37, 0x00, 0x80, 0x36, 0xff, +0x10, 0x10, 0x81, 0x36, 0xdb, 0x90, 0xa8, 0x10, 0x10, 0x90, 0x28, 0x81, +0x36, 0xf9, 0x90, 0x38, 0xa4, 0x37, 0xa0, 0x36, 0xf5, 0xa4, 0x37, 0x90, +0x36, 0xf3, 0x90, 0x70, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x37, 0xb8, 0x36, +0xf8, 0x80, 0x36, 0xf6, 0x90, 0x60, 0x90, 0x28, 0x24, 0x37, 0xf0, 0xa4, +0x37, 0xe0, 0x36, 0xfd, 0x80, 0xa4, 0x37, 0xd0, 0x36, 0xfb, 0x80, 0x90, +0xf8, 0x90, 0x90, 0x90, 0x50, 0x90, 0x28, 0x80, 0x38, 0x17, 0x80, 0x38, +0x20, 0x80, 0xa4, 0x40, 0xf0, 0x38, 0x1f, 0x90, 0x28, 0x81, 0x38, 0x1d, +0x80, 0xa4, 0x40, 0xd8, 0x38, 0x1c, 0x90, 0x28, 0x82, 0x38, 0x1a, 0x81, +0xa4, 0x40, 0xc0, 0x38, 0x19, 0x98, 0xe8, 0x01, 0xb0, 0x90, 0x88, 0x90, +0x60, 0xa4, 0x36, 0x38, 0x10, 0x10, 0x10, 0x10, 0x83, 0x33, 0xb7, 0x24, +0x36, 0x30, 0x90, 0x28, 0x24, 0x36, 0x28, 0x24, 0x36, 0x20, 0x90, 0x88, +0x90, 0x60, 0xa4, 0x36, 0x10, 0x10, 0x10, 0x10, 0x10, 0x83, 0x33, 0xb6, +0x24, 0x36, 0x08, 0x90, 0x28, 0x24, 0x36, 0x00, 0x24, 0x35, 0xf8, 0xa8, +0x09, 0x00, 0x0e, 0x20, 0x96, 0x48, 0x95, 0xe8, 0x93, 0x38, 0x91, 0xa0, +0x90, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, 0x60, 0x33, 0xcd, 0xa4, +0x1e, 0x50, 0x33, 0xcb, 0x90, 0x38, 0xa4, 0x1e, 0x40, 0x33, 0xc9, 0x80, +0x33, 0xc7, 0x90, 0x60, 0x90, 0x28, 0x24, 0x1e, 0x00, 0xa4, 0x1d, 0xf0, +0x33, 0xbf, 0x90, 0x38, 0xa4, 0x1d, 0xe0, 0x33, 0xbd, 0xa4, 0x1e, 0x28, +0x33, 0xc6, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, 0x18, 0x33, +0xc4, 0xa4, 0x1e, 0x08, 0x33, 0xc2, 0x90, 0x38, 0xa4, 0x35, 0xb0, 0x36, +0xbc, 0xa4, 0x35, 0x50, 0x36, 0xb0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x32, +0x90, 0x36, 0x5e, 0xa4, 0x32, 0x60, 0x36, 0x58, 0x10, 0x10, 0xa4, 0x1d, +0xd0, 0x33, 0xbb, 0x99, 0x60, 0x02, 0x70, 0x90, 0x90, 0x90, 0x50, 0x90, +0x28, 0x24, 0x1e, 0x90, 0x80, 0x33, 0xda, 0x80, 0xa4, 0x1e, 0x98, 0x33, +0xd8, 0x90, 0x50, 0x90, 0x28, 0x24, 0x1e, 0xa0, 0x80, 0x33, 0xdb, 0x90, +0x38, 0xa4, 0x1e, 0xa8, 0x33, 0xd9, 0xa4, 0x1e, 0x70, 0x33, 0xcf, 0x90, +0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xe8, 0x36, 0xa5, 0xa4, 0x34, +0x48, 0x36, 0x92, 0x90, 0x38, 0xa4, 0x33, 0xe0, 0x36, 0x83, 0xa4, 0x33, +0x50, 0x36, 0x72, 0x81, 0xa4, 0x1e, 0x80, 0x33, 0xd1, 0xe4, 0xa2, 0x04, +0x40, 0x38, 0x13, 0x18, 0x24, 0x1d, 0xc8, 0xe4, 0xe2, 0x02, 0xc0, 0x38, +0x0d, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, +0x38, 0xa4, 0x35, 0xa8, 0x36, 0xbb, 0xa4, 0x35, 0x48, 0x36, 0xaf, 0x80, +0x90, 0x38, 0xa4, 0x32, 0x88, 0x36, 0x5d, 0xa4, 0x32, 0x58, 0x36, 0x57, +0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xd8, +0x36, 0xa4, 0xa4, 0x34, 0x40, 0x36, 0x90, 0x90, 0x38, 0xa4, 0x33, 0xd0, +0x36, 0x82, 0xa4, 0x33, 0x48, 0x36, 0x70, 0xe4, 0xa2, 0x01, 0x40, 0x38, +0x07, 0x18, 0x24, 0x1d, 0xc0, 0xe4, 0xe1, 0xff, 0xc0, 0x38, 0x01, 0x92, +0x90, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, +0x38, 0xa4, 0x35, 0xa0, 0x36, 0xba, 0xa4, 0x35, 0x40, 0x36, 0xae, 0x80, +0x90, 0x38, 0xa4, 0x32, 0x80, 0x36, 0x5c, 0xa4, 0x32, 0x50, 0x36, 0x56, +0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xc8, +0x36, 0xa3, 0xa4, 0x34, 0x38, 0x36, 0x8e, 0x90, 0x38, 0xa4, 0x33, 0xc0, +0x36, 0x81, 0xa4, 0x33, 0x40, 0x36, 0x6e, 0xe4, 0xa2, 0x04, 0x80, 0x38, +0x15, 0x10, 0x10, 0xe4, 0xe2, 0x03, 0x00, 0x38, 0x0f, 0x92, 0x50, 0x99, +0x1c, 0x1e, 0xb0, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, +0x35, 0x98, 0x36, 0xb9, 0xa4, 0x35, 0x38, 0x36, 0xad, 0x80, 0x90, 0x38, +0xa4, 0x32, 0x78, 0x36, 0x5b, 0xa4, 0x32, 0x48, 0x36, 0x55, 0x18, 0x20, +0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xb8, 0x36, 0xa2, +0xa4, 0x34, 0x30, 0x36, 0x8c, 0x90, 0x38, 0xa4, 0x33, 0xb0, 0x36, 0x80, +0xa4, 0x33, 0x38, 0x36, 0x6c, 0xe4, 0xa2, 0x01, 0x80, 0x38, 0x09, 0x10, +0x10, 0xe4, 0xe2, 0x00, 0x00, 0x38, 0x03, 0xc0, 0x40, 0x80, 0x10, 0x10, +0x81, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0x98, 0x80, 0x85, 0x36, 0x66, +0xc9, 0xe1, 0x99, 0x00, 0x85, 0x36, 0x63, 0x80, 0x36, 0x61, 0x80, 0xd8, +0x47, 0x80, 0x0d, 0xc0, 0xc0, 0x80, 0x10, 0x10, 0x82, 0x90, 0x58, 0xd5, +0x81, 0x80, 0x80, 0x37, 0xfd, 0x80, 0x37, 0xfb, 0xd5, 0x81, 0x80, 0x80, +0x37, 0xf9, 0x80, 0x37, 0xf7, 0xc0, 0x80, 0x10, 0x10, 0x82, 0x90, 0x58, +0xd5, 0x81, 0x80, 0x80, 0x37, 0xfe, 0x80, 0x37, 0xfc, 0xd5, 0x81, 0x80, +0x80, 0x37, 0xfa, 0x80, 0x37, 0xf8, 0xc0, 0x80, 0x83, 0xa4, 0x3f, 0xa8, +0x37, 0xf6, 0xa0, 0x59, 0x60, 0xa0, 0x41, 0xe0, 0xa8, 0x1e, 0xb0, 0x34, +0x88, 0xa0, 0x12, 0x38, 0xa0, 0x0b, 0x48, 0x96, 0x00, 0x9a, 0xf0, 0x05, +0xc0, 0x91, 0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x15, 0x58, +0x33, 0xb5, 0xa4, 0x15, 0x78, 0x33, 0xb4, 0x10, 0x10, 0xa4, 0x15, 0x68, +0x33, 0xb3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0xf8, 0x33, 0x9a, 0xa4, +0x15, 0x18, 0x33, 0x99, 0x10, 0x10, 0xa4, 0x15, 0x08, 0x33, 0x98, 0x90, +0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x98, 0x33, 0x7f, 0xa4, 0x14, +0xb8, 0x33, 0x7e, 0x10, 0x10, 0xa4, 0x14, 0xa8, 0x33, 0x7d, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x14, 0x38, 0x33, 0x63, 0xa4, 0x14, 0x58, 0x33, 0x62, +0x10, 0x10, 0xa4, 0x14, 0x48, 0x33, 0x61, 0x91, 0x70, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x15, 0x28, 0x33, 0xb0, 0xa4, 0x15, 0x48, 0x33, +0xb2, 0x10, 0x10, 0xa4, 0x15, 0x38, 0x33, 0xb1, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x14, 0xc8, 0x33, 0x95, 0xa4, 0x14, 0xe8, 0x33, 0x97, 0x10, 0x10, +0xa4, 0x14, 0xd8, 0x33, 0x96, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x14, 0x68, 0x33, 0x7a, 0xa4, 0x14, 0x88, 0x33, 0x7c, 0x10, 0x10, 0xa4, +0x14, 0x78, 0x33, 0x7b, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x08, 0x33, +0x5e, 0xa4, 0x14, 0x28, 0x33, 0x60, 0x10, 0x10, 0xa4, 0x14, 0x18, 0x33, +0x5f, 0xe4, 0xe1, 0x8b, 0x40, 0x36, 0x41, 0x9a, 0xf0, 0x05, 0x00, 0x91, +0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0xa0, 0x33, 0xad, +0xa4, 0x13, 0x98, 0x33, 0xaf, 0x10, 0x10, 0xa4, 0x13, 0x90, 0x33, 0xae, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x88, 0x33, 0x92, 0xa4, 0x13, 0x80, +0x33, 0x94, 0x10, 0x10, 0xa4, 0x13, 0x78, 0x33, 0x93, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x13, 0x70, 0x33, 0x77, 0xa4, 0x13, 0x68, 0x33, +0x79, 0x10, 0x10, 0xa4, 0x13, 0x60, 0x33, 0x78, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x13, 0x58, 0x33, 0x5b, 0xa4, 0x13, 0x50, 0x33, 0x5d, 0x10, 0x10, +0xa4, 0x13, 0x48, 0x33, 0x5c, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0xaa, 0x80, 0x33, 0xac, 0x10, 0x10, 0x80, 0x33, 0xab, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x8f, 0x80, 0x33, 0x91, 0x10, 0x10, +0x80, 0x33, 0x90, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x74, +0x80, 0x33, 0x76, 0x10, 0x10, 0x80, 0x33, 0x75, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x58, 0x80, 0x33, 0x5a, 0x10, 0x10, 0x80, 0x33, 0x59, 0xe4, +0xe1, 0x66, 0x40, 0x35, 0xc1, 0x95, 0x40, 0x9a, 0x90, 0x05, 0x00, 0x91, +0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xa7, 0x80, 0x33, +0xa9, 0x10, 0x10, 0x80, 0x33, 0xa8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x8c, 0x80, 0x33, 0x8e, 0x10, 0x10, 0x80, 0x33, 0x8d, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x13, 0x30, 0x33, 0x71, 0xa4, 0x13, 0x40, 0x33, +0x73, 0x10, 0x10, 0xa4, 0x13, 0x38, 0x33, 0x72, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x13, 0x00, 0x33, 0x55, 0xa4, 0x13, 0x10, 0x33, 0x57, 0x10, 0x10, +0xa4, 0x13, 0x08, 0x33, 0x56, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0xa4, 0x80, 0x33, 0xa6, 0x10, 0x10, 0x80, 0x33, 0xa5, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x89, 0x80, 0x33, 0x8b, 0x10, 0x10, +0x80, 0x33, 0x8a, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x18, +0x33, 0x6e, 0xa4, 0x13, 0x28, 0x33, 0x70, 0x10, 0x10, 0xa4, 0x13, 0x20, +0x33, 0x6f, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x12, 0xe8, 0x33, 0x52, 0xa4, +0x12, 0xf8, 0x33, 0x54, 0x10, 0x10, 0xa4, 0x12, 0xf0, 0x33, 0x53, 0xe4, +0xe1, 0x8a, 0x40, 0x36, 0x3d, 0x98, 0xb8, 0x01, 0x68, 0x10, 0x10, 0x10, +0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4f, 0x80, 0x33, 0x51, 0x10, +0x10, 0x80, 0x33, 0x50, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x97, 0x00, +0x60, 0xa0, 0x96, 0xc0, 0x90, 0x30, 0x60, 0xa0, 0x96, 0x80, 0x60, 0xa0, +0x96, 0x40, 0xe4, 0xe1, 0x64, 0x40, 0x35, 0xb9, 0xa0, 0x08, 0x08, 0x94, +0xe0, 0x9a, 0x60, 0x04, 0xa0, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x13, 0xd8, 0x33, 0x9e, 0xa4, 0x13, 0xf8, 0x33, 0xa3, 0x10, +0x10, 0xa4, 0x13, 0xe8, 0x33, 0xa2, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x83, 0x80, 0x33, 0x88, 0x10, 0x10, 0x80, 0x33, 0x87, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x68, 0x80, 0x33, 0x6d, 0x10, 0x10, 0x80, +0x33, 0x6c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x49, 0x80, 0x33, 0x4e, +0x10, 0x10, 0x80, 0x33, 0x4d, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x13, 0xa8, 0x33, 0x9b, 0xa4, 0x13, 0xc8, 0x33, 0x9d, 0x10, +0x10, 0xa4, 0x13, 0xb8, 0x33, 0x9c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x80, 0x80, 0x33, 0x82, 0x10, 0x10, 0x80, 0x33, 0x81, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x65, 0x80, 0x33, 0x67, 0x10, 0x10, 0x80, +0x33, 0x66, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x46, 0x80, 0x33, 0x48, +0x10, 0x10, 0x80, 0x33, 0x47, 0xe4, 0xe1, 0x89, 0x40, 0x36, 0x39, 0x9a, +0x60, 0x02, 0xe0, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x1a, 0x20, 0x33, 0x9f, 0xa4, 0x1a, 0x10, 0x33, 0xa1, 0x10, 0x10, 0xa4, +0x1a, 0x00, 0x33, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x84, 0x80, +0x33, 0x86, 0x10, 0x10, 0x80, 0x33, 0x85, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0x69, 0x80, 0x33, 0x6b, 0x10, 0x10, 0x80, 0x33, 0x6a, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4a, 0x80, 0x33, 0x4c, 0x10, 0x10, +0x80, 0x33, 0x4b, 0x81, 0x90, 0x50, 0x90, 0x28, 0x24, 0x19, 0xd0, 0x24, +0x19, 0xf0, 0x10, 0x10, 0x24, 0x19, 0xe0, 0xe4, 0xe1, 0x62, 0x40, 0x35, +0xb1, 0x93, 0x90, 0x99, 0xb8, 0x03, 0x50, 0x90, 0xe8, 0x90, 0x88, 0x90, +0x40, 0x80, 0xa4, 0x15, 0xb8, 0x32, 0xca, 0x10, 0x10, 0xa4, 0x15, 0xa8, +0x32, 0xc9, 0x90, 0x28, 0x81, 0x32, 0xc6, 0x10, 0x10, 0x80, 0x32, 0xc5, +0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xc2, 0x10, 0x10, 0x80, 0x32, 0xc1, +0x90, 0x28, 0x81, 0x32, 0xbe, 0x10, 0x10, 0x80, 0x32, 0xbd, 0x90, 0xe8, +0x90, 0x88, 0x90, 0x40, 0x80, 0xa4, 0x15, 0x88, 0x32, 0xc7, 0x10, 0x10, +0xa4, 0x15, 0x98, 0x32, 0xc8, 0x90, 0x28, 0x81, 0x32, 0xc3, 0x10, 0x10, +0x80, 0x32, 0xc4, 0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xbf, 0x10, 0x10, +0x80, 0x32, 0xc0, 0x90, 0x28, 0x81, 0x32, 0xbb, 0x10, 0x10, 0x80, 0x32, +0xbc, 0xe4, 0xe1, 0x88, 0x40, 0x36, 0x35, 0x88, 0x00, 0x88, 0x10, 0x10, +0x10, 0x10, 0x90, 0x28, 0x81, 0x32, 0xb9, 0x10, 0x10, 0x80, 0x32, 0xba, +0xe4, 0xe1, 0x60, 0x40, 0x35, 0xa9, 0xa0, 0x0e, 0x80, 0xa0, 0x09, 0x08, +0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x33, 0x39, 0x80, 0x33, 0x38, 0x10, 0x10, 0x80, 0x33, +0x37, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x1e, 0x80, 0x33, 0x1d, 0x10, +0x10, 0x80, 0x33, 0x1c, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x03, 0x80, 0x33, 0x02, 0x10, 0x10, 0x80, 0x33, 0x01, 0x90, 0x50, 0x90, +0x28, 0x80, 0x32, 0xe8, 0x80, 0x32, 0xe7, 0x10, 0x10, 0x80, 0x32, 0xe6, +0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x34, 0x80, +0x33, 0x36, 0x10, 0x10, 0x80, 0x33, 0x35, 0x90, 0x50, 0x90, 0x28, 0x80, +0x33, 0x19, 0x80, 0x33, 0x1b, 0x10, 0x10, 0x80, 0x33, 0x1a, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfe, 0x80, 0x33, 0x00, 0x10, 0x10, +0x80, 0x32, 0xff, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe3, 0x80, 0x32, +0xe5, 0x10, 0x10, 0x80, 0x32, 0xe4, 0xe4, 0xe1, 0x7a, 0x40, 0x36, 0x11, +0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x31, 0x80, 0x33, 0x33, 0x10, 0x10, 0x80, 0x33, 0x32, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x16, 0x80, 0x33, 0x18, 0x10, 0x10, 0x80, +0x33, 0x17, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfb, 0x80, +0x32, 0xfd, 0x10, 0x10, 0x80, 0x32, 0xfc, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xe0, 0x80, 0x32, 0xe2, 0x10, 0x10, 0x80, 0x32, 0xe1, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x2e, 0x80, 0x33, 0x30, +0x10, 0x10, 0x80, 0x33, 0x2f, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x13, +0x80, 0x33, 0x15, 0x10, 0x10, 0x80, 0x33, 0x14, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xf8, 0x80, 0x32, 0xfa, 0x10, 0x10, 0x80, 0x32, +0xf9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xdd, 0x80, 0x32, 0xdf, 0x10, +0x10, 0x80, 0x32, 0xde, 0xe4, 0xe1, 0x59, 0x40, 0x35, 0x79, 0x94, 0x80, +0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x2b, 0x80, 0x33, 0x2d, 0x10, 0x10, 0x80, 0x33, 0x2c, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x10, 0x80, 0x33, 0x12, 0x10, 0x10, 0x80, +0x33, 0x11, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xf5, 0x80, +0x32, 0xf7, 0x10, 0x10, 0x80, 0x32, 0xf6, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xda, 0x80, 0x32, 0xdc, 0x10, 0x10, 0x80, 0x32, 0xdb, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x28, 0x80, 0x33, 0x2a, +0x10, 0x10, 0x80, 0x33, 0x29, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x0d, +0x80, 0x33, 0x0f, 0x10, 0x10, 0x80, 0x33, 0x0e, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xf2, 0x80, 0x32, 0xf4, 0x10, 0x10, 0x80, 0x32, +0xf3, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd7, 0x80, 0x32, 0xd9, 0x10, +0x10, 0x80, 0x32, 0xd8, 0xe4, 0xe1, 0x78, 0x40, 0x36, 0x09, 0x88, 0x00, +0xb0, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd4, +0x80, 0x32, 0xd6, 0x10, 0x10, 0x80, 0x32, 0xd5, 0xe4, 0xe1, 0x58, 0x40, +0x35, 0x75, 0x96, 0xe8, 0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x22, 0x80, 0x33, 0x27, +0x10, 0x10, 0x80, 0x33, 0x26, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x07, +0x80, 0x33, 0x0c, 0x10, 0x10, 0x80, 0x33, 0x0b, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xec, 0x80, 0x32, 0xf1, 0x10, 0x10, 0x80, 0x32, +0xf0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xce, 0x80, 0x32, 0xd3, 0x10, +0x10, 0x80, 0x32, 0xd2, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x1f, 0x80, 0x33, 0x21, 0x10, 0x10, 0x80, 0x33, 0x20, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x04, 0x80, 0x33, 0x06, 0x10, 0x10, 0x80, +0x33, 0x05, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe9, 0x80, +0x32, 0xeb, 0x10, 0x10, 0x80, 0x32, 0xea, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xcb, 0x80, 0x32, 0xcd, 0x10, 0x10, 0x80, 0x32, 0xcc, 0xe4, 0xe1, +0x76, 0x40, 0x36, 0x01, 0x88, 0x02, 0x28, 0x91, 0x10, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x23, 0x80, 0x33, 0x25, 0x10, 0x10, 0x80, +0x33, 0x24, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x08, 0x80, 0x33, 0x0a, +0x10, 0x10, 0x80, 0x33, 0x09, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xed, 0x80, 0x32, 0xef, 0x10, 0x10, 0x80, 0x32, 0xee, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xcf, 0x80, 0x32, 0xd1, 0x10, 0x10, 0x80, 0x32, +0xd0, 0xe4, 0xe1, 0x57, 0x40, 0x35, 0x71, 0x90, 0x40, 0xe5, 0x21, 0x74, +0x40, 0x35, 0xf9, 0xe5, 0x21, 0x56, 0x40, 0x35, 0x6d, 0x9e, 0xb4, 0x23, +0xe8, 0x93, 0x70, 0x91, 0xd8, 0xd5, 0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90, +0x48, 0x80, 0x8c, 0x3f, 0x38, 0x84, 0x37, 0xf1, 0xa4, 0x3d, 0x18, 0x37, +0xbb, 0x90, 0x28, 0x24, 0x3c, 0x58, 0xa4, 0x3a, 0xd8, 0x37, 0x73, 0xd0, +0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3f, 0x18, 0x84, 0x37, 0xef, 0xa4, +0x3d, 0x08, 0x37, 0xb9, 0x90, 0x28, 0x24, 0x3c, 0x48, 0xa4, 0x3a, 0xc8, +0x37, 0x71, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, +0xdb, 0xa4, 0x3c, 0xe8, 0x37, 0xb5, 0x90, 0x28, 0x24, 0x3c, 0x28, 0xa4, +0x3a, 0xa8, 0x37, 0x6d, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xd7, +0xa4, 0x3c, 0xd8, 0x37, 0xb3, 0x90, 0x28, 0x24, 0x3c, 0x18, 0xa4, 0x3a, +0x98, 0x37, 0x6b, 0x91, 0x98, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, +0x28, 0x80, 0x37, 0xcf, 0xa4, 0x3c, 0xb8, 0x37, 0xaf, 0x90, 0x28, 0x24, +0x3b, 0xf8, 0xa4, 0x3a, 0x78, 0x37, 0x67, 0xd0, 0xc3, 0x40, 0x90, 0x28, +0x80, 0x37, 0xcb, 0xa4, 0x3c, 0xa8, 0x37, 0xad, 0x90, 0x28, 0x24, 0x3b, +0xe8, 0xa4, 0x3a, 0x68, 0x37, 0x65, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, +0x90, 0x28, 0x80, 0x37, 0xc3, 0xa4, 0x3c, 0x88, 0x37, 0xa9, 0x90, 0x28, +0x24, 0x3b, 0xc8, 0xa4, 0x3a, 0x48, 0x37, 0x61, 0xd0, 0xc3, 0x40, 0x90, +0x28, 0x80, 0x37, 0xbf, 0xa4, 0x3c, 0x78, 0x37, 0xa7, 0x90, 0x28, 0x24, +0x3b, 0xb8, 0xa4, 0x3a, 0x38, 0x37, 0x5f, 0x93, 0x70, 0x91, 0xd8, 0xd5, +0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3f, 0x58, 0x84, +0x37, 0xf3, 0xa4, 0x3d, 0x28, 0x37, 0xbd, 0x90, 0x28, 0x24, 0x3c, 0x68, +0xa4, 0x3a, 0xe8, 0x37, 0x75, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, +0x3f, 0x28, 0x84, 0x37, 0xf0, 0xa4, 0x3d, 0x10, 0x37, 0xba, 0x90, 0x28, +0x24, 0x3c, 0x50, 0xa4, 0x3a, 0xd0, 0x37, 0x72, 0xd5, 0x06, 0x80, 0xd0, +0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xdf, 0xa4, 0x3c, 0xf8, 0x37, 0xb7, +0x90, 0x28, 0x24, 0x3c, 0x38, 0xa4, 0x3a, 0xb8, 0x37, 0x6f, 0xd0, 0xc3, +0x40, 0x90, 0x28, 0x80, 0x37, 0xd9, 0xa4, 0x3c, 0xe0, 0x37, 0xb4, 0x90, +0x28, 0x24, 0x3c, 0x20, 0xa4, 0x3a, 0xa0, 0x37, 0x6c, 0x91, 0x98, 0xd5, +0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xd3, 0xa4, 0x3c, +0xc8, 0x37, 0xb1, 0x90, 0x28, 0x24, 0x3c, 0x08, 0xa4, 0x3a, 0x88, 0x37, +0x69, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xcd, 0xa4, 0x3c, 0xb0, +0x37, 0xae, 0x90, 0x28, 0x24, 0x3b, 0xf0, 0xa4, 0x3a, 0x70, 0x37, 0x66, +0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xc7, 0xa4, +0x3c, 0x98, 0x37, 0xab, 0x90, 0x28, 0x24, 0x3b, 0xd8, 0xa4, 0x3a, 0x58, +0x37, 0x63, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xc1, 0xa4, 0x3c, +0x80, 0x37, 0xa8, 0x90, 0x28, 0x24, 0x3b, 0xc0, 0xa4, 0x3a, 0x40, 0x37, +0x60, 0x99, 0xd8, 0x03, 0x90, 0x81, 0x90, 0xe0, 0x5b, 0x41, 0x40, 0x03, +0x40, 0x51, 0x40, 0xc0, 0xa4, 0x23, 0x80, 0x34, 0x60, 0xd1, 0x42, 0x00, +0xa4, 0x22, 0x80, 0x34, 0x40, 0xa4, 0x21, 0x80, 0x34, 0x20, 0x5b, 0x41, +0x40, 0x03, 0x40, 0x51, 0x40, 0xc0, 0xa4, 0x22, 0xa0, 0x34, 0x64, 0xd1, +0x42, 0x00, 0xa4, 0x21, 0xa0, 0x34, 0x44, 0xa4, 0x20, 0xa0, 0x34, 0x24, +0x81, 0x90, 0xe0, 0x5b, 0x41, 0x40, 0x03, 0x40, 0x51, 0x40, 0xc0, 0xa4, +0x22, 0xe0, 0x34, 0x6c, 0xd1, 0x42, 0x00, 0xa4, 0x21, 0xe0, 0x34, 0x4c, +0xa4, 0x20, 0xe0, 0x34, 0x2c, 0x5b, 0x41, 0x40, 0x03, 0x40, 0x51, 0x40, +0xc0, 0xa4, 0x22, 0xc0, 0x34, 0x68, 0xd1, 0x42, 0x00, 0xa4, 0x21, 0xc0, +0x34, 0x48, 0xa4, 0x20, 0xc0, 0x34, 0x28, 0xa8, 0x0b, 0x18, 0x13, 0xa8, +0x96, 0x80, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0xb8, 0x32, 0x58, 0x24, 0x12, 0xb0, 0x90, 0x38, +0xa4, 0x11, 0xe0, 0x32, 0x3d, 0x24, 0x11, 0xd8, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x11, 0x08, 0x32, 0x22, 0x24, 0x11, 0x00, 0x90, 0x38, 0xa4, 0x10, +0x30, 0x32, 0x07, 0x24, 0x10, 0x28, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x12, 0xa8, 0x32, 0x53, 0x24, 0x12, 0xa0, 0x90, 0x38, 0xa4, 0x11, +0xd0, 0x32, 0x38, 0x24, 0x11, 0xc8, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, +0xf8, 0x32, 0x1d, 0x24, 0x10, 0xf0, 0x90, 0x38, 0xa4, 0x10, 0x20, 0x32, +0x02, 0x24, 0x10, 0x18, 0xe4, 0xe1, 0xd0, 0x40, 0x37, 0x43, 0x99, 0x90, +0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x90, 0x32, +0x50, 0x24, 0x12, 0x88, 0x90, 0x38, 0xa4, 0x11, 0xb8, 0x32, 0x35, 0x24, +0x11, 0xb0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xe0, 0x32, 0x1a, 0x24, +0x10, 0xd8, 0x90, 0x38, 0xa4, 0x10, 0x08, 0x31, 0xff, 0x24, 0x10, 0x00, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x78, 0x32, 0x4d, 0x24, +0x12, 0x70, 0x90, 0x38, 0xa4, 0x11, 0xa0, 0x32, 0x32, 0x24, 0x11, 0x98, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xc8, 0x32, 0x17, 0x24, 0x10, 0xc0, +0x90, 0x38, 0xa4, 0x0f, 0xf0, 0x31, 0xfc, 0x24, 0x0f, 0xe8, 0xe4, 0xe1, +0xce, 0xc0, 0x37, 0x3d, 0x93, 0x78, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x60, 0x32, 0x4a, 0x24, 0x12, 0x58, +0x90, 0x38, 0xa4, 0x11, 0x88, 0x32, 0x2f, 0x24, 0x11, 0x80, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x10, 0xb0, 0x32, 0x14, 0x24, 0x10, 0xa8, 0x90, 0x38, +0xa4, 0x0f, 0xd8, 0x31, 0xf9, 0x24, 0x0f, 0xd0, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0x48, 0x32, 0x47, 0x24, 0x12, 0x40, 0x90, 0x38, +0xa4, 0x11, 0x70, 0x32, 0x2c, 0x24, 0x11, 0x68, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x10, 0x98, 0x32, 0x11, 0x24, 0x10, 0x90, 0x90, 0x38, 0xa4, 0x0f, +0xc0, 0x31, 0xf6, 0x24, 0x0f, 0xb8, 0xec, 0xa1, 0x1e, 0x00, 0x02, 0x00, +0x34, 0x7a, 0xa4, 0x39, 0xa8, 0x37, 0x37, 0x88, 0x00, 0x88, 0x10, 0x10, +0x10, 0x10, 0x90, 0x38, 0xa4, 0x0f, 0xa8, 0x31, 0xf3, 0x24, 0x0f, 0xa0, +0xe9, 0x61, 0x1d, 0x40, 0x02, 0x00, 0x34, 0x76, 0xe3, 0x61, 0xcb, 0xc0, +0x37, 0x31, 0x95, 0x08, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x30, 0x32, 0x41, 0x24, 0x12, 0x28, +0x90, 0x38, 0xa4, 0x11, 0x58, 0x32, 0x26, 0x24, 0x11, 0x50, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x10, 0x80, 0x32, 0x0b, 0x24, 0x10, 0x78, 0x90, 0x38, +0xa4, 0x0f, 0x90, 0x31, 0xed, 0x24, 0x0f, 0x88, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0x00, 0x32, 0x3e, 0x24, 0x11, 0xf8, 0x90, 0x38, +0xa4, 0x11, 0x28, 0x32, 0x23, 0x24, 0x11, 0x20, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x10, 0x50, 0x32, 0x08, 0x24, 0x10, 0x48, 0x90, 0x38, 0xa4, 0x0f, +0x60, 0x31, 0xea, 0x24, 0x0f, 0x58, 0xe4, 0xe1, 0xd0, 0x80, 0x37, 0x45, +0x88, 0x01, 0x88, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x20, +0x32, 0x42, 0x24, 0x12, 0x18, 0x90, 0x38, 0xa4, 0x11, 0x48, 0x32, 0x27, +0x24, 0x11, 0x40, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0x70, 0x32, 0x0c, +0x24, 0x10, 0x68, 0x90, 0x38, 0xa4, 0x0f, 0x80, 0x31, 0xee, 0x24, 0x0f, +0x78, 0xe4, 0xe1, 0xcf, 0x00, 0x37, 0x3f, 0x92, 0xd0, 0x99, 0x50, 0x02, +0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe9, 0x24, 0x0f, +0x40, 0x90, 0x28, 0x80, 0x31, 0xe5, 0x24, 0x0f, 0x20, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0xe1, 0x24, 0x0f, 0x00, 0x90, 0x28, 0x80, 0x31, 0xdd, +0x24, 0x0e, 0xe0, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe6, +0x24, 0x0f, 0x38, 0x90, 0x28, 0x80, 0x31, 0xe2, 0x24, 0x0f, 0x18, 0x90, +0x50, 0x90, 0x28, 0x80, 0x31, 0xde, 0x24, 0x0e, 0xf8, 0x90, 0x28, 0x80, +0x31, 0xda, 0x24, 0x0e, 0xd8, 0xec, 0xe1, 0xcd, 0xa1, 0x1f, 0x00, 0x37, +0x39, 0x88, 0x00, 0x78, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x80, 0x31, +0xd8, 0x24, 0x0e, 0xc8, 0xec, 0xe1, 0xcc, 0x21, 0x1d, 0x00, 0x37, 0x33, +0xe5, 0xa1, 0x55, 0x40, 0x35, 0x51, 0xa0, 0x2a, 0x10, 0xa8, 0x16, 0x60, +0x29, 0xd8, 0xa0, 0x0c, 0x48, 0xa0, 0x0a, 0xc8, 0x95, 0x60, 0x92, 0xb0, +0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xa1, 0x80, +0x31, 0xa0, 0x10, 0x10, 0x80, 0x31, 0x9f, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x08, 0x98, 0x31, 0xb3, 0xa4, 0x08, 0x90, 0x31, 0xb2, 0x10, 0x10, 0xa4, +0x08, 0x88, 0x31, 0xb1, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, +0xb8, 0x31, 0xd7, 0xa4, 0x09, 0xb0, 0x31, 0xd6, 0x10, 0x10, 0xa4, 0x09, +0xa8, 0x31, 0xd5, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x28, 0x31, 0xc5, +0xa4, 0x09, 0x20, 0x31, 0xc4, 0x10, 0x10, 0xa4, 0x09, 0x18, 0x31, 0xc3, +0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x9c, 0x80, +0x31, 0x9e, 0x10, 0x10, 0x80, 0x31, 0x9d, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x08, 0x70, 0x31, 0xae, 0xa4, 0x08, 0x80, 0x31, 0xb0, 0x10, 0x10, 0xa4, +0x08, 0x78, 0x31, 0xaf, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, +0x90, 0x31, 0xd2, 0xa4, 0x09, 0xa0, 0x31, 0xd4, 0x10, 0x10, 0xa4, 0x09, +0x98, 0x31, 0xd3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x00, 0x31, 0xc0, +0xa4, 0x09, 0x10, 0x31, 0xc2, 0x10, 0x10, 0xa4, 0x09, 0x08, 0x31, 0xc1, +0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, +0x99, 0x80, 0x31, 0x9b, 0x10, 0x10, 0x80, 0x31, 0x9a, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x58, 0x31, 0xab, 0xa4, 0x08, 0x68, 0x31, 0xad, 0x10, +0x10, 0xa4, 0x08, 0x60, 0x31, 0xac, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x09, 0x78, 0x31, 0xcf, 0xa4, 0x09, 0x88, 0x31, 0xd1, 0x10, 0x10, +0xa4, 0x09, 0x80, 0x31, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xe8, +0x31, 0xbd, 0xa4, 0x08, 0xf8, 0x31, 0xbf, 0x10, 0x10, 0xa4, 0x08, 0xf0, +0x31, 0xbe, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, +0x96, 0x80, 0x31, 0x98, 0x10, 0x10, 0x80, 0x31, 0x97, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x40, 0x31, 0xa8, 0xa4, 0x08, 0x50, 0x31, 0xaa, 0x10, +0x10, 0xa4, 0x08, 0x48, 0x31, 0xa9, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x09, 0x60, 0x31, 0xcc, 0xa4, 0x09, 0x70, 0x31, 0xce, 0x10, 0x10, +0xa4, 0x09, 0x68, 0x31, 0xcd, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xd0, +0x31, 0xba, 0xa4, 0x08, 0xe0, 0x31, 0xbc, 0x10, 0x10, 0xa4, 0x08, 0xd8, +0x31, 0xbb, 0x10, 0x10, 0x90, 0xa8, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, +0x90, 0x28, 0x80, 0x31, 0x8d, 0x80, 0x31, 0x8f, 0x10, 0x10, 0x80, 0x31, +0x8e, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0xc0, 0x60, 0xa0, 0x2a, +0x80, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0x40, 0x60, 0xa0, 0x2a, 0x00, 0x97, +0xf0, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x93, 0x80, 0x31, 0x95, 0x10, 0x10, 0x80, 0x31, 0x94, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x28, 0x31, 0xa5, 0xa4, 0x08, 0x38, +0x31, 0xa7, 0x10, 0x10, 0xa4, 0x08, 0x30, 0x31, 0xa6, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x09, 0x48, 0x31, 0xc9, 0xa4, 0x09, 0x58, 0x31, +0xcb, 0x10, 0x10, 0xa4, 0x09, 0x50, 0x31, 0xca, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x08, 0xb8, 0x31, 0xb7, 0xa4, 0x08, 0xc8, 0x31, 0xb9, 0x10, 0x10, +0xa4, 0x08, 0xc0, 0x31, 0xb8, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x90, 0x80, 0x31, 0x92, 0x10, 0x10, 0x80, 0x31, 0x91, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x10, 0x31, 0xa2, 0xa4, 0x08, 0x20, +0x31, 0xa4, 0x10, 0x10, 0xa4, 0x08, 0x18, 0x31, 0xa3, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x09, 0x30, 0x31, 0xc6, 0xa4, 0x09, 0x40, 0x31, +0xc8, 0x10, 0x10, 0xa4, 0x09, 0x38, 0x31, 0xc7, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x08, 0xa0, 0x31, 0xb4, 0xa4, 0x08, 0xb0, 0x31, 0xb6, 0x10, 0x10, +0xa4, 0x08, 0xa8, 0x31, 0xb5, 0x10, 0x10, 0x91, 0x40, 0x90, 0xa0, 0x90, +0x50, 0x90, 0x28, 0x80, 0x30, 0xcb, 0x80, 0x30, 0xca, 0x90, 0x28, 0x80, +0x30, 0xc9, 0x80, 0x30, 0xc8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xc4, +0x80, 0x30, 0xc7, 0x90, 0x28, 0x80, 0x30, 0xc6, 0x80, 0x30, 0xc5, 0x90, +0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbc, 0x80, 0x30, 0xc3, 0x90, +0x28, 0x80, 0x30, 0xc2, 0x80, 0x30, 0xc1, 0x90, 0x50, 0x90, 0x28, 0x80, +0x30, 0xbd, 0x80, 0x30, 0xc0, 0x90, 0x28, 0x80, 0x30, 0xbf, 0x80, 0x30, +0xbe, 0x91, 0x88, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, +0x3b, 0x10, 0x10, 0x80, 0x31, 0x3a, 0x90, 0x28, 0x81, 0x31, 0x3d, 0x10, +0x10, 0x80, 0x31, 0x3c, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, 0x41, 0x10, +0x10, 0x80, 0x31, 0x40, 0x90, 0x28, 0x81, 0x31, 0x3f, 0x10, 0x10, 0x80, +0x31, 0x3e, 0x80, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x81, 0x31, 0x38, +0x10, 0x10, 0x80, 0x31, 0x39, 0xa0, 0x0b, 0x90, 0xa0, 0x0a, 0xc8, 0x95, +0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x31, 0x56, 0x80, 0x31, 0x55, 0x10, 0x10, 0x80, 0x31, 0x54, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x06, 0xe8, 0x31, 0x68, 0xa4, 0x06, 0xe0, 0x31, 0x67, +0x10, 0x10, 0xa4, 0x06, 0xd8, 0x31, 0x66, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x08, 0x31, 0x8c, 0xa4, 0x08, 0x00, 0x31, 0x8b, 0x10, +0x10, 0xa4, 0x07, 0xf8, 0x31, 0x8a, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, +0x78, 0x31, 0x7a, 0xa4, 0x07, 0x70, 0x31, 0x79, 0x10, 0x10, 0xa4, 0x07, +0x68, 0x31, 0x78, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x31, 0x51, 0x80, 0x31, 0x53, 0x10, 0x10, 0x80, 0x31, 0x52, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x06, 0xc0, 0x31, 0x63, 0xa4, 0x06, 0xd0, 0x31, 0x65, +0x10, 0x10, 0xa4, 0x06, 0xc8, 0x31, 0x64, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x07, 0xe0, 0x31, 0x87, 0xa4, 0x07, 0xf0, 0x31, 0x89, 0x10, +0x10, 0xa4, 0x07, 0xe8, 0x31, 0x88, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, +0x50, 0x31, 0x75, 0xa4, 0x07, 0x60, 0x31, 0x77, 0x10, 0x10, 0xa4, 0x07, +0x58, 0x31, 0x76, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x4e, 0x80, 0x31, 0x50, 0x10, 0x10, 0x80, 0x31, 0x4f, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0xa8, 0x31, 0x60, 0xa4, 0x06, 0xb8, +0x31, 0x62, 0x10, 0x10, 0xa4, 0x06, 0xb0, 0x31, 0x61, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0xc8, 0x31, 0x84, 0xa4, 0x07, 0xd8, 0x31, +0x86, 0x10, 0x10, 0xa4, 0x07, 0xd0, 0x31, 0x85, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x07, 0x38, 0x31, 0x72, 0xa4, 0x07, 0x48, 0x31, 0x74, 0x10, 0x10, +0xa4, 0x07, 0x40, 0x31, 0x73, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x4b, 0x80, 0x31, 0x4d, 0x10, 0x10, 0x80, 0x31, 0x4c, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x90, 0x31, 0x5d, 0xa4, 0x06, 0xa0, +0x31, 0x5f, 0x10, 0x10, 0xa4, 0x06, 0x98, 0x31, 0x5e, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0xb0, 0x31, 0x81, 0xa4, 0x07, 0xc0, 0x31, +0x83, 0x10, 0x10, 0xa4, 0x07, 0xb8, 0x31, 0x82, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x07, 0x20, 0x31, 0x6f, 0xa4, 0x07, 0x30, 0x31, 0x71, 0x10, 0x10, +0xa4, 0x07, 0x28, 0x31, 0x70, 0x10, 0x10, 0x80, 0x10, 0x10, 0x10, 0x10, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x42, 0x80, 0x31, 0x44, 0x10, 0x10, +0x80, 0x31, 0x43, 0x80, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x48, 0x80, 0x31, 0x4a, 0x10, 0x10, +0x80, 0x31, 0x49, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x78, 0x31, 0x5a, +0xa4, 0x06, 0x88, 0x31, 0x5c, 0x10, 0x10, 0xa4, 0x06, 0x80, 0x31, 0x5b, +0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x98, 0x31, 0x7e, 0xa4, +0x07, 0xa8, 0x31, 0x80, 0x10, 0x10, 0xa4, 0x07, 0xa0, 0x31, 0x7f, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0x08, 0x31, 0x6c, 0xa4, 0x07, 0x18, 0x31, +0x6e, 0x10, 0x10, 0xa4, 0x07, 0x10, 0x31, 0x6d, 0x91, 0x40, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x45, 0x80, 0x31, 0x47, 0x10, 0x10, +0x80, 0x31, 0x46, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x60, 0x31, 0x57, +0xa4, 0x06, 0x70, 0x31, 0x59, 0x10, 0x10, 0xa4, 0x06, 0x68, 0x31, 0x58, +0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x80, 0x31, 0x7b, 0xa4, +0x07, 0x90, 0x31, 0x7d, 0x10, 0x10, 0xa4, 0x07, 0x88, 0x31, 0x7c, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x06, 0xf0, 0x31, 0x69, 0xa4, 0x07, 0x00, 0x31, +0x6b, 0x10, 0x10, 0xa4, 0x06, 0xf8, 0x31, 0x6a, 0x10, 0x10, 0x91, 0x40, +0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbb, 0x80, 0x30, 0xba, +0x90, 0x28, 0x80, 0x30, 0xb9, 0x80, 0x30, 0xb8, 0x90, 0x50, 0x90, 0x28, +0x80, 0x30, 0xb4, 0x80, 0x30, 0xb7, 0x90, 0x28, 0x80, 0x30, 0xb6, 0x80, +0x30, 0xb5, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xac, 0x80, +0x30, 0xb3, 0x90, 0x28, 0x80, 0x30, 0xb2, 0x80, 0x30, 0xb1, 0x90, 0x50, +0x90, 0x28, 0x80, 0x30, 0xad, 0x80, 0x30, 0xb0, 0x90, 0x28, 0x80, 0x30, +0xaf, 0x80, 0x30, 0xae, 0xc3, 0xc0, 0x30, 0x42, 0x9c, 0xe8, 0x07, 0x60, +0x91, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x80, 0x88, 0x00, 0x80, 0x90, 0x50, +0x90, 0x28, 0x80, 0x33, 0xf8, 0x80, 0x33, 0xf9, 0x81, 0x33, 0xef, 0xd0, +0x41, 0x80, 0x24, 0x20, 0x90, 0x24, 0x20, 0x98, 0x10, 0x10, 0x80, 0x90, +0x58, 0x80, 0x90, 0x28, 0x24, 0x1f, 0x90, 0x24, 0x1f, 0x98, 0x81, 0x24, +0x1f, 0x50, 0x92, 0x68, 0x91, 0x00, 0x80, 0x90, 0x90, 0x90, 0x30, 0x80, +0x24, 0x20, 0x00, 0x90, 0x38, 0xa4, 0x1f, 0xf8, 0x34, 0x06, 0x80, 0x34, +0x05, 0x80, 0x90, 0x28, 0x80, 0x34, 0x0f, 0xa4, 0x1f, 0xe0, 0x34, 0x0e, +0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x80, 0x34, 0x09, 0xa4, 0x1f, +0xf0, 0x34, 0x08, 0x90, 0x28, 0x80, 0x34, 0x04, 0xa4, 0x1f, 0xe8, 0x34, +0x03, 0x90, 0x50, 0x90, 0x28, 0x80, 0x34, 0x0d, 0x80, 0x34, 0x0c, 0x90, +0x28, 0x24, 0x20, 0x88, 0x24, 0x20, 0x80, 0x90, 0x58, 0x80, 0x10, 0x10, +0x80, 0x10, 0x10, 0x80, 0x33, 0xfb, 0x80, 0x90, 0x40, 0x10, 0x10, 0x80, +0x24, 0x1f, 0x60, 0x80, 0x10, 0x10, 0x80, 0x33, 0xfa, 0x91, 0x58, 0x91, +0x00, 0x90, 0x80, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xf6, 0x80, +0x33, 0xf7, 0x81, 0x33, 0xee, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0xf4, 0x80, 0x33, 0xf5, 0x81, 0x33, 0xed, 0x83, 0x90, 0x28, 0x24, 0x1f, +0x80, 0x24, 0x1f, 0x88, 0x90, 0xe8, 0x81, 0x90, 0x88, 0x90, 0x38, 0x10, +0x10, 0x80, 0x34, 0x07, 0x90, 0x28, 0x80, 0x34, 0x02, 0x80, 0x34, 0x01, +0x80, 0x90, 0x28, 0x80, 0x34, 0x0b, 0x80, 0x34, 0x0a, 0x82, 0x10, 0x10, +0x80, 0x24, 0x1f, 0x58, 0x97, 0x10, 0x9e, 0x10, 0x06, 0x98, 0x93, 0x00, +0x91, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x80, 0x30, +0x71, 0x24, 0x03, 0x78, 0x90, 0x38, 0xa4, 0x04, 0x10, 0x30, 0x83, 0x24, +0x04, 0x08, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x30, 0x30, 0xa7, 0x24, +0x05, 0x28, 0x90, 0x38, 0xa4, 0x04, 0xa0, 0x30, 0x95, 0x24, 0x04, 0x98, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x70, 0x30, 0x6c, 0x24, +0x03, 0x68, 0x90, 0x38, 0xa4, 0x04, 0x00, 0x30, 0x7e, 0x24, 0x03, 0xf8, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x20, 0x30, 0xa2, 0x24, 0x05, 0x18, +0x90, 0x38, 0xa4, 0x04, 0x90, 0x30, 0x90, 0x24, 0x04, 0x88, 0x91, 0x80, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x58, 0x30, 0x69, 0x24, +0x03, 0x50, 0x90, 0x38, 0xa4, 0x03, 0xe8, 0x30, 0x7b, 0x24, 0x03, 0xe0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x08, 0x30, 0x9f, 0x24, 0x05, 0x00, +0x90, 0x38, 0xa4, 0x04, 0x78, 0x30, 0x8d, 0x24, 0x04, 0x70, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x40, 0x30, 0x66, 0x24, 0x03, 0x38, +0x90, 0x38, 0xa4, 0x03, 0xd0, 0x30, 0x78, 0x24, 0x03, 0xc8, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x04, 0xf0, 0x30, 0x9c, 0x24, 0x04, 0xe8, 0x90, 0x38, +0xa4, 0x04, 0x60, 0x30, 0x8a, 0x24, 0x04, 0x58, 0x10, 0x10, 0x80, 0x10, +0x10, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x02, 0xf8, 0x30, 0x5d, 0x24, 0x02, +0xf0, 0xd7, 0x42, 0x00, 0xa4, 0x39, 0x58, 0x37, 0x2d, 0xa4, 0x39, 0x38, +0x37, 0x29, 0x9c, 0xe0, 0x06, 0x90, 0x93, 0x00, 0x91, 0x80, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x28, 0x30, 0x63, 0x24, 0x03, 0x20, +0x90, 0x38, 0xa4, 0x03, 0xb8, 0x30, 0x75, 0x24, 0x03, 0xb0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x04, 0xd8, 0x30, 0x99, 0x24, 0x04, 0xd0, 0x90, 0x38, +0xa4, 0x04, 0x48, 0x30, 0x87, 0x24, 0x04, 0x40, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x03, 0x10, 0x30, 0x60, 0x24, 0x03, 0x08, 0x90, 0x38, +0xa4, 0x03, 0xa0, 0x30, 0x72, 0x24, 0x03, 0x98, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x04, 0xc0, 0x30, 0x96, 0x24, 0x04, 0xb8, 0x90, 0x38, 0xa4, 0x04, +0x30, 0x30, 0x84, 0x24, 0x04, 0x28, 0x10, 0x10, 0x90, 0xe0, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x02, 0x88, 0x30, 0x52, 0xa4, 0x02, 0x78, 0x30, 0x50, +0x90, 0x38, 0xa4, 0x02, 0x70, 0x30, 0x4b, 0xa4, 0x02, 0x60, 0x30, 0x4d, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x02, 0x50, 0x30, 0x43, 0xa4, 0x02, 0x40, +0x30, 0x49, 0x90, 0x38, 0xa4, 0x02, 0x38, 0x30, 0x44, 0xa4, 0x02, 0x28, +0x30, 0x46, 0x91, 0x48, 0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, +0x30, 0x56, 0x24, 0x02, 0xa8, 0x90, 0x28, 0x80, 0x30, 0x58, 0x24, 0x02, +0xb8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0x5c, 0x24, 0x02, 0xd8, 0x90, +0x28, 0x80, 0x30, 0x5a, 0x24, 0x02, 0xc8, 0x80, 0x10, 0x10, 0x10, 0x10, +0x90, 0x28, 0x80, 0x30, 0x53, 0x24, 0x02, 0xa0, 0xd7, 0x42, 0x00, 0xa4, +0x39, 0x60, 0x37, 0x2e, 0xa4, 0x39, 0x40, 0x37, 0x2a, 0xa0, 0x14, 0x68, +0xa0, 0x10, 0x90, 0xa0, 0x0c, 0x60, 0x9e, 0x88, 0x09, 0xd0, 0x94, 0xf0, +0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4c, 0x40, +0x85, 0x35, 0x4d, 0xcb, 0x61, 0x45, 0x00, 0x85, 0x35, 0x23, 0x9a, 0x00, +0x03, 0xf8, 0x91, 0x98, 0x80, 0x91, 0x10, 0x90, 0xa0, 0x90, 0x68, 0x90, +0x20, 0x3a, 0x75, 0xc9, 0xe2, 0x9c, 0xc0, 0x85, 0x35, 0x4b, 0xa4, 0x53, +0x88, 0x3a, 0x72, 0x90, 0x38, 0xa4, 0x53, 0x50, 0x3a, 0x6b, 0xa4, 0x53, +0x40, 0x3a, 0x69, 0x90, 0x48, 0x10, 0x10, 0xa4, 0x53, 0x08, 0x3a, 0x62, +0x10, 0x10, 0x80, 0x3a, 0x5e, 0x81, 0x10, 0x10, 0x80, 0xa4, 0x52, 0xd8, +0x3a, 0x5c, 0x91, 0xb0, 0x91, 0x60, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x53, 0x78, 0x3a, 0x70, 0xa4, 0x53, 0x68, 0x3a, 0x6e, 0x90, 0x38, +0xa4, 0x53, 0x30, 0x3a, 0x67, 0xa4, 0x53, 0x20, 0x3a, 0x65, 0x90, 0x48, +0x10, 0x10, 0xa4, 0x52, 0xf8, 0x3a, 0x60, 0x10, 0x10, 0x80, 0x3a, 0x5d, +0x90, 0x28, 0x80, 0x3a, 0x56, 0x80, 0x3a, 0x55, 0x81, 0x10, 0x10, 0x80, +0xa4, 0x52, 0xc8, 0x3a, 0x5a, 0xcb, 0x61, 0x44, 0xc0, 0x85, 0x35, 0x22, +0x90, 0xd8, 0x88, 0x00, 0x90, 0x84, 0x90, 0x38, 0xc1, 0xc0, 0x85, 0x3a, +0x78, 0xc9, 0xe1, 0x4c, 0x00, 0x85, 0x35, 0x49, 0xcb, 0x61, 0x44, 0x80, +0x85, 0x35, 0x21, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4b, +0xc0, 0x85, 0x35, 0x47, 0xcb, 0x61, 0x44, 0x40, 0x85, 0x35, 0x20, 0x91, +0xf8, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4b, +0x40, 0x85, 0x35, 0x43, 0xcb, 0x61, 0x43, 0xc0, 0x85, 0x35, 0x1e, 0x88, +0x01, 0x00, 0x90, 0xa0, 0x81, 0x90, 0x70, 0x80, 0x90, 0x20, 0x3a, 0x6c, +0xc9, 0xe1, 0x4b, 0x00, 0x85, 0x35, 0x41, 0x81, 0x3a, 0x63, 0x81, 0x10, +0x10, 0x80, 0xa4, 0x52, 0xb8, 0x3a, 0x58, 0xcb, 0x61, 0x43, 0x80, 0x85, +0x35, 0x1d, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, +0x4a, 0xc0, 0x85, 0x35, 0x3f, 0xcb, 0x61, 0x43, 0x40, 0x85, 0x35, 0x1c, +0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4a, 0x80, 0x85, 0x35, +0x3d, 0xcb, 0x61, 0x43, 0x00, 0x85, 0x35, 0x1b, 0x92, 0x38, 0x81, 0x91, +0x68, 0x91, 0x18, 0x90, 0x80, 0x90, 0x40, 0x80, 0xa4, 0x54, 0x38, 0x3a, +0x88, 0x80, 0xa4, 0x54, 0x30, 0x3a, 0x85, 0x90, 0x28, 0x81, 0x3a, 0x84, +0x90, 0x38, 0xa4, 0x54, 0x10, 0x3a, 0x83, 0xa4, 0x54, 0x00, 0x3a, 0x81, +0x90, 0x28, 0x80, 0x3a, 0x7f, 0x80, 0x3a, 0x7e, 0x80, 0x90, 0x40, 0x10, +0x10, 0x80, 0x24, 0x53, 0xe8, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x53, 0xd8, +0x3a, 0x7c, 0xa4, 0x53, 0xc8, 0x3a, 0x7a, 0x90, 0x28, 0x80, 0x3a, 0x77, +0x80, 0x3a, 0x76, 0x9a, 0xd0, 0x03, 0xe0, 0x91, 0x60, 0x90, 0xb0, 0x88, +0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4a, 0x00, 0x85, 0x35, 0x39, +0xcb, 0x61, 0x42, 0x80, 0x85, 0x35, 0x19, 0x88, 0x00, 0x68, 0x84, 0x10, +0x10, 0xc9, 0xe1, 0x49, 0xc0, 0x85, 0x35, 0x37, 0xcb, 0x61, 0x42, 0x40, +0x85, 0x35, 0x18, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, +0xe1, 0x49, 0x80, 0x85, 0x35, 0x35, 0xcb, 0x61, 0x42, 0x00, 0x85, 0x35, +0x17, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x49, 0x40, 0x85, +0x35, 0x33, 0xcb, 0x61, 0x41, 0xc0, 0x85, 0x35, 0x16, 0x90, 0x90, 0x90, +0x48, 0xcb, 0xa1, 0x40, 0x00, 0x85, 0x35, 0x05, 0xcb, 0xa1, 0x3f, 0xc0, +0x85, 0x35, 0x04, 0x90, 0x48, 0xcb, 0xa1, 0x3f, 0x80, 0x85, 0x35, 0x03, +0xcb, 0xa1, 0x3f, 0x40, 0x85, 0x35, 0x02, 0xcb, 0xa2, 0x94, 0xc0, 0x80, +0x3a, 0x54, 0x92, 0x40, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, +0x60, 0x84, 0x24, 0x27, 0xd8, 0x8c, 0x27, 0x58, 0x84, 0x24, 0x27, 0xd0, +0x90, 0x48, 0x8c, 0x27, 0x50, 0x84, 0x24, 0x27, 0xc8, 0x8c, 0x27, 0x48, +0x84, 0x24, 0x27, 0xc0, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, 0x38, 0x84, +0x24, 0x27, 0xb0, 0x8c, 0x27, 0x30, 0x84, 0x24, 0x27, 0xa8, 0x90, 0x48, +0x8c, 0x27, 0x28, 0x84, 0x24, 0x27, 0xa0, 0x8c, 0x27, 0x20, 0x84, 0x24, +0x27, 0x98, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, 0x10, 0x84, +0x24, 0x27, 0x88, 0x8c, 0x27, 0x08, 0x84, 0x24, 0x27, 0x80, 0x90, 0x48, +0x8c, 0x27, 0x00, 0x84, 0x24, 0x27, 0x78, 0x8c, 0x26, 0xf8, 0x84, 0x24, +0x27, 0x70, 0x90, 0x38, 0xa4, 0x26, 0xe0, 0x34, 0xdd, 0xa4, 0x26, 0xd0, +0x34, 0xdb, 0xa0, 0x0f, 0x50, 0xa0, 0x09, 0x08, 0x9a, 0x30, 0x04, 0x40, +0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x92, 0xc0, +0x3a, 0x43, 0xe5, 0x22, 0x8a, 0xc0, 0x3a, 0x3f, 0xcb, 0x61, 0x32, 0x40, +0x85, 0x34, 0xd8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x82, 0xc0, 0x3a, +0x03, 0xe5, 0x22, 0x7a, 0xc0, 0x39, 0xff, 0xcb, 0x61, 0x32, 0x00, 0x85, +0x34, 0xd7, 0x90, 0x48, 0xcb, 0xa1, 0x31, 0xc0, 0x85, 0x34, 0xd6, 0xcb, +0xa1, 0x31, 0x80, 0x85, 0x34, 0xd5, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, +0x00, 0x80, 0xe5, 0x22, 0x6c, 0xc0, 0x39, 0xcb, 0xe5, 0x22, 0x60, 0xc0, +0x39, 0x9b, 0xcb, 0x61, 0x31, 0x00, 0x85, 0x34, 0xd3, 0x98, 0x50, 0x00, +0x80, 0xe5, 0x22, 0x54, 0xc0, 0x39, 0x6b, 0xe5, 0x22, 0x48, 0xc0, 0x39, +0x3b, 0xcb, 0x61, 0x30, 0xc0, 0x85, 0x34, 0xd2, 0x90, 0x48, 0xcb, 0xa1, +0x30, 0x80, 0x85, 0x34, 0xd1, 0xcb, 0xa1, 0x30, 0x40, 0x85, 0x34, 0xd0, +0x92, 0x20, 0x91, 0x30, 0x90, 0xb8, 0xd5, 0x03, 0x00, 0xc0, 0xc0, 0x81, +0x8c, 0x01, 0xa0, 0x84, 0x30, 0x3e, 0xc0, 0xc0, 0x81, 0x8c, 0x01, 0x80, +0x84, 0x30, 0x3c, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, 0x30, 0x28, 0xc0, +0xc0, 0x81, 0x30, 0x24, 0x90, 0x78, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, +0x30, 0x1c, 0xc0, 0xc0, 0x81, 0x30, 0x18, 0xd5, 0x02, 0x00, 0xc0, 0xc0, +0x81, 0x30, 0x10, 0xc0, 0xc0, 0x81, 0x30, 0x0c, 0x91, 0x70, 0x90, 0xd8, +0xd5, 0x03, 0x80, 0xc8, 0xe2, 0x40, 0xc0, 0x81, 0x8c, 0x01, 0xc0, 0x84, +0x30, 0x40, 0xc8, 0xe2, 0x42, 0xc0, 0x81, 0x8c, 0x01, 0x90, 0x84, 0x30, +0x3d, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x3f, 0xc0, 0x81, 0x30, 0x2c, 0xc8, +0xe2, 0x3a, 0x40, 0x81, 0x30, 0x26, 0x90, 0x98, 0xd5, 0x02, 0x80, 0xc8, +0xe2, 0x2f, 0x40, 0x81, 0x30, 0x20, 0xc8, 0xe2, 0x31, 0x40, 0x81, 0x30, +0x1a, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x2e, 0x40, 0x81, 0x30, 0x14, 0xc8, +0xe2, 0x28, 0xc0, 0x81, 0x30, 0x0e, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x90, +0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x86, 0xc0, 0x3a, 0x13, +0xe5, 0x22, 0x88, 0xc0, 0x3a, 0x37, 0xcb, 0x61, 0x2f, 0xc0, 0x85, 0x34, +0xce, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x76, 0xc0, 0x39, 0xd3, 0xe5, +0x22, 0x78, 0xc0, 0x39, 0xf7, 0xcb, 0x61, 0x2f, 0x80, 0x85, 0x34, 0xcd, +0x90, 0x48, 0xcb, 0xa1, 0x2f, 0x40, 0x85, 0x34, 0xcc, 0xcb, 0xa1, 0x2f, +0x00, 0x85, 0x34, 0xcb, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, +0xe5, 0x22, 0x68, 0xc0, 0x39, 0xbb, 0xe5, 0x22, 0x5c, 0xc0, 0x39, 0x8b, +0xcb, 0x61, 0x2d, 0x40, 0x85, 0x34, 0xba, 0x98, 0x50, 0x00, 0x80, 0xe5, +0x22, 0x50, 0xc0, 0x39, 0x5b, 0xe5, 0x22, 0x44, 0xc0, 0x39, 0x2b, 0xcb, +0x61, 0x2d, 0x00, 0x85, 0x34, 0xb9, 0x90, 0x48, 0xcb, 0xa1, 0x2c, 0xc0, +0x85, 0x34, 0xb8, 0xcb, 0xa1, 0x2c, 0x80, 0x85, 0x34, 0xb7, 0x91, 0x00, +0x90, 0x80, 0x90, 0x40, 0xe5, 0x20, 0x02, 0x40, 0x30, 0x0a, 0xe5, 0x20, +0x01, 0x80, 0x30, 0x07, 0x90, 0x40, 0xe5, 0x20, 0x00, 0xc0, 0x30, 0x04, +0xe5, 0x20, 0x00, 0x00, 0x30, 0x01, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, +0x35, 0xc0, 0x38, 0xcd, 0xe5, 0x22, 0x38, 0x00, 0x38, 0xf5, 0x90, 0x40, +0xe5, 0x22, 0x24, 0x40, 0x38, 0x87, 0xe5, 0x22, 0x26, 0x80, 0x38, 0xaf, +0x80, 0x99, 0x28, 0x02, 0xf0, 0x8c, 0x25, 0x48, 0x90, 0x80, 0x90, 0x40, +0xe5, 0x22, 0x8c, 0xc0, 0x3a, 0x2f, 0xe5, 0x22, 0x89, 0xc0, 0x3a, 0x3b, +0x90, 0x40, 0xe5, 0x22, 0x7c, 0xc0, 0x39, 0xef, 0xe5, 0x22, 0x79, 0xc0, +0x39, 0xfb, 0x91, 0x48, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, +0x6a, 0xc0, 0x39, 0xc3, 0xe5, 0x22, 0x5e, 0xc0, 0x39, 0x93, 0xcb, 0x61, +0x2b, 0x00, 0x85, 0x34, 0xb0, 0x90, 0x40, 0xe5, 0x22, 0x52, 0xc0, 0x39, +0x63, 0xe5, 0x22, 0x46, 0xc0, 0x39, 0x33, 0x90, 0x48, 0xcb, 0xa1, 0x2a, +0x80, 0x85, 0x34, 0xae, 0xcb, 0xa1, 0x2a, 0xc0, 0x85, 0x34, 0xaf, 0x10, +0x10, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, 0x3c, 0x40, 0x38, 0xed, 0xe5, +0x22, 0x39, 0x40, 0x38, 0xfb, 0x90, 0x40, 0xe5, 0x22, 0x2a, 0xc0, 0x38, +0xa7, 0xe5, 0x22, 0x27, 0xc0, 0x38, 0xb5, +}; + +static const struct ia64_dis_names ia64_dis_names[] = { +{ 0x51, 41, 0, 10 }, +{ 0x31, 41, 1, 20 }, +{ 0x11, 42, 0, 19 }, +{ 0x29, 41, 0, 12 }, +{ 0x19, 41, 1, 24 }, +{ 0x9, 42, 0, 23 }, +{ 0x15, 41, 0, 14 }, +{ 0xd, 41, 1, 28 }, +{ 0x5, 42, 0, 27 }, +{ 0xb, 41, 0, 16 }, +{ 0x7, 41, 1, 32 }, +{ 0x3, 42, 0, 31 }, +{ 0x51, 39, 1, 58 }, +{ 0x50, 39, 0, 34 }, +{ 0xd1, 39, 1, 57 }, +{ 0xd0, 39, 0, 33 }, +{ 0x31, 39, 1, 68 }, +{ 0x30, 39, 1, 44 }, +{ 0x11, 40, 1, 67 }, +{ 0x10, 40, 0, 43 }, +{ 0x71, 39, 1, 66 }, +{ 0x70, 39, 1, 42 }, +{ 0x31, 40, 1, 65 }, +{ 0x30, 40, 0, 41 }, +{ 0x29, 39, 1, 60 }, +{ 0x28, 39, 0, 36 }, +{ 0x69, 39, 1, 59 }, +{ 0x68, 39, 0, 35 }, +{ 0x19, 39, 1, 72 }, +{ 0x18, 39, 1, 48 }, +{ 0x9, 40, 1, 71 }, +{ 0x8, 40, 0, 47 }, +{ 0x39, 39, 1, 70 }, +{ 0x38, 39, 1, 46 }, +{ 0x19, 40, 1, 69 }, +{ 0x18, 40, 0, 45 }, +{ 0x15, 39, 1, 62 }, +{ 0x14, 39, 0, 38 }, +{ 0x35, 39, 1, 61 }, +{ 0x34, 39, 0, 37 }, +{ 0xd, 39, 1, 76 }, +{ 0xc, 39, 1, 52 }, +{ 0x5, 40, 1, 75 }, +{ 0x4, 40, 0, 51 }, +{ 0x1d, 39, 1, 74 }, +{ 0x1c, 39, 1, 50 }, +{ 0xd, 40, 1, 73 }, +{ 0xc, 40, 0, 49 }, +{ 0xb, 39, 1, 64 }, +{ 0xa, 39, 0, 40 }, +{ 0x1b, 39, 1, 63 }, +{ 0x1a, 39, 0, 39 }, +{ 0x7, 39, 1, 80 }, +{ 0x6, 39, 1, 56 }, +{ 0x3, 40, 1, 79 }, +{ 0x2, 40, 0, 55 }, +{ 0xf, 39, 1, 78 }, +{ 0xe, 39, 1, 54 }, +{ 0x7, 40, 1, 77 }, +{ 0x6, 40, 0, 53 }, +{ 0x8, 38, 0, 82 }, +{ 0x18, 38, 0, 81 }, +{ 0x1, 38, 1, 86 }, +{ 0x2, 38, 0, 85 }, +{ 0x3, 38, 1, 84 }, +{ 0x4, 38, 0, 83 }, +{ 0x1, 336, 0, 87 }, +{ 0x20, 289, 0, 98 }, +{ 0x220, 289, 0, 94 }, +{ 0x1220, 289, 0, 91 }, +{ 0xa20, 289, 0, 92 }, +{ 0x620, 289, 0, 93 }, +{ 0x120, 289, 0, 95 }, +{ 0xa0, 289, 0, 96 }, +{ 0x60, 289, 0, 97 }, +{ 0x10, 289, 0, 102 }, +{ 0x90, 289, 0, 99 }, +{ 0x50, 289, 0, 100 }, +{ 0x30, 289, 0, 101 }, +{ 0x8, 289, 0, 103 }, +{ 0x4, 289, 0, 104 }, +{ 0x2, 289, 0, 105 }, +{ 0x1, 289, 0, 106 }, +{ 0x1, 411, 0, 108 }, +{ 0x3, 411, 0, 107 }, +{ 0x2, 417, 0, 109 }, +{ 0x1, 417, 0, 110 }, +{ 0x2, 413, 0, 111 }, +{ 0x1, 413, 0, 112 }, +{ 0x2, 415, 0, 113 }, +{ 0x1, 415, 0, 114 }, +{ 0x2, 419, 0, 115 }, +{ 0x1, 419, 0, 116 }, +{ 0x1, 268, 0, 143 }, +{ 0x5, 268, 0, 141 }, +{ 0x3, 268, 0, 142 }, +{ 0x140, 277, 0, 119 }, +{ 0x540, 277, 0, 117 }, +{ 0x340, 277, 0, 118 }, +{ 0xc0, 277, 0, 131 }, +{ 0x2c0, 277, 0, 129 }, +{ 0x1c0, 277, 0, 130 }, +{ 0x20, 277, 0, 146 }, +{ 0xa0, 277, 0, 144 }, +{ 0x60, 277, 0, 145 }, +{ 0x10, 277, 0, 158 }, +{ 0x50, 277, 0, 156 }, +{ 0x30, 277, 0, 157 }, +{ 0x8, 277, 0, 170 }, +{ 0x28, 277, 0, 168 }, +{ 0x18, 277, 0, 169 }, +{ 0x4, 277, 0, 180 }, +{ 0x2, 277, 0, 181 }, +{ 0x1, 277, 0, 182 }, +{ 0x140, 271, 0, 122 }, +{ 0x540, 271, 0, 120 }, +{ 0x340, 271, 0, 121 }, +{ 0xc0, 271, 0, 134 }, +{ 0x2c0, 271, 0, 132 }, +{ 0x1c0, 271, 0, 133 }, +{ 0x20, 271, 0, 149 }, +{ 0xa0, 271, 0, 147 }, +{ 0x60, 271, 0, 148 }, +{ 0x10, 271, 0, 161 }, +{ 0x50, 271, 0, 159 }, +{ 0x30, 271, 0, 160 }, +{ 0x8, 271, 0, 173 }, +{ 0x28, 271, 0, 171 }, +{ 0x18, 271, 0, 172 }, +{ 0x4, 271, 0, 183 }, +{ 0x2, 271, 0, 184 }, +{ 0x1, 271, 0, 185 }, +{ 0x140, 274, 0, 125 }, +{ 0x540, 274, 0, 123 }, +{ 0x340, 274, 0, 124 }, +{ 0xc0, 274, 0, 137 }, +{ 0x2c0, 274, 0, 135 }, +{ 0x1c0, 274, 0, 136 }, +{ 0x20, 274, 0, 152 }, +{ 0xa0, 274, 0, 150 }, +{ 0x60, 274, 0, 151 }, +{ 0x10, 274, 0, 164 }, +{ 0x50, 274, 0, 162 }, +{ 0x30, 274, 0, 163 }, +{ 0x8, 274, 0, 176 }, +{ 0x28, 274, 0, 174 }, +{ 0x18, 274, 0, 175 }, +{ 0x4, 274, 0, 186 }, +{ 0x2, 274, 0, 187 }, +{ 0x1, 274, 0, 188 }, +{ 0x140, 286, 0, 128 }, +{ 0x540, 286, 0, 126 }, +{ 0x340, 286, 0, 127 }, +{ 0xc0, 286, 0, 140 }, +{ 0x2c0, 286, 0, 138 }, +{ 0x1c0, 286, 0, 139 }, +{ 0x20, 286, 0, 155 }, +{ 0xa0, 286, 0, 153 }, +{ 0x60, 286, 0, 154 }, +{ 0x10, 286, 0, 167 }, +{ 0x50, 286, 0, 165 }, +{ 0x30, 286, 0, 166 }, +{ 0x8, 286, 0, 179 }, +{ 0x28, 286, 0, 177 }, +{ 0x18, 286, 0, 178 }, +{ 0x4, 286, 0, 189 }, +{ 0x2, 286, 0, 190 }, +{ 0x1, 286, 0, 191 }, +{ 0x8, 390, 0, 192 }, +{ 0x4, 390, 0, 193 }, +{ 0x2, 390, 0, 194 }, +{ 0x1, 390, 0, 195 }, +{ 0x20, 288, 0, 203 }, +{ 0x220, 288, 0, 199 }, +{ 0x1220, 288, 0, 196 }, +{ 0xa20, 288, 0, 197 }, +{ 0x620, 288, 0, 198 }, +{ 0x120, 288, 0, 200 }, +{ 0xa0, 288, 0, 201 }, +{ 0x60, 288, 0, 202 }, +{ 0x10, 288, 0, 207 }, +{ 0x90, 288, 0, 204 }, +{ 0x50, 288, 0, 205 }, +{ 0x30, 288, 0, 206 }, +{ 0x8, 288, 0, 208 }, +{ 0x4, 288, 0, 209 }, +{ 0x2, 288, 0, 210 }, +{ 0x1, 288, 0, 211 }, +{ 0x20, 287, 0, 219 }, +{ 0x220, 287, 0, 215 }, +{ 0x1220, 287, 0, 212 }, +{ 0xa20, 287, 0, 213 }, +{ 0x620, 287, 0, 214 }, +{ 0x120, 287, 0, 216 }, +{ 0xa0, 287, 0, 217 }, +{ 0x60, 287, 0, 218 }, +{ 0x10, 287, 0, 223 }, +{ 0x90, 287, 0, 220 }, +{ 0x50, 287, 0, 221 }, +{ 0x30, 287, 0, 222 }, +{ 0x8, 287, 0, 224 }, +{ 0x4, 287, 0, 225 }, +{ 0x2, 287, 0, 226 }, +{ 0x1, 287, 0, 227 }, +{ 0x140, 279, 0, 230 }, +{ 0x540, 279, 0, 228 }, +{ 0x340, 279, 0, 229 }, +{ 0xc0, 279, 0, 239 }, +{ 0x2c0, 279, 0, 237 }, +{ 0x1c0, 279, 0, 238 }, +{ 0x20, 279, 0, 248 }, +{ 0xa0, 279, 0, 246 }, +{ 0x60, 279, 0, 247 }, +{ 0x10, 279, 0, 257 }, +{ 0x50, 279, 0, 255 }, +{ 0x30, 279, 0, 256 }, +{ 0x8, 279, 0, 266 }, +{ 0x28, 279, 0, 264 }, +{ 0x18, 279, 0, 265 }, +{ 0x4, 279, 0, 273 }, +{ 0x2, 279, 0, 274 }, +{ 0x1, 279, 0, 275 }, +{ 0x140, 281, 0, 233 }, +{ 0x540, 281, 0, 231 }, +{ 0x340, 281, 0, 232 }, +{ 0xc0, 281, 0, 242 }, +{ 0x2c0, 281, 0, 240 }, +{ 0x1c0, 281, 0, 241 }, +{ 0x20, 281, 0, 251 }, +{ 0xa0, 281, 0, 249 }, +{ 0x60, 281, 0, 250 }, +{ 0x10, 281, 0, 260 }, +{ 0x50, 281, 0, 258 }, +{ 0x30, 281, 0, 259 }, +{ 0x8, 281, 0, 269 }, +{ 0x28, 281, 0, 267 }, +{ 0x18, 281, 0, 268 }, +{ 0x4, 281, 0, 276 }, +{ 0x2, 281, 0, 277 }, +{ 0x1, 281, 0, 278 }, +{ 0x140, 283, 0, 236 }, +{ 0x540, 283, 0, 234 }, +{ 0x340, 283, 0, 235 }, +{ 0xc0, 283, 0, 245 }, +{ 0x2c0, 283, 0, 243 }, +{ 0x1c0, 283, 0, 244 }, +{ 0x20, 283, 0, 254 }, +{ 0xa0, 283, 0, 252 }, +{ 0x60, 283, 0, 253 }, +{ 0x10, 283, 0, 263 }, +{ 0x50, 283, 0, 261 }, +{ 0x30, 283, 0, 262 }, +{ 0x8, 283, 0, 272 }, +{ 0x28, 283, 0, 270 }, +{ 0x18, 283, 0, 271 }, +{ 0x4, 283, 0, 279 }, +{ 0x2, 283, 0, 280 }, +{ 0x1, 283, 0, 281 }, +{ 0x140, 278, 0, 284 }, +{ 0x540, 278, 0, 282 }, +{ 0x340, 278, 0, 283 }, +{ 0xc0, 278, 0, 293 }, +{ 0x2c0, 278, 0, 291 }, +{ 0x1c0, 278, 0, 292 }, +{ 0x20, 278, 0, 302 }, +{ 0xa0, 278, 0, 300 }, +{ 0x60, 278, 0, 301 }, +{ 0x10, 278, 0, 311 }, +{ 0x50, 278, 0, 309 }, +{ 0x30, 278, 0, 310 }, +{ 0x8, 278, 0, 320 }, +{ 0x28, 278, 0, 318 }, +{ 0x18, 278, 0, 319 }, +{ 0x4, 278, 0, 327 }, +{ 0x2, 278, 0, 328 }, +{ 0x1, 278, 0, 329 }, +{ 0x140, 280, 0, 287 }, +{ 0x540, 280, 0, 285 }, +{ 0x340, 280, 0, 286 }, +{ 0xc0, 280, 0, 296 }, +{ 0x2c0, 280, 0, 294 }, +{ 0x1c0, 280, 0, 295 }, +{ 0x20, 280, 0, 305 }, +{ 0xa0, 280, 0, 303 }, +{ 0x60, 280, 0, 304 }, +{ 0x10, 280, 0, 314 }, +{ 0x50, 280, 0, 312 }, +{ 0x30, 280, 0, 313 }, +{ 0x8, 280, 0, 323 }, +{ 0x28, 280, 0, 321 }, +{ 0x18, 280, 0, 322 }, +{ 0x4, 280, 0, 330 }, +{ 0x2, 280, 0, 331 }, +{ 0x1, 280, 0, 332 }, +{ 0x140, 282, 0, 290 }, +{ 0x540, 282, 0, 288 }, +{ 0x340, 282, 0, 289 }, +{ 0xc0, 282, 0, 299 }, +{ 0x2c0, 282, 0, 297 }, +{ 0x1c0, 282, 0, 298 }, +{ 0x20, 282, 0, 308 }, +{ 0xa0, 282, 0, 306 }, +{ 0x60, 282, 0, 307 }, +{ 0x10, 282, 0, 317 }, +{ 0x50, 282, 0, 315 }, +{ 0x30, 282, 0, 316 }, +{ 0x8, 282, 0, 326 }, +{ 0x28, 282, 0, 324 }, +{ 0x18, 282, 0, 325 }, +{ 0x4, 282, 0, 333 }, +{ 0x2, 282, 0, 334 }, +{ 0x1, 282, 0, 335 }, +{ 0x1, 410, 0, 337 }, +{ 0x3, 410, 0, 336 }, +{ 0x2, 416, 0, 338 }, +{ 0x1, 416, 0, 339 }, +{ 0x2, 412, 0, 340 }, +{ 0x1, 412, 0, 341 }, +{ 0x2, 414, 0, 342 }, +{ 0x1, 414, 0, 343 }, +{ 0x2, 418, 0, 344 }, +{ 0x1, 418, 0, 345 }, +{ 0x1, 267, 0, 372 }, +{ 0x5, 267, 0, 370 }, +{ 0x3, 267, 0, 371 }, +{ 0x140, 276, 0, 348 }, +{ 0x540, 276, 0, 346 }, +{ 0x340, 276, 0, 347 }, +{ 0xc0, 276, 0, 360 }, +{ 0x2c0, 276, 0, 358 }, +{ 0x1c0, 276, 0, 359 }, +{ 0x20, 276, 0, 375 }, +{ 0xa0, 276, 0, 373 }, +{ 0x60, 276, 0, 374 }, +{ 0x10, 276, 0, 387 }, +{ 0x50, 276, 0, 385 }, +{ 0x30, 276, 0, 386 }, +{ 0x8, 276, 0, 399 }, +{ 0x28, 276, 0, 397 }, +{ 0x18, 276, 0, 398 }, +{ 0x4, 276, 0, 409 }, +{ 0x2, 276, 0, 410 }, +{ 0x1, 276, 0, 411 }, +{ 0x140, 270, 0, 351 }, +{ 0x540, 270, 0, 349 }, +{ 0x340, 270, 0, 350 }, +{ 0xc0, 270, 0, 363 }, +{ 0x2c0, 270, 0, 361 }, +{ 0x1c0, 270, 0, 362 }, +{ 0x20, 270, 0, 378 }, +{ 0xa0, 270, 0, 376 }, +{ 0x60, 270, 0, 377 }, +{ 0x10, 270, 0, 390 }, +{ 0x50, 270, 0, 388 }, +{ 0x30, 270, 0, 389 }, +{ 0x8, 270, 0, 402 }, +{ 0x28, 270, 0, 400 }, +{ 0x18, 270, 0, 401 }, +{ 0x4, 270, 0, 412 }, +{ 0x2, 270, 0, 413 }, +{ 0x1, 270, 0, 414 }, +{ 0x140, 273, 0, 354 }, +{ 0x540, 273, 0, 352 }, +{ 0x340, 273, 0, 353 }, +{ 0xc0, 273, 0, 366 }, +{ 0x2c0, 273, 0, 364 }, +{ 0x1c0, 273, 0, 365 }, +{ 0x20, 273, 0, 381 }, +{ 0xa0, 273, 0, 379 }, +{ 0x60, 273, 0, 380 }, +{ 0x10, 273, 0, 393 }, +{ 0x50, 273, 0, 391 }, +{ 0x30, 273, 0, 392 }, +{ 0x8, 273, 0, 405 }, +{ 0x28, 273, 0, 403 }, +{ 0x18, 273, 0, 404 }, +{ 0x4, 273, 0, 415 }, +{ 0x2, 273, 0, 416 }, +{ 0x1, 273, 0, 417 }, +{ 0x140, 285, 0, 357 }, +{ 0x540, 285, 0, 355 }, +{ 0x340, 285, 0, 356 }, +{ 0xc0, 285, 0, 369 }, +{ 0x2c0, 285, 0, 367 }, +{ 0x1c0, 285, 0, 368 }, +{ 0x20, 285, 0, 384 }, +{ 0xa0, 285, 0, 382 }, +{ 0x60, 285, 0, 383 }, +{ 0x10, 285, 0, 396 }, +{ 0x50, 285, 0, 394 }, +{ 0x30, 285, 0, 395 }, +{ 0x8, 285, 0, 408 }, +{ 0x28, 285, 0, 406 }, +{ 0x18, 285, 0, 407 }, +{ 0x4, 285, 0, 418 }, +{ 0x2, 285, 0, 419 }, +{ 0x1, 285, 0, 420 }, +{ 0x1, 266, 0, 447 }, +{ 0x5, 266, 0, 445 }, +{ 0x3, 266, 0, 446 }, +{ 0x140, 275, 0, 423 }, +{ 0x540, 275, 0, 421 }, +{ 0x340, 275, 0, 422 }, +{ 0xc0, 275, 0, 435 }, +{ 0x2c0, 275, 0, 433 }, +{ 0x1c0, 275, 0, 434 }, +{ 0x20, 275, 0, 450 }, +{ 0xa0, 275, 0, 448 }, +{ 0x60, 275, 0, 449 }, +{ 0x10, 275, 0, 462 }, +{ 0x50, 275, 0, 460 }, +{ 0x30, 275, 0, 461 }, +{ 0x8, 275, 0, 474 }, +{ 0x28, 275, 0, 472 }, +{ 0x18, 275, 0, 473 }, +{ 0x4, 275, 0, 484 }, +{ 0x2, 275, 0, 485 }, +{ 0x1, 275, 0, 486 }, +{ 0x140, 269, 0, 426 }, +{ 0x540, 269, 0, 424 }, +{ 0x340, 269, 0, 425 }, +{ 0xc0, 269, 0, 438 }, +{ 0x2c0, 269, 0, 436 }, +{ 0x1c0, 269, 0, 437 }, +{ 0x20, 269, 0, 453 }, +{ 0xa0, 269, 0, 451 }, +{ 0x60, 269, 0, 452 }, +{ 0x10, 269, 0, 465 }, +{ 0x50, 269, 0, 463 }, +{ 0x30, 269, 0, 464 }, +{ 0x8, 269, 0, 477 }, +{ 0x28, 269, 0, 475 }, +{ 0x18, 269, 0, 476 }, +{ 0x4, 269, 0, 487 }, +{ 0x2, 269, 0, 488 }, +{ 0x1, 269, 0, 489 }, +{ 0x140, 272, 0, 429 }, +{ 0x540, 272, 0, 427 }, +{ 0x340, 272, 0, 428 }, +{ 0xc0, 272, 0, 441 }, +{ 0x2c0, 272, 0, 439 }, +{ 0x1c0, 272, 0, 440 }, +{ 0x20, 272, 0, 456 }, +{ 0xa0, 272, 0, 454 }, +{ 0x60, 272, 0, 455 }, +{ 0x10, 272, 0, 468 }, +{ 0x50, 272, 0, 466 }, +{ 0x30, 272, 0, 467 }, +{ 0x8, 272, 0, 480 }, +{ 0x28, 272, 0, 478 }, +{ 0x18, 272, 0, 479 }, +{ 0x4, 272, 0, 490 }, +{ 0x2, 272, 0, 491 }, +{ 0x1, 272, 0, 492 }, +{ 0x140, 284, 0, 432 }, +{ 0x540, 284, 0, 430 }, +{ 0x340, 284, 0, 431 }, +{ 0xc0, 284, 0, 444 }, +{ 0x2c0, 284, 0, 442 }, +{ 0x1c0, 284, 0, 443 }, +{ 0x20, 284, 0, 459 }, +{ 0xa0, 284, 0, 457 }, +{ 0x60, 284, 0, 458 }, +{ 0x10, 284, 0, 471 }, +{ 0x50, 284, 0, 469 }, +{ 0x30, 284, 0, 470 }, +{ 0x8, 284, 0, 483 }, +{ 0x28, 284, 0, 481 }, +{ 0x18, 284, 0, 482 }, +{ 0x4, 284, 0, 493 }, +{ 0x2, 284, 0, 494 }, +{ 0x1, 284, 0, 495 }, +{ 0x8, 409, 0, 497 }, +{ 0x18, 409, 0, 496 }, +{ 0x4, 409, 0, 499 }, +{ 0xc, 409, 0, 498 }, +{ 0x2, 409, 0, 506 }, +{ 0x1, 409, 0, 507 }, +{ 0x4, 407, 0, 501 }, +{ 0xc, 407, 0, 500 }, +{ 0x2, 407, 0, 508 }, +{ 0x1, 407, 0, 509 }, +{ 0x4, 405, 0, 503 }, +{ 0xc, 405, 0, 502 }, +{ 0x2, 405, 0, 510 }, +{ 0x1, 405, 0, 511 }, +{ 0x4, 401, 0, 505 }, +{ 0xc, 401, 0, 504 }, +{ 0x2, 401, 0, 512 }, +{ 0x1, 401, 0, 513 }, +{ 0xa00, 265, 0, 528 }, +{ 0x2a00, 265, 0, 526 }, +{ 0x1a00, 265, 0, 527 }, +{ 0x600, 265, 0, 540 }, +{ 0x2600, 265, 0, 516 }, +{ 0xa600, 265, 0, 514 }, +{ 0x6600, 265, 0, 515 }, +{ 0x1600, 265, 0, 538 }, +{ 0xe00, 265, 0, 539 }, +{ 0x100, 265, 0, 552 }, +{ 0x500, 265, 0, 550 }, +{ 0x300, 265, 0, 551 }, +{ 0x80, 265, 0, 555 }, +{ 0x280, 265, 0, 553 }, +{ 0x180, 265, 0, 554 }, +{ 0x40, 265, 0, 567 }, +{ 0x140, 265, 0, 565 }, +{ 0xc0, 265, 0, 566 }, +{ 0x20, 265, 0, 579 }, +{ 0xa0, 265, 0, 577 }, +{ 0x60, 265, 0, 578 }, +{ 0x10, 265, 0, 591 }, +{ 0x50, 265, 0, 589 }, +{ 0x30, 265, 0, 590 }, +{ 0x8, 265, 0, 603 }, +{ 0x28, 265, 0, 601 }, +{ 0x18, 265, 0, 602 }, +{ 0x4, 265, 0, 613 }, +{ 0x2, 265, 0, 614 }, +{ 0x1, 265, 0, 615 }, +{ 0x500, 261, 0, 531 }, +{ 0x1500, 261, 0, 529 }, +{ 0xd00, 261, 0, 530 }, +{ 0x300, 261, 0, 543 }, +{ 0x1300, 261, 0, 519 }, +{ 0x5300, 261, 0, 517 }, +{ 0x3300, 261, 0, 518 }, +{ 0xb00, 261, 0, 541 }, +{ 0x700, 261, 0, 542 }, +{ 0x80, 261, 0, 558 }, +{ 0x280, 261, 0, 556 }, +{ 0x180, 261, 0, 557 }, +{ 0x40, 261, 0, 570 }, +{ 0x140, 261, 0, 568 }, +{ 0xc0, 261, 0, 569 }, +{ 0x20, 261, 0, 582 }, +{ 0xa0, 261, 0, 580 }, +{ 0x60, 261, 0, 581 }, +{ 0x10, 261, 0, 594 }, +{ 0x50, 261, 0, 592 }, +{ 0x30, 261, 0, 593 }, +{ 0x8, 261, 0, 606 }, +{ 0x28, 261, 0, 604 }, +{ 0x18, 261, 0, 605 }, +{ 0x4, 261, 0, 616 }, +{ 0x2, 261, 0, 617 }, +{ 0x1, 261, 0, 618 }, +{ 0x500, 258, 0, 534 }, +{ 0x1500, 258, 0, 532 }, +{ 0xd00, 258, 0, 533 }, +{ 0x300, 258, 0, 546 }, +{ 0x1300, 258, 0, 522 }, +{ 0x5300, 258, 0, 520 }, +{ 0x3300, 258, 0, 521 }, +{ 0xb00, 258, 0, 544 }, +{ 0x700, 258, 0, 545 }, +{ 0x80, 258, 0, 561 }, +{ 0x280, 258, 0, 559 }, +{ 0x180, 258, 0, 560 }, +{ 0x40, 258, 0, 573 }, +{ 0x140, 258, 0, 571 }, +{ 0xc0, 258, 0, 572 }, +{ 0x20, 258, 0, 585 }, +{ 0xa0, 258, 0, 583 }, +{ 0x60, 258, 0, 584 }, +{ 0x10, 258, 0, 597 }, +{ 0x50, 258, 0, 595 }, +{ 0x30, 258, 0, 596 }, +{ 0x8, 258, 0, 609 }, +{ 0x28, 258, 0, 607 }, +{ 0x18, 258, 0, 608 }, +{ 0x4, 258, 0, 619 }, +{ 0x2, 258, 0, 620 }, +{ 0x1, 258, 0, 621 }, +{ 0x500, 253, 0, 537 }, +{ 0x1500, 253, 0, 535 }, +{ 0xd00, 253, 0, 536 }, +{ 0x300, 253, 0, 549 }, +{ 0x1300, 253, 0, 525 }, +{ 0x5300, 253, 0, 523 }, +{ 0x3300, 253, 0, 524 }, +{ 0xb00, 253, 0, 547 }, +{ 0x700, 253, 0, 548 }, +{ 0x80, 253, 0, 564 }, +{ 0x280, 253, 0, 562 }, +{ 0x180, 253, 0, 563 }, +{ 0x40, 253, 0, 576 }, +{ 0x140, 253, 0, 574 }, +{ 0xc0, 253, 0, 575 }, +{ 0x20, 253, 0, 588 }, +{ 0xa0, 253, 0, 586 }, +{ 0x60, 253, 0, 587 }, +{ 0x10, 253, 0, 600 }, +{ 0x50, 253, 0, 598 }, +{ 0x30, 253, 0, 599 }, +{ 0x8, 253, 0, 612 }, +{ 0x28, 253, 0, 610 }, +{ 0x18, 253, 0, 611 }, +{ 0x4, 253, 0, 622 }, +{ 0x2, 253, 0, 623 }, +{ 0x1, 253, 0, 624 }, +{ 0x8, 238, 0, 625 }, +{ 0x4, 238, 0, 626 }, +{ 0x2, 238, 0, 627 }, +{ 0x1, 238, 0, 628 }, +{ 0x2, 176, 0, 631 }, +{ 0xa, 176, 0, 629 }, +{ 0x6, 176, 0, 630 }, +{ 0x1, 176, 0, 637 }, +{ 0x5, 176, 0, 635 }, +{ 0x3, 176, 0, 636 }, +{ 0x2, 175, 0, 634 }, +{ 0xa, 175, 0, 632 }, +{ 0x6, 175, 0, 633 }, +{ 0x1, 175, 0, 640 }, +{ 0x5, 175, 0, 638 }, +{ 0x3, 175, 0, 639 }, +{ 0x4, 451, 0, 641 }, +{ 0x2, 451, 0, 642 }, +{ 0x1, 451, 0, 643 }, +{ 0x4, 450, 0, 644 }, +{ 0x2, 450, 0, 645 }, +{ 0x1, 450, 0, 646 }, +{ 0x4, 449, 0, 647 }, +{ 0x2, 449, 0, 648 }, +{ 0x1, 449, 0, 649 }, +{ 0x4, 448, 0, 650 }, +{ 0x2, 448, 0, 651 }, +{ 0x1, 448, 0, 652 }, +{ 0x2, 123, 1, 658 }, +{ 0x2, 124, 0, 657 }, +{ 0xa, 123, 1, 654 }, +{ 0xa, 124, 0, 653 }, +{ 0x6, 123, 1, 656 }, +{ 0x6, 124, 0, 655 }, +{ 0x1, 123, 1, 688 }, +{ 0x1, 124, 0, 687 }, +{ 0x5, 123, 1, 684 }, +{ 0x5, 124, 0, 683 }, +{ 0x3, 123, 1, 686 }, +{ 0x3, 124, 0, 685 }, +{ 0x2, 131, 1, 664 }, +{ 0x2, 132, 0, 663 }, +{ 0xa, 131, 1, 660 }, +{ 0xa, 132, 0, 659 }, +{ 0x6, 131, 1, 662 }, +{ 0x6, 132, 0, 661 }, +{ 0x1, 131, 1, 694 }, +{ 0x1, 132, 0, 693 }, +{ 0x5, 131, 1, 690 }, +{ 0x5, 132, 0, 689 }, +{ 0x3, 131, 1, 692 }, +{ 0x3, 132, 0, 691 }, +{ 0x2, 129, 1, 670 }, +{ 0x2, 130, 0, 669 }, +{ 0xa, 129, 1, 666 }, +{ 0xa, 130, 0, 665 }, +{ 0x6, 129, 1, 668 }, +{ 0x6, 130, 0, 667 }, +{ 0x1, 129, 1, 700 }, +{ 0x1, 130, 0, 699 }, +{ 0x5, 129, 1, 696 }, +{ 0x5, 130, 0, 695 }, +{ 0x3, 129, 1, 698 }, +{ 0x3, 130, 0, 697 }, +{ 0x2, 127, 1, 676 }, +{ 0x2, 128, 0, 675 }, +{ 0xa, 127, 1, 672 }, +{ 0xa, 128, 0, 671 }, +{ 0x6, 127, 1, 674 }, +{ 0x6, 128, 0, 673 }, +{ 0x1, 127, 1, 706 }, +{ 0x1, 128, 0, 705 }, +{ 0x5, 127, 1, 702 }, +{ 0x5, 128, 0, 701 }, +{ 0x3, 127, 1, 704 }, +{ 0x3, 128, 0, 703 }, +{ 0x2, 125, 1, 682 }, +{ 0x2, 126, 0, 681 }, +{ 0xa, 125, 1, 678 }, +{ 0xa, 126, 0, 677 }, +{ 0x6, 125, 1, 680 }, +{ 0x6, 126, 0, 679 }, +{ 0x1, 125, 1, 712 }, +{ 0x1, 126, 0, 711 }, +{ 0x5, 125, 1, 708 }, +{ 0x5, 126, 0, 707 }, +{ 0x3, 125, 1, 710 }, +{ 0x3, 126, 0, 709 }, +{ 0x4, 402, 1, 718 }, +{ 0x4, 403, 0, 717 }, +{ 0xc, 402, 1, 716 }, +{ 0xc, 403, 0, 715 }, +{ 0x2, 402, 1, 728 }, +{ 0x2, 403, 0, 727 }, +{ 0x1, 402, 1, 730 }, +{ 0x1, 403, 0, 729 }, +{ 0x8, 408, 0, 714 }, +{ 0x18, 408, 0, 713 }, +{ 0x4, 408, 0, 720 }, +{ 0xc, 408, 0, 719 }, +{ 0x2, 408, 0, 731 }, +{ 0x1, 408, 0, 732 }, +{ 0x4, 406, 0, 722 }, +{ 0xc, 406, 0, 721 }, +{ 0x2, 406, 0, 733 }, +{ 0x1, 406, 0, 734 }, +{ 0x4, 404, 0, 724 }, +{ 0xc, 404, 0, 723 }, +{ 0x2, 404, 0, 735 }, +{ 0x1, 404, 0, 736 }, +{ 0x4, 400, 0, 726 }, +{ 0xc, 400, 0, 725 }, +{ 0x2, 400, 0, 737 }, +{ 0x1, 400, 0, 738 }, +{ 0xa00, 264, 0, 753 }, +{ 0x2a00, 264, 0, 751 }, +{ 0x1a00, 264, 0, 752 }, +{ 0x600, 264, 0, 765 }, +{ 0x2600, 264, 0, 741 }, +{ 0xa600, 264, 0, 739 }, +{ 0x6600, 264, 0, 740 }, +{ 0x1600, 264, 0, 763 }, +{ 0xe00, 264, 0, 764 }, +{ 0x100, 264, 0, 777 }, +{ 0x500, 264, 0, 775 }, +{ 0x300, 264, 0, 776 }, +{ 0x80, 264, 0, 780 }, +{ 0x280, 264, 0, 778 }, +{ 0x180, 264, 0, 779 }, +{ 0x40, 264, 0, 792 }, +{ 0x140, 264, 0, 790 }, +{ 0xc0, 264, 0, 791 }, +{ 0x20, 264, 0, 804 }, +{ 0xa0, 264, 0, 802 }, +{ 0x60, 264, 0, 803 }, +{ 0x10, 264, 0, 816 }, +{ 0x50, 264, 0, 814 }, +{ 0x30, 264, 0, 815 }, +{ 0x8, 264, 0, 828 }, +{ 0x28, 264, 0, 826 }, +{ 0x18, 264, 0, 827 }, +{ 0x4, 264, 0, 838 }, +{ 0x2, 264, 0, 839 }, +{ 0x1, 264, 0, 840 }, +{ 0x500, 260, 0, 756 }, +{ 0x1500, 260, 0, 754 }, +{ 0xd00, 260, 0, 755 }, +{ 0x300, 260, 0, 768 }, +{ 0x1300, 260, 0, 744 }, +{ 0x5300, 260, 0, 742 }, +{ 0x3300, 260, 0, 743 }, +{ 0xb00, 260, 0, 766 }, +{ 0x700, 260, 0, 767 }, +{ 0x80, 260, 0, 783 }, +{ 0x280, 260, 0, 781 }, +{ 0x180, 260, 0, 782 }, +{ 0x40, 260, 0, 795 }, +{ 0x140, 260, 0, 793 }, +{ 0xc0, 260, 0, 794 }, +{ 0x20, 260, 0, 807 }, +{ 0xa0, 260, 0, 805 }, +{ 0x60, 260, 0, 806 }, +{ 0x10, 260, 0, 819 }, +{ 0x50, 260, 0, 817 }, +{ 0x30, 260, 0, 818 }, +{ 0x8, 260, 0, 831 }, +{ 0x28, 260, 0, 829 }, +{ 0x18, 260, 0, 830 }, +{ 0x4, 260, 0, 841 }, +{ 0x2, 260, 0, 842 }, +{ 0x1, 260, 0, 843 }, +{ 0x500, 257, 0, 759 }, +{ 0x1500, 257, 0, 757 }, +{ 0xd00, 257, 0, 758 }, +{ 0x300, 257, 0, 771 }, +{ 0x1300, 257, 0, 747 }, +{ 0x5300, 257, 0, 745 }, +{ 0x3300, 257, 0, 746 }, +{ 0xb00, 257, 0, 769 }, +{ 0x700, 257, 0, 770 }, +{ 0x80, 257, 0, 786 }, +{ 0x280, 257, 0, 784 }, +{ 0x180, 257, 0, 785 }, +{ 0x40, 257, 0, 798 }, +{ 0x140, 257, 0, 796 }, +{ 0xc0, 257, 0, 797 }, +{ 0x20, 257, 0, 810 }, +{ 0xa0, 257, 0, 808 }, +{ 0x60, 257, 0, 809 }, +{ 0x10, 257, 0, 822 }, +{ 0x50, 257, 0, 820 }, +{ 0x30, 257, 0, 821 }, +{ 0x8, 257, 0, 834 }, +{ 0x28, 257, 0, 832 }, +{ 0x18, 257, 0, 833 }, +{ 0x4, 257, 0, 844 }, +{ 0x2, 257, 0, 845 }, +{ 0x1, 257, 0, 846 }, +{ 0x500, 252, 0, 762 }, +{ 0x1500, 252, 0, 760 }, +{ 0xd00, 252, 0, 761 }, +{ 0x300, 252, 0, 774 }, +{ 0x1300, 252, 0, 750 }, +{ 0x5300, 252, 0, 748 }, +{ 0x3300, 252, 0, 749 }, +{ 0xb00, 252, 0, 772 }, +{ 0x700, 252, 0, 773 }, +{ 0x80, 252, 0, 789 }, +{ 0x280, 252, 0, 787 }, +{ 0x180, 252, 0, 788 }, +{ 0x40, 252, 0, 801 }, +{ 0x140, 252, 0, 799 }, +{ 0xc0, 252, 0, 800 }, +{ 0x20, 252, 0, 813 }, +{ 0xa0, 252, 0, 811 }, +{ 0x60, 252, 0, 812 }, +{ 0x10, 252, 0, 825 }, +{ 0x50, 252, 0, 823 }, +{ 0x30, 252, 0, 824 }, +{ 0x8, 252, 0, 837 }, +{ 0x28, 252, 0, 835 }, +{ 0x18, 252, 0, 836 }, +{ 0x4, 252, 0, 847 }, +{ 0x2, 252, 0, 848 }, +{ 0x1, 252, 0, 849 }, +{ 0x8, 254, 1, 895 }, +{ 0x8, 255, 0, 894 }, +{ 0x28, 254, 1, 891 }, +{ 0x28, 255, 0, 890 }, +{ 0x18, 254, 1, 893 }, +{ 0x18, 255, 0, 892 }, +{ 0x4, 254, 1, 957 }, +{ 0x4, 255, 0, 956 }, +{ 0x2, 254, 1, 959 }, +{ 0x2, 255, 0, 958 }, +{ 0x1, 254, 1, 961 }, +{ 0x1, 255, 0, 960 }, +{ 0xa00, 262, 0, 865 }, +{ 0x2a00, 262, 0, 863 }, +{ 0x1a00, 262, 0, 864 }, +{ 0x600, 262, 0, 877 }, +{ 0x2600, 262, 0, 853 }, +{ 0xa600, 262, 0, 851 }, +{ 0x6600, 262, 0, 852 }, +{ 0x1600, 262, 0, 875 }, +{ 0xe00, 262, 0, 876 }, +{ 0x100, 262, 0, 889 }, +{ 0x500, 262, 0, 887 }, +{ 0x300, 262, 0, 888 }, +{ 0x80, 262, 0, 898 }, +{ 0x280, 262, 0, 896 }, +{ 0x180, 262, 0, 897 }, +{ 0x40, 262, 0, 910 }, +{ 0x140, 262, 0, 908 }, +{ 0xc0, 262, 0, 909 }, +{ 0x20, 262, 0, 922 }, +{ 0xa0, 262, 0, 920 }, +{ 0x60, 262, 0, 921 }, +{ 0x10, 262, 0, 934 }, +{ 0x50, 262, 0, 932 }, +{ 0x30, 262, 0, 933 }, +{ 0x8, 262, 0, 946 }, +{ 0x28, 262, 0, 944 }, +{ 0x18, 262, 0, 945 }, +{ 0x4, 262, 0, 962 }, +{ 0x2, 262, 0, 963 }, +{ 0x1, 262, 1, 964 }, +{ 0x1, 263, 0, 850 }, +{ 0x500, 259, 0, 868 }, +{ 0x1500, 259, 0, 866 }, +{ 0xd00, 259, 0, 867 }, +{ 0x300, 259, 0, 880 }, +{ 0x1300, 259, 0, 856 }, +{ 0x5300, 259, 0, 854 }, +{ 0x3300, 259, 0, 855 }, +{ 0xb00, 259, 0, 878 }, +{ 0x700, 259, 0, 879 }, +{ 0x80, 259, 0, 901 }, +{ 0x280, 259, 0, 899 }, +{ 0x180, 259, 0, 900 }, +{ 0x40, 259, 0, 913 }, +{ 0x140, 259, 0, 911 }, +{ 0xc0, 259, 0, 912 }, +{ 0x20, 259, 0, 925 }, +{ 0xa0, 259, 0, 923 }, +{ 0x60, 259, 0, 924 }, +{ 0x10, 259, 0, 937 }, +{ 0x50, 259, 0, 935 }, +{ 0x30, 259, 0, 936 }, +{ 0x8, 259, 0, 949 }, +{ 0x28, 259, 0, 947 }, +{ 0x18, 259, 0, 948 }, +{ 0x4, 259, 0, 965 }, +{ 0x2, 259, 0, 966 }, +{ 0x1, 259, 0, 967 }, +{ 0x500, 256, 0, 871 }, +{ 0x1500, 256, 0, 869 }, +{ 0xd00, 256, 0, 870 }, +{ 0x300, 256, 0, 883 }, +{ 0x1300, 256, 0, 859 }, +{ 0x5300, 256, 0, 857 }, +{ 0x3300, 256, 0, 858 }, +{ 0xb00, 256, 0, 881 }, +{ 0x700, 256, 0, 882 }, +{ 0x80, 256, 0, 904 }, +{ 0x280, 256, 0, 902 }, +{ 0x180, 256, 0, 903 }, +{ 0x40, 256, 0, 916 }, +{ 0x140, 256, 0, 914 }, +{ 0xc0, 256, 0, 915 }, +{ 0x20, 256, 0, 928 }, +{ 0xa0, 256, 0, 926 }, +{ 0x60, 256, 0, 927 }, +{ 0x10, 256, 0, 940 }, +{ 0x50, 256, 0, 938 }, +{ 0x30, 256, 0, 939 }, +{ 0x8, 256, 0, 952 }, +{ 0x28, 256, 0, 950 }, +{ 0x18, 256, 0, 951 }, +{ 0x4, 256, 0, 968 }, +{ 0x2, 256, 0, 969 }, +{ 0x1, 256, 0, 970 }, +{ 0x500, 251, 0, 874 }, +{ 0x1500, 251, 0, 872 }, +{ 0xd00, 251, 0, 873 }, +{ 0x300, 251, 0, 886 }, +{ 0x1300, 251, 0, 862 }, +{ 0x5300, 251, 0, 860 }, +{ 0x3300, 251, 0, 861 }, +{ 0xb00, 251, 0, 884 }, +{ 0x700, 251, 0, 885 }, +{ 0x80, 251, 0, 907 }, +{ 0x280, 251, 0, 905 }, +{ 0x180, 251, 0, 906 }, +{ 0x40, 251, 0, 919 }, +{ 0x140, 251, 0, 917 }, +{ 0xc0, 251, 0, 918 }, +{ 0x20, 251, 0, 931 }, +{ 0xa0, 251, 0, 929 }, +{ 0x60, 251, 0, 930 }, +{ 0x10, 251, 0, 943 }, +{ 0x50, 251, 0, 941 }, +{ 0x30, 251, 0, 942 }, +{ 0x8, 251, 0, 955 }, +{ 0x28, 251, 0, 953 }, +{ 0x18, 251, 0, 954 }, +{ 0x4, 251, 0, 971 }, +{ 0x2, 251, 0, 972 }, +{ 0x1, 251, 0, 973 }, +{ 0x2, 150, 0, 975 }, +{ 0x1, 150, 0, 976 }, +{ 0x1, 50, 0, 977 }, +{ 0x3, 49, 0, 978 }, +{ 0x1, 428, 0, 979 }, +{ 0x1, 442, 0, 980 }, +{ 0x2, 386, 0, 983 }, +{ 0x1, 386, 0, 984 }, +{ 0x2, 384, 0, 985 }, +{ 0x1, 384, 0, 986 }, +{ 0x1, 383, 0, 987 }, +{ 0x1, 328, 0, 992 }, +{ 0x1, 327, 0, 993 }, +{ 0x1, 326, 0, 994 }, +{ 0x1, 325, 0, 995 }, +{ 0x1, 250, 0, 996 }, +{ 0x1, 249, 0, 997 }, +{ 0x1, 324, 0, 998 }, +{ 0x1, 323, 0, 999 }, +{ 0x1, 322, 0, 1000 }, +{ 0x1, 321, 0, 1001 }, +{ 0x1, 320, 0, 1002 }, +{ 0x1, 319, 0, 1003 }, +{ 0x1, 318, 0, 1004 }, +{ 0x2, 248, 0, 1005 }, +{ 0x1, 248, 0, 1006 }, +{ 0x2, 366, 0, 1012 }, +{ 0x1, 366, 0, 1013 }, +{ 0x1, 317, 0, 1014 }, +{ 0x1, 316, 0, 1015 }, +{ 0x1, 315, 0, 1016 }, +{ 0x1, 314, 0, 1017 }, +{ 0x1, 8, 1, 1019 }, +{ 0x1, 9, 0, 1018 }, +{ 0x1, 313, 0, 1020 }, +{ 0x1, 312, 0, 1021 }, +{ 0x1, 311, 0, 1022 }, +{ 0x1, 310, 0, 1023 }, +{ 0x1, 388, 0, 1024 }, +{ 0x1, 399, 0, 1025 }, +{ 0x1, 389, 0, 1026 }, +{ 0x1, 423, 0, 1027 }, +{ 0x1, 309, 0, 1031 }, +{ 0x1, 247, 0, 1032 }, +{ 0x1, 177, 0, 1035 }, +{ 0x2, 291, 0, 1039 }, +{ 0x1, 291, 0, 1040 }, +{ 0x1, 236, 0, 1041 }, +{ 0x5, 48, 0, 1043 }, +{ 0x3, 48, 0, 1044 }, +{ 0x5, 47, 0, 1045 }, +{ 0x3, 47, 0, 1046 }, +{ 0x1, 365, 0, 1047 }, +{ 0x1, 373, 0, 1048 }, +{ 0x1, 371, 0, 1049 }, +{ 0x1, 392, 0, 1050 }, +{ 0x1, 372, 0, 1051 }, +{ 0x1, 370, 0, 1052 }, +{ 0x2, 378, 0, 1053 }, +{ 0x1, 378, 0, 1055 }, +{ 0x2, 376, 0, 1054 }, +{ 0x1, 376, 0, 1056 }, +{ 0x2, 396, 0, 1057 }, +{ 0x1, 396, 0, 1060 }, +{ 0x2, 377, 0, 1058 }, +{ 0x1, 377, 0, 1061 }, +{ 0x2, 375, 0, 1059 }, +{ 0x1, 375, 0, 1062 }, +{ 0x1, 338, 0, 1063 }, +{ 0x1, 337, 0, 1064 }, +{ 0x1, 369, 0, 1065 }, +{ 0x1, 360, 0, 1066 }, +{ 0x1, 362, 0, 1067 }, +{ 0x1, 359, 0, 1068 }, +{ 0x1, 361, 0, 1069 }, +{ 0x2, 446, 0, 1070 }, +{ 0x1, 446, 0, 1073 }, +{ 0x2, 445, 0, 1071 }, +{ 0x1, 445, 0, 1074 }, +{ 0x2, 444, 0, 1072 }, +{ 0x1, 444, 0, 1075 }, +{ 0x1, 348, 0, 1076 }, +{ 0x2, 347, 0, 1077 }, +{ 0x1, 347, 0, 1078 }, +{ 0x2, 294, 0, 1079 }, +{ 0x1, 294, 0, 1082 }, +{ 0x2, 293, 0, 1080 }, +{ 0x1, 293, 0, 1083 }, +{ 0x2, 292, 0, 1081 }, +{ 0x1, 292, 0, 1084 }, +{ 0x2, 363, 0, 1085 }, +{ 0x1, 363, 0, 1086 }, +{ 0x2, 364, 0, 1087 }, +{ 0x1, 364, 0, 1088 }, +{ 0xa, 438, 1, 1100 }, +{ 0xa, 439, 1, 1099 }, +{ 0xa, 440, 1, 1098 }, +{ 0xa, 441, 0, 1097 }, +{ 0x1a, 438, 1, 1092 }, +{ 0x1a, 439, 1, 1091 }, +{ 0x32, 440, 1, 1090 }, +{ 0x32, 441, 0, 1089 }, +{ 0x6, 438, 1, 1108 }, +{ 0x6, 439, 1, 1107 }, +{ 0x6, 440, 1, 1106 }, +{ 0x6, 441, 0, 1105 }, +{ 0x1, 438, 1, 1120 }, +{ 0x1, 439, 1, 1119 }, +{ 0x1, 440, 1, 1118 }, +{ 0x1, 441, 0, 1117 }, +{ 0x9, 438, 1, 1104 }, +{ 0x9, 439, 1, 1103 }, +{ 0x9, 440, 1, 1102 }, +{ 0x9, 441, 0, 1101 }, +{ 0x19, 438, 1, 1096 }, +{ 0x19, 439, 1, 1095 }, +{ 0x31, 440, 1, 1094 }, +{ 0x31, 441, 0, 1093 }, +{ 0x5, 438, 1, 1112 }, +{ 0x5, 439, 1, 1111 }, +{ 0x5, 440, 1, 1110 }, +{ 0x5, 441, 0, 1109 }, +{ 0x3, 438, 1, 1116 }, +{ 0x3, 439, 1, 1115 }, +{ 0x3, 440, 1, 1114 }, +{ 0x3, 441, 0, 1113 }, +{ 0xa, 429, 1, 1132 }, +{ 0xa, 430, 1, 1131 }, +{ 0xa, 431, 1, 1130 }, +{ 0xa, 432, 0, 1129 }, +{ 0x1a, 429, 1, 1124 }, +{ 0x1a, 430, 1, 1123 }, +{ 0x32, 431, 1, 1122 }, +{ 0x32, 432, 0, 1121 }, +{ 0x6, 429, 1, 1140 }, +{ 0x6, 430, 1, 1139 }, +{ 0x6, 431, 1, 1138 }, +{ 0x6, 432, 0, 1137 }, +{ 0x1, 429, 1, 1152 }, +{ 0x1, 430, 1, 1151 }, +{ 0x1, 431, 1, 1150 }, +{ 0x1, 432, 0, 1149 }, +{ 0x9, 429, 1, 1136 }, +{ 0x9, 430, 1, 1135 }, +{ 0x9, 431, 1, 1134 }, +{ 0x9, 432, 0, 1133 }, +{ 0x19, 429, 1, 1128 }, +{ 0x19, 430, 1, 1127 }, +{ 0x31, 431, 1, 1126 }, +{ 0x31, 432, 0, 1125 }, +{ 0x5, 429, 1, 1144 }, +{ 0x5, 430, 1, 1143 }, +{ 0x5, 431, 1, 1142 }, +{ 0x5, 432, 0, 1141 }, +{ 0x3, 429, 1, 1148 }, +{ 0x3, 430, 1, 1147 }, +{ 0x3, 431, 1, 1146 }, +{ 0x3, 432, 0, 1145 }, +{ 0xa, 433, 1, 1164 }, +{ 0xa, 434, 1, 1163 }, +{ 0xa, 435, 1, 1162 }, +{ 0xa, 436, 0, 1161 }, +{ 0x1a, 433, 1, 1156 }, +{ 0x1a, 434, 1, 1155 }, +{ 0x32, 435, 1, 1154 }, +{ 0x32, 436, 0, 1153 }, +{ 0x6, 433, 1, 1172 }, +{ 0x6, 434, 1, 1171 }, +{ 0x6, 435, 1, 1170 }, +{ 0x6, 436, 0, 1169 }, +{ 0x1, 433, 1, 1184 }, +{ 0x1, 434, 1, 1183 }, +{ 0x1, 435, 1, 1182 }, +{ 0x1, 436, 0, 1181 }, +{ 0x9, 433, 1, 1168 }, +{ 0x9, 434, 1, 1167 }, +{ 0x9, 435, 1, 1166 }, +{ 0x9, 436, 0, 1165 }, +{ 0x19, 433, 1, 1160 }, +{ 0x19, 434, 1, 1159 }, +{ 0x31, 435, 1, 1158 }, +{ 0x31, 436, 0, 1157 }, +{ 0x5, 433, 1, 1176 }, +{ 0x5, 434, 1, 1175 }, +{ 0x5, 435, 1, 1174 }, +{ 0x5, 436, 0, 1173 }, +{ 0x3, 433, 1, 1180 }, +{ 0x3, 434, 1, 1179 }, +{ 0x3, 435, 1, 1178 }, +{ 0x3, 436, 0, 1177 }, +{ 0x1, 139, 0, 1185 }, +{ 0x1, 138, 0, 1186 }, +{ 0x1, 391, 1, 1188 }, +{ 0x1, 137, 0, 1187 }, +{ 0x2, 395, 1, 1190 }, +{ 0x2, 141, 0, 1189 }, +{ 0x1, 395, 1, 1192 }, +{ 0x1, 141, 0, 1191 }, +{ 0x1, 397, 0, 1193 }, +{ 0x1, 136, 0, 1194 }, +{ 0x2, 135, 0, 1195 }, +{ 0x2, 134, 0, 1196 }, +{ 0x1, 459, 1, 1202 }, +{ 0x1, 246, 0, 1033 }, +{ 0x1, 458, 0, 1203 }, +{ 0x1, 457, 1, 1204 }, +{ 0x1, 245, 0, 1042 }, +{ 0x1, 308, 0, 1205 }, +{ 0x1, 307, 1, 1206 }, +{ 0x1, 290, 0, 1034 }, +{ 0x1, 306, 0, 1207 }, +{ 0x1, 305, 1, 1208 }, +{ 0x1, 427, 0, 1036 }, +{ 0x1, 304, 1, 1209 }, +{ 0x1, 398, 0, 1038 }, +{ 0x1, 303, 0, 1210 }, +{ 0x1, 302, 0, 1211 }, +{ 0x1, 301, 0, 1212 }, +{ 0x1, 300, 1, 1213 }, +{ 0x2, 398, 0, 1037 }, +{ 0x10, 299, 0, 1217 }, +{ 0x90, 299, 0, 1215 }, +{ 0x190, 299, 0, 1214 }, +{ 0x50, 299, 0, 1216 }, +{ 0x30, 299, 0, 1219 }, +{ 0x70, 299, 0, 1218 }, +{ 0x8, 299, 0, 1221 }, +{ 0x18, 299, 0, 1220 }, +{ 0x4, 299, 0, 1222 }, +{ 0x1, 299, 0, 1225 }, +{ 0x3, 299, 0, 1224 }, +{ 0x1, 298, 1, 1226 }, +{ 0x2, 299, 0, 1223 }, +{ 0x3, 46, 0, 1227 }, +{ 0x1, 241, 1, 1228 }, +{ 0x1, 242, 1, 1028 }, +{ 0x1, 243, 0, 88 }, +{ 0x1, 341, 1, 1229 }, +{ 0x1, 342, 1, 1029 }, +{ 0x1, 343, 0, 89 }, +{ 0x1, 34, 1, 1230 }, +{ 0x1, 35, 1, 1030 }, +{ 0x1, 36, 0, 90 }, +{ 0x1, 230, 0, 1231 }, +{ 0x4, 452, 0, 1232 }, +{ 0x2, 452, 0, 1233 }, +{ 0x1, 452, 1, 1235 }, +{ 0x1, 453, 0, 1234 }, +{ 0x8, 454, 0, 1236 }, +{ 0x4, 454, 0, 1237 }, +{ 0x1, 454, 1, 1239 }, +{ 0x2, 454, 0, 1238 }, +{ 0x8, 219, 0, 1240 }, +{ 0x4, 219, 0, 1241 }, +{ 0x2, 219, 0, 1242 }, +{ 0x1, 219, 1, 1244 }, +{ 0x1, 220, 0, 1243 }, +{ 0x10, 221, 0, 1245 }, +{ 0x8, 221, 0, 1246 }, +{ 0x4, 221, 0, 1247 }, +{ 0x1, 221, 1, 1249 }, +{ 0x2, 221, 0, 1248 }, +{ 0x220, 191, 0, 1250 }, +{ 0x120, 191, 0, 1251 }, +{ 0xa0, 191, 0, 1252 }, +{ 0x60, 191, 1, 1254 }, +{ 0x4, 192, 0, 1253 }, +{ 0x110, 191, 0, 1260 }, +{ 0x90, 191, 0, 1261 }, +{ 0x50, 191, 0, 1262 }, +{ 0x30, 191, 1, 1264 }, +{ 0x2, 192, 0, 1263 }, +{ 0x8, 191, 0, 1265 }, +{ 0x4, 191, 0, 1266 }, +{ 0x2, 191, 0, 1267 }, +{ 0x1, 191, 1, 1269 }, +{ 0x1, 192, 0, 1268 }, +{ 0x440, 193, 0, 1255 }, +{ 0x240, 193, 0, 1256 }, +{ 0x140, 193, 0, 1257 }, +{ 0xc0, 193, 1, 1259 }, +{ 0x40, 193, 0, 1258 }, +{ 0x220, 193, 0, 1270 }, +{ 0x120, 193, 0, 1271 }, +{ 0xa0, 193, 0, 1272 }, +{ 0x60, 193, 1, 1274 }, +{ 0x20, 193, 0, 1273 }, +{ 0x10, 193, 0, 1275 }, +{ 0x8, 193, 0, 1276 }, +{ 0x4, 193, 0, 1277 }, +{ 0x1, 193, 1, 1279 }, +{ 0x2, 193, 0, 1278 }, +{ 0x8, 215, 0, 1280 }, +{ 0x4, 215, 0, 1281 }, +{ 0x2, 215, 0, 1282 }, +{ 0x1, 215, 1, 1284 }, +{ 0x1, 216, 0, 1283 }, +{ 0x220, 187, 0, 1285 }, +{ 0x120, 187, 0, 1286 }, +{ 0xa0, 187, 0, 1287 }, +{ 0x60, 187, 1, 1289 }, +{ 0x4, 188, 0, 1288 }, +{ 0x110, 187, 0, 1295 }, +{ 0x90, 187, 0, 1296 }, +{ 0x50, 187, 0, 1297 }, +{ 0x30, 187, 1, 1299 }, +{ 0x2, 188, 0, 1298 }, +{ 0x8, 187, 0, 1300 }, +{ 0x4, 187, 0, 1301 }, +{ 0x2, 187, 0, 1302 }, +{ 0x1, 187, 1, 1304 }, +{ 0x1, 188, 0, 1303 }, +{ 0x440, 233, 0, 1290 }, +{ 0x240, 233, 0, 1291 }, +{ 0x140, 233, 0, 1292 }, +{ 0xc0, 233, 1, 1294 }, +{ 0x40, 233, 0, 1293 }, +{ 0x220, 233, 0, 1305 }, +{ 0x120, 233, 0, 1306 }, +{ 0xa0, 233, 0, 1307 }, +{ 0x60, 233, 1, 1309 }, +{ 0x20, 233, 0, 1308 }, +{ 0x10, 233, 0, 1310 }, +{ 0x8, 233, 0, 1311 }, +{ 0x4, 233, 0, 1312 }, +{ 0x1, 233, 1, 1314 }, +{ 0x2, 233, 0, 1313 }, +{ 0x8, 207, 0, 1315 }, +{ 0x4, 207, 0, 1316 }, +{ 0x2, 207, 0, 1317 }, +{ 0x1, 207, 1, 1319 }, +{ 0x1, 208, 0, 1318 }, +{ 0x10, 214, 0, 1320 }, +{ 0x8, 214, 0, 1321 }, +{ 0x4, 214, 0, 1322 }, +{ 0x1, 214, 1, 1324 }, +{ 0x2, 214, 0, 1323 }, +{ 0x220, 178, 0, 1325 }, +{ 0x120, 178, 0, 1326 }, +{ 0xa0, 178, 0, 1327 }, +{ 0x60, 178, 1, 1329 }, +{ 0x4, 179, 0, 1328 }, +{ 0x110, 178, 0, 1350 }, +{ 0x90, 178, 0, 1351 }, +{ 0x50, 178, 0, 1352 }, +{ 0x30, 178, 1, 1354 }, +{ 0x2, 179, 0, 1353 }, +{ 0x8, 178, 0, 1355 }, +{ 0x4, 178, 0, 1356 }, +{ 0x2, 178, 0, 1357 }, +{ 0x1, 178, 1, 1359 }, +{ 0x1, 179, 0, 1358 }, +{ 0x440, 186, 0, 1330 }, +{ 0x240, 186, 0, 1331 }, +{ 0x140, 186, 0, 1332 }, +{ 0xc0, 186, 1, 1334 }, +{ 0x40, 186, 0, 1333 }, +{ 0x220, 186, 0, 1360 }, +{ 0x120, 186, 0, 1361 }, +{ 0xa0, 186, 0, 1362 }, +{ 0x60, 186, 1, 1364 }, +{ 0x20, 186, 0, 1363 }, +{ 0x10, 186, 0, 1365 }, +{ 0x8, 186, 0, 1366 }, +{ 0x4, 186, 0, 1367 }, +{ 0x1, 186, 1, 1369 }, +{ 0x2, 186, 0, 1368 }, +{ 0x440, 143, 0, 1335 }, +{ 0x240, 143, 0, 1336 }, +{ 0x140, 143, 0, 1337 }, +{ 0xc0, 143, 1, 1339 }, +{ 0x40, 143, 0, 1338 }, +{ 0x220, 143, 0, 1370 }, +{ 0x120, 143, 0, 1371 }, +{ 0xa0, 143, 0, 1372 }, +{ 0x60, 143, 1, 1374 }, +{ 0x20, 143, 0, 1373 }, +{ 0x10, 143, 0, 1375 }, +{ 0x8, 143, 0, 1376 }, +{ 0x1, 143, 1, 1379 }, +{ 0x2, 143, 0, 1378 }, +{ 0x440, 194, 1, 1345 }, +{ 0x441, 174, 0, 1340 }, +{ 0x240, 194, 1, 1346 }, +{ 0x241, 174, 0, 1341 }, +{ 0x140, 194, 1, 1347 }, +{ 0x141, 174, 0, 1342 }, +{ 0xc0, 194, 1, 1349 }, +{ 0x40, 194, 1, 1348 }, +{ 0xc1, 174, 1, 1344 }, +{ 0x41, 174, 0, 1343 }, +{ 0x220, 194, 1, 1390 }, +{ 0x221, 174, 0, 1380 }, +{ 0x120, 194, 1, 1391 }, +{ 0x121, 174, 0, 1381 }, +{ 0xa0, 194, 1, 1392 }, +{ 0xa1, 174, 0, 1382 }, +{ 0x60, 194, 1, 1394 }, +{ 0x20, 194, 1, 1393 }, +{ 0x61, 174, 1, 1384 }, +{ 0x21, 174, 0, 1383 }, +{ 0x10, 194, 1, 1395 }, +{ 0x11, 174, 0, 1385 }, +{ 0x8, 194, 1, 1396 }, +{ 0x9, 174, 0, 1386 }, +{ 0x4, 194, 1, 1397 }, +{ 0x5, 174, 0, 1387 }, +{ 0x1, 194, 1, 1399 }, +{ 0x2, 194, 1, 1398 }, +{ 0x3, 174, 1, 1389 }, +{ 0x1, 174, 0, 1388 }, +{ 0x1, 153, 1, 1407 }, +{ 0x1, 154, 1, 1406 }, +{ 0x1, 155, 1, 1405 }, +{ 0x1, 156, 0, 1404 }, +{ 0x3, 153, 1, 1403 }, +{ 0x3, 154, 1, 1402 }, +{ 0x3, 155, 1, 1401 }, +{ 0x3, 156, 0, 1400 }, +{ 0x1108, 159, 1, 1569 }, +{ 0x1108, 160, 1, 1568 }, +{ 0x1108, 165, 1, 1409 }, +{ 0x1108, 166, 0, 1408 }, +{ 0x908, 159, 1, 1571 }, +{ 0x908, 160, 1, 1570 }, +{ 0x908, 165, 1, 1411 }, +{ 0x908, 166, 0, 1410 }, +{ 0x508, 159, 1, 1573 }, +{ 0x508, 160, 1, 1572 }, +{ 0x508, 165, 1, 1413 }, +{ 0x508, 166, 0, 1412 }, +{ 0x308, 159, 1, 1577 }, +{ 0x308, 160, 1, 1576 }, +{ 0x108, 160, 1, 1574 }, +{ 0x18, 161, 1, 1575 }, +{ 0x308, 165, 1, 1417 }, +{ 0x308, 166, 1, 1416 }, +{ 0x108, 166, 1, 1414 }, +{ 0x18, 167, 0, 1415 }, +{ 0x88, 159, 1, 1609 }, +{ 0x88, 160, 1, 1608 }, +{ 0x88, 165, 1, 1489 }, +{ 0x88, 166, 0, 1488 }, +{ 0x48, 159, 1, 1611 }, +{ 0x48, 160, 1, 1610 }, +{ 0x48, 165, 1, 1491 }, +{ 0x48, 166, 0, 1490 }, +{ 0x28, 159, 1, 1613 }, +{ 0x28, 160, 1, 1612 }, +{ 0x28, 165, 1, 1493 }, +{ 0x28, 166, 0, 1492 }, +{ 0x18, 159, 1, 1617 }, +{ 0x18, 160, 1, 1616 }, +{ 0x8, 160, 1, 1614 }, +{ 0x8, 161, 1, 1615 }, +{ 0x18, 165, 1, 1497 }, +{ 0x18, 166, 1, 1496 }, +{ 0x8, 166, 1, 1494 }, +{ 0x8, 167, 0, 1495 }, +{ 0x884, 159, 1, 1579 }, +{ 0x884, 160, 1, 1578 }, +{ 0x442, 162, 1, 1469 }, +{ 0x442, 163, 1, 1468 }, +{ 0x884, 165, 1, 1439 }, +{ 0x884, 166, 1, 1438 }, +{ 0x442, 168, 1, 1419 }, +{ 0x442, 169, 0, 1418 }, +{ 0x484, 159, 1, 1581 }, +{ 0x484, 160, 1, 1580 }, +{ 0x242, 162, 1, 1471 }, +{ 0x242, 163, 1, 1470 }, +{ 0x484, 165, 1, 1441 }, +{ 0x484, 166, 1, 1440 }, +{ 0x242, 168, 1, 1421 }, +{ 0x242, 169, 0, 1420 }, +{ 0x284, 159, 1, 1583 }, +{ 0x284, 160, 1, 1582 }, +{ 0x142, 162, 1, 1473 }, +{ 0x142, 163, 1, 1472 }, +{ 0x284, 165, 1, 1443 }, +{ 0x284, 166, 1, 1442 }, +{ 0x142, 168, 1, 1423 }, +{ 0x142, 169, 0, 1422 }, +{ 0x184, 159, 1, 1587 }, +{ 0x184, 160, 1, 1586 }, +{ 0x84, 160, 1, 1584 }, +{ 0xc, 161, 1, 1585 }, +{ 0xc2, 162, 1, 1477 }, +{ 0xc2, 163, 1, 1476 }, +{ 0x42, 163, 1, 1474 }, +{ 0x6, 164, 1, 1475 }, +{ 0x184, 165, 1, 1447 }, +{ 0x184, 166, 1, 1446 }, +{ 0x84, 166, 1, 1444 }, +{ 0xc, 167, 1, 1445 }, +{ 0xc2, 168, 1, 1427 }, +{ 0xc2, 169, 1, 1426 }, +{ 0x42, 169, 1, 1424 }, +{ 0x6, 170, 0, 1425 }, +{ 0x44, 159, 1, 1619 }, +{ 0x44, 160, 1, 1618 }, +{ 0x22, 162, 1, 1549 }, +{ 0x22, 163, 1, 1548 }, +{ 0x44, 165, 1, 1519 }, +{ 0x44, 166, 1, 1518 }, +{ 0x22, 168, 1, 1499 }, +{ 0x22, 169, 0, 1498 }, +{ 0x24, 159, 1, 1621 }, +{ 0x24, 160, 1, 1620 }, +{ 0x12, 162, 1, 1551 }, +{ 0x12, 163, 1, 1550 }, +{ 0x24, 165, 1, 1521 }, +{ 0x24, 166, 1, 1520 }, +{ 0x12, 168, 1, 1501 }, +{ 0x12, 169, 0, 1500 }, +{ 0x14, 159, 1, 1623 }, +{ 0x14, 160, 1, 1622 }, +{ 0xa, 162, 1, 1553 }, +{ 0xa, 163, 1, 1552 }, +{ 0x14, 165, 1, 1523 }, +{ 0x14, 166, 1, 1522 }, +{ 0xa, 168, 1, 1503 }, +{ 0xa, 169, 0, 1502 }, +{ 0xc, 159, 1, 1627 }, +{ 0xc, 160, 1, 1626 }, +{ 0x4, 160, 1, 1624 }, +{ 0x4, 161, 1, 1625 }, +{ 0x6, 162, 1, 1557 }, +{ 0x6, 163, 1, 1556 }, +{ 0x2, 163, 1, 1554 }, +{ 0x2, 164, 1, 1555 }, +{ 0xc, 165, 1, 1527 }, +{ 0xc, 166, 1, 1526 }, +{ 0x4, 166, 1, 1524 }, +{ 0x4, 167, 1, 1525 }, +{ 0x6, 168, 1, 1507 }, +{ 0x6, 169, 1, 1506 }, +{ 0x2, 169, 1, 1504 }, +{ 0x2, 170, 0, 1505 }, +{ 0x442, 159, 1, 1589 }, +{ 0x442, 160, 1, 1588 }, +{ 0x221, 162, 1, 1479 }, +{ 0x221, 163, 1, 1478 }, +{ 0x442, 165, 1, 1449 }, +{ 0x442, 166, 1, 1448 }, +{ 0x221, 168, 1, 1429 }, +{ 0x221, 169, 0, 1428 }, +{ 0x242, 159, 1, 1591 }, +{ 0x242, 160, 1, 1590 }, +{ 0x121, 162, 1, 1481 }, +{ 0x121, 163, 1, 1480 }, +{ 0x242, 165, 1, 1451 }, +{ 0x242, 166, 1, 1450 }, +{ 0x121, 168, 1, 1431 }, +{ 0x121, 169, 0, 1430 }, +{ 0x142, 159, 1, 1593 }, +{ 0x142, 160, 1, 1592 }, +{ 0xa1, 162, 1, 1483 }, +{ 0xa1, 163, 1, 1482 }, +{ 0x142, 165, 1, 1453 }, +{ 0x142, 166, 1, 1452 }, +{ 0xa1, 168, 1, 1433 }, +{ 0xa1, 169, 0, 1432 }, +{ 0xc2, 159, 1, 1597 }, +{ 0xc2, 160, 1, 1596 }, +{ 0x42, 160, 1, 1594 }, +{ 0x6, 161, 1, 1595 }, +{ 0x61, 162, 1, 1487 }, +{ 0x61, 163, 1, 1486 }, +{ 0x21, 163, 1, 1484 }, +{ 0x3, 164, 1, 1485 }, +{ 0xc2, 165, 1, 1457 }, +{ 0xc2, 166, 1, 1456 }, +{ 0x42, 166, 1, 1454 }, +{ 0x6, 167, 1, 1455 }, +{ 0x61, 168, 1, 1437 }, +{ 0x61, 169, 1, 1436 }, +{ 0x21, 169, 1, 1434 }, +{ 0x3, 170, 0, 1435 }, +{ 0x22, 159, 1, 1629 }, +{ 0x22, 160, 1, 1628 }, +{ 0x11, 162, 1, 1559 }, +{ 0x11, 163, 1, 1558 }, +{ 0x22, 165, 1, 1529 }, +{ 0x22, 166, 1, 1528 }, +{ 0x11, 168, 1, 1509 }, +{ 0x11, 169, 0, 1508 }, +{ 0x12, 159, 1, 1631 }, +{ 0x12, 160, 1, 1630 }, +{ 0x9, 162, 1, 1561 }, +{ 0x9, 163, 1, 1560 }, +{ 0x12, 165, 1, 1531 }, +{ 0x12, 166, 1, 1530 }, +{ 0x9, 168, 1, 1511 }, +{ 0x9, 169, 0, 1510 }, +{ 0xa, 159, 1, 1633 }, +{ 0xa, 160, 1, 1632 }, +{ 0x5, 162, 1, 1563 }, +{ 0x5, 163, 1, 1562 }, +{ 0xa, 165, 1, 1533 }, +{ 0xa, 166, 1, 1532 }, +{ 0x5, 168, 1, 1513 }, +{ 0x5, 169, 0, 1512 }, +{ 0x6, 159, 1, 1637 }, +{ 0x6, 160, 1, 1636 }, +{ 0x2, 160, 1, 1634 }, +{ 0x2, 161, 1, 1635 }, +{ 0x3, 162, 1, 1567 }, +{ 0x3, 163, 1, 1566 }, +{ 0x1, 163, 1, 1564 }, +{ 0x1, 164, 1, 1565 }, +{ 0x6, 165, 1, 1537 }, +{ 0x6, 166, 1, 1536 }, +{ 0x2, 166, 1, 1534 }, +{ 0x2, 167, 1, 1535 }, +{ 0x3, 168, 1, 1517 }, +{ 0x3, 169, 1, 1516 }, +{ 0x1, 169, 1, 1514 }, +{ 0x1, 170, 0, 1515 }, +{ 0x221, 159, 1, 1599 }, +{ 0x221, 160, 1, 1598 }, +{ 0x221, 165, 1, 1459 }, +{ 0x221, 166, 0, 1458 }, +{ 0x121, 159, 1, 1601 }, +{ 0x121, 160, 1, 1600 }, +{ 0x121, 165, 1, 1461 }, +{ 0x121, 166, 0, 1460 }, +{ 0xa1, 159, 1, 1603 }, +{ 0xa1, 160, 1, 1602 }, +{ 0xa1, 165, 1, 1463 }, +{ 0xa1, 166, 0, 1462 }, +{ 0x61, 159, 1, 1607 }, +{ 0x61, 160, 1, 1606 }, +{ 0x21, 160, 1, 1604 }, +{ 0x3, 161, 1, 1605 }, +{ 0x61, 165, 1, 1467 }, +{ 0x61, 166, 1, 1466 }, +{ 0x21, 166, 1, 1464 }, +{ 0x3, 167, 0, 1465 }, +{ 0x11, 159, 1, 1639 }, +{ 0x11, 160, 1, 1638 }, +{ 0x11, 165, 1, 1539 }, +{ 0x11, 166, 0, 1538 }, +{ 0x9, 159, 1, 1641 }, +{ 0x9, 160, 1, 1640 }, +{ 0x9, 165, 1, 1541 }, +{ 0x9, 166, 0, 1540 }, +{ 0x5, 159, 1, 1643 }, +{ 0x5, 160, 1, 1642 }, +{ 0x5, 165, 1, 1543 }, +{ 0x5, 166, 0, 1542 }, +{ 0x3, 159, 1, 1647 }, +{ 0x3, 160, 1, 1646 }, +{ 0x1, 160, 1, 1644 }, +{ 0x1, 161, 1, 1645 }, +{ 0x3, 165, 1, 1547 }, +{ 0x3, 166, 1, 1546 }, +{ 0x1, 166, 1, 1544 }, +{ 0x1, 167, 0, 1545 }, +{ 0x442, 205, 0, 1648 }, +{ 0x242, 205, 0, 1649 }, +{ 0x142, 205, 0, 1650 }, +{ 0xc2, 205, 1, 1652 }, +{ 0x6, 206, 1, 1651 }, +{ 0x1, 443, 0, 981 }, +{ 0x22, 205, 0, 1658 }, +{ 0x12, 205, 0, 1659 }, +{ 0xa, 205, 0, 1660 }, +{ 0x6, 205, 1, 1662 }, +{ 0x2, 206, 1, 1661 }, +{ 0x2, 367, 0, 1010 }, +{ 0x221, 205, 0, 1653 }, +{ 0x121, 205, 0, 1654 }, +{ 0xa1, 205, 0, 1655 }, +{ 0x61, 205, 1, 1657 }, +{ 0x3, 206, 1, 1656 }, +{ 0x1, 437, 0, 982 }, +{ 0x11, 205, 0, 1663 }, +{ 0x9, 205, 0, 1664 }, +{ 0x5, 205, 0, 1665 }, +{ 0x3, 205, 1, 1667 }, +{ 0x1, 206, 1, 1666 }, +{ 0x1, 367, 0, 1011 }, +{ 0x4, 211, 0, 1668 }, +{ 0x1, 211, 0, 1670 }, +{ 0x1, 218, 0, 1671 }, +{ 0x1, 217, 1, 1672 }, +{ 0x2, 211, 0, 1669 }, +{ 0x1, 196, 0, 1673 }, +{ 0x880, 202, 0, 1674 }, +{ 0x480, 202, 0, 1675 }, +{ 0x280, 202, 0, 1676 }, +{ 0x180, 202, 1, 1678 }, +{ 0x80, 203, 0, 1677 }, +{ 0x440, 202, 1, 1689 }, +{ 0x88, 204, 0, 1679 }, +{ 0x240, 202, 1, 1690 }, +{ 0x48, 204, 0, 1680 }, +{ 0x140, 202, 1, 1691 }, +{ 0x28, 204, 0, 1681 }, +{ 0xc0, 202, 1, 1693 }, +{ 0x40, 203, 1, 1692 }, +{ 0x18, 204, 1, 1683 }, +{ 0x8, 204, 0, 1682 }, +{ 0x220, 202, 1, 1694 }, +{ 0x44, 204, 0, 1684 }, +{ 0x120, 202, 1, 1695 }, +{ 0x24, 204, 0, 1685 }, +{ 0xa0, 202, 1, 1696 }, +{ 0x14, 204, 0, 1686 }, +{ 0x60, 202, 1, 1698 }, +{ 0x20, 203, 1, 1697 }, +{ 0xc, 204, 1, 1688 }, +{ 0x4, 204, 0, 1687 }, +{ 0x110, 202, 0, 1699 }, +{ 0x90, 202, 0, 1700 }, +{ 0x50, 202, 0, 1701 }, +{ 0x30, 202, 1, 1703 }, +{ 0x10, 203, 1, 1702 }, +{ 0x1, 385, 0, 974 }, +{ 0x88, 202, 0, 1704 }, +{ 0x48, 202, 0, 1705 }, +{ 0x28, 202, 0, 1706 }, +{ 0x18, 202, 1, 1708 }, +{ 0x8, 203, 1, 1707 }, +{ 0xc, 368, 0, 1007 }, +{ 0x44, 202, 1, 1719 }, +{ 0x22, 204, 0, 1709 }, +{ 0x24, 202, 1, 1720 }, +{ 0x12, 204, 0, 1710 }, +{ 0x14, 202, 1, 1721 }, +{ 0xa, 204, 0, 1711 }, +{ 0xc, 202, 1, 1723 }, +{ 0x4, 203, 1, 1722 }, +{ 0x6, 204, 1, 1713 }, +{ 0x2, 204, 1, 1712 }, +{ 0x6, 368, 0, 1008 }, +{ 0x22, 202, 1, 1724 }, +{ 0x11, 204, 0, 1714 }, +{ 0x12, 202, 1, 1725 }, +{ 0x9, 204, 0, 1715 }, +{ 0xa, 202, 1, 1726 }, +{ 0x5, 204, 0, 1716 }, +{ 0x6, 202, 1, 1728 }, +{ 0x2, 203, 1, 1727 }, +{ 0x3, 204, 1, 1718 }, +{ 0x1, 204, 1, 1717 }, +{ 0x3, 368, 0, 1009 }, +{ 0x11, 202, 0, 1729 }, +{ 0x9, 202, 0, 1730 }, +{ 0x5, 202, 0, 1731 }, +{ 0x3, 202, 1, 1733 }, +{ 0x1, 203, 0, 1732 }, +{ 0x8, 198, 0, 1734 }, +{ 0x4, 198, 0, 1735 }, +{ 0x2, 198, 0, 1736 }, +{ 0x1, 198, 1, 1738 }, +{ 0x1, 199, 1, 1737 }, +{ 0x1, 332, 0, 988 }, +{ 0x8, 200, 0, 1739 }, +{ 0x4, 200, 0, 1740 }, +{ 0x2, 200, 0, 1741 }, +{ 0x1, 200, 1, 1743 }, +{ 0x1, 201, 1, 1742 }, +{ 0x1, 331, 0, 989 }, +{ 0x8, 209, 0, 1744 }, +{ 0x4, 209, 0, 1745 }, +{ 0x2, 209, 0, 1746 }, +{ 0x1, 209, 1, 1748 }, +{ 0x1, 210, 1, 1747 }, +{ 0x1, 330, 0, 990 }, +{ 0x8, 212, 0, 1749 }, +{ 0x4, 212, 0, 1750 }, +{ 0x2, 212, 0, 1751 }, +{ 0x1, 212, 1, 1753 }, +{ 0x1, 213, 1, 1752 }, +{ 0x1, 329, 0, 991 }, +{ 0x8, 224, 0, 1754 }, +{ 0x4, 224, 0, 1755 }, +{ 0x2, 224, 0, 1756 }, +{ 0x1, 224, 1, 1758 }, +{ 0x1, 225, 0, 1757 }, +{ 0x8, 222, 0, 1759 }, +{ 0x4, 222, 0, 1760 }, +{ 0x2, 222, 0, 1761 }, +{ 0x1, 222, 1, 1763 }, +{ 0x1, 223, 0, 1762 }, +{ 0x1, 240, 0, 1764 }, +{ 0x1, 340, 0, 1765 }, +{ 0x1, 33, 0, 1766 }, +{ 0x8, 151, 0, 1767 }, +{ 0x4, 151, 0, 1768 }, +{ 0x2, 151, 0, 1769 }, +{ 0x1, 151, 1, 1771 }, +{ 0x1, 152, 0, 1770 }, +{ 0x8, 157, 0, 1772 }, +{ 0x4, 157, 0, 1773 }, +{ 0x2, 157, 0, 1774 }, +{ 0x1, 157, 1, 1776 }, +{ 0x1, 158, 0, 1775 }, +{ 0x8, 231, 0, 1777 }, +{ 0x4, 231, 0, 1778 }, +{ 0x2, 231, 0, 1779 }, +{ 0x1, 231, 1, 1781 }, +{ 0x1, 232, 0, 1780 }, +{ 0x1, 173, 0, 1782 }, +{ 0x442, 171, 0, 1783 }, +{ 0x242, 171, 0, 1784 }, +{ 0x142, 171, 0, 1785 }, +{ 0xc2, 171, 1, 1787 }, +{ 0x6, 172, 0, 1786 }, +{ 0x22, 171, 0, 1793 }, +{ 0x12, 171, 0, 1794 }, +{ 0xa, 171, 0, 1795 }, +{ 0x6, 171, 1, 1797 }, +{ 0x2, 172, 1, 1796 }, +{ 0x1, 135, 0, 1197 }, +{ 0x221, 171, 0, 1788 }, +{ 0x121, 171, 0, 1789 }, +{ 0xa1, 171, 0, 1790 }, +{ 0x61, 171, 1, 1792 }, +{ 0x3, 172, 0, 1791 }, +{ 0x11, 171, 0, 1798 }, +{ 0x9, 171, 0, 1799 }, +{ 0x5, 171, 0, 1800 }, +{ 0x3, 171, 1, 1802 }, +{ 0x1, 172, 1, 1801 }, +{ 0x1, 134, 0, 1198 }, +{ 0x1, 237, 0, 1803 }, +{ 0x1, 195, 0, 1804 }, +{ 0x1, 149, 0, 1805 }, +{ 0x1, 148, 0, 1806 }, +{ 0x4, 234, 0, 1807 }, +{ 0x2, 234, 0, 1808 }, +{ 0x1, 234, 0, 1809 }, +{ 0x1, 197, 0, 1810 }, +{ 0x2, 235, 0, 1811 }, +{ 0x1, 235, 0, 1812 }, +{ 0x4, 185, 0, 1813 }, +{ 0x2, 185, 0, 1814 }, +{ 0x1, 185, 0, 1815 }, +{ 0x4, 182, 0, 1816 }, +{ 0x1, 190, 0, 1819 }, +{ 0x1, 189, 1, 1820 }, +{ 0x2, 182, 0, 1817 }, +{ 0x1, 142, 0, 1821 }, +{ 0x1, 297, 1, 1822 }, +{ 0x1, 182, 0, 1818 }, +{ 0x8, 144, 0, 1823 }, +{ 0x4, 144, 0, 1824 }, +{ 0x2, 144, 0, 1825 }, +{ 0x1, 144, 1, 1827 }, +{ 0x1, 145, 0, 1826 }, +{ 0x8, 146, 0, 1828 }, +{ 0x4, 146, 0, 1829 }, +{ 0x2, 146, 0, 1830 }, +{ 0x1, 146, 1, 1832 }, +{ 0x1, 147, 1, 1831 }, +{ 0x1, 426, 0, 1199 }, +{ 0x8, 180, 0, 1833 }, +{ 0x4, 180, 0, 1834 }, +{ 0x2, 180, 0, 1835 }, +{ 0x1, 180, 1, 1837 }, +{ 0x1, 181, 1, 1836 }, +{ 0x1, 425, 0, 1200 }, +{ 0x8, 183, 0, 1838 }, +{ 0x4, 183, 0, 1839 }, +{ 0x2, 183, 0, 1840 }, +{ 0x1, 183, 1, 1842 }, +{ 0x1, 184, 1, 1841 }, +{ 0x1, 424, 0, 1201 }, +{ 0x8, 228, 0, 1843 }, +{ 0x4, 228, 0, 1844 }, +{ 0x2, 228, 0, 1845 }, +{ 0x1, 228, 1, 1847 }, +{ 0x1, 229, 0, 1846 }, +{ 0x8, 226, 0, 1848 }, +{ 0x4, 226, 0, 1849 }, +{ 0x2, 226, 0, 1850 }, +{ 0x1, 226, 1, 1852 }, +{ 0x1, 227, 0, 1851 }, +{ 0x8, 44, 0, 1857 }, +{ 0x18, 44, 0, 1853 }, +{ 0x4, 44, 0, 1858 }, +{ 0xc, 44, 0, 1854 }, +{ 0x2, 44, 0, 1859 }, +{ 0x6, 44, 0, 1855 }, +{ 0x1, 44, 0, 1860 }, +{ 0x3, 44, 0, 1856 }, +{ 0x51, 30, 0, 1862 }, +{ 0xd1, 30, 0, 1861 }, +{ 0x31, 30, 1, 1872 }, +{ 0x11, 31, 0, 1871 }, +{ 0x71, 30, 1, 1870 }, +{ 0x31, 31, 0, 1869 }, +{ 0x29, 30, 0, 1864 }, +{ 0x69, 30, 0, 1863 }, +{ 0x19, 30, 1, 1876 }, +{ 0x9, 31, 0, 1875 }, +{ 0x39, 30, 1, 1874 }, +{ 0x19, 31, 0, 1873 }, +{ 0x15, 30, 0, 1866 }, +{ 0x35, 30, 0, 1865 }, +{ 0xd, 30, 1, 1880 }, +{ 0x5, 31, 0, 1879 }, +{ 0x1d, 30, 1, 1878 }, +{ 0xd, 31, 0, 1877 }, +{ 0xb, 30, 0, 1868 }, +{ 0x1b, 30, 0, 1867 }, +{ 0x7, 30, 1, 1884 }, +{ 0x3, 31, 0, 1883 }, +{ 0xf, 30, 1, 1882 }, +{ 0x7, 31, 0, 1881 }, +{ 0xa2, 28, 0, 1886 }, +{ 0x1a2, 28, 0, 1885 }, +{ 0x62, 28, 1, 1896 }, +{ 0x22, 29, 0, 1895 }, +{ 0xe2, 28, 1, 1894 }, +{ 0x62, 29, 0, 1893 }, +{ 0x52, 28, 0, 1888 }, +{ 0xd2, 28, 0, 1887 }, +{ 0x32, 28, 1, 1900 }, +{ 0x12, 29, 0, 1899 }, +{ 0x72, 28, 1, 1898 }, +{ 0x32, 29, 0, 1897 }, +{ 0x2a, 28, 0, 1890 }, +{ 0x6a, 28, 0, 1889 }, +{ 0x1a, 28, 1, 1904 }, +{ 0xa, 29, 0, 1903 }, +{ 0x3a, 28, 1, 1902 }, +{ 0x1a, 29, 0, 1901 }, +{ 0x16, 28, 0, 1892 }, +{ 0x36, 28, 0, 1891 }, +{ 0xe, 28, 1, 1908 }, +{ 0x6, 29, 0, 1907 }, +{ 0x1e, 28, 1, 1906 }, +{ 0xe, 29, 0, 1905 }, +{ 0x51, 28, 0, 1910 }, +{ 0xd1, 28, 0, 1909 }, +{ 0x31, 28, 1, 1920 }, +{ 0x11, 29, 0, 1919 }, +{ 0x71, 28, 1, 1918 }, +{ 0x31, 29, 0, 1917 }, +{ 0x29, 28, 0, 1912 }, +{ 0x69, 28, 0, 1911 }, +{ 0x19, 28, 1, 1924 }, +{ 0x9, 29, 0, 1923 }, +{ 0x39, 28, 1, 1922 }, +{ 0x19, 29, 0, 1921 }, +{ 0x15, 28, 0, 1914 }, +{ 0x35, 28, 0, 1913 }, +{ 0xd, 28, 1, 1928 }, +{ 0x5, 29, 0, 1927 }, +{ 0x1d, 28, 1, 1926 }, +{ 0xd, 29, 0, 1925 }, +{ 0xb, 28, 0, 1916 }, +{ 0x1b, 28, 0, 1915 }, +{ 0x7, 28, 1, 1932 }, +{ 0x3, 29, 0, 1931 }, +{ 0xf, 28, 1, 1930 }, +{ 0x7, 29, 0, 1929 }, +{ 0x51, 26, 0, 1934 }, +{ 0xd1, 26, 0, 1933 }, +{ 0x31, 26, 1, 1944 }, +{ 0x11, 27, 0, 1943 }, +{ 0x71, 26, 1, 1942 }, +{ 0x31, 27, 0, 1941 }, +{ 0x29, 26, 0, 1936 }, +{ 0x69, 26, 0, 1935 }, +{ 0x19, 26, 1, 1948 }, +{ 0x9, 27, 0, 1947 }, +{ 0x39, 26, 1, 1946 }, +{ 0x19, 27, 0, 1945 }, +{ 0x15, 26, 0, 1938 }, +{ 0x35, 26, 0, 1937 }, +{ 0xd, 26, 1, 1952 }, +{ 0x5, 27, 0, 1951 }, +{ 0x1d, 26, 1, 1950 }, +{ 0xd, 27, 0, 1949 }, +{ 0xb, 26, 0, 1940 }, +{ 0x1b, 26, 0, 1939 }, +{ 0x7, 26, 1, 1956 }, +{ 0x3, 27, 0, 1955 }, +{ 0xf, 26, 1, 1954 }, +{ 0x7, 27, 0, 1953 }, +{ 0xa2, 24, 0, 1958 }, +{ 0x1a2, 24, 0, 1957 }, +{ 0x62, 24, 1, 1968 }, +{ 0x22, 25, 0, 1967 }, +{ 0xe2, 24, 1, 1966 }, +{ 0x62, 25, 0, 1965 }, +{ 0x52, 24, 0, 1960 }, +{ 0xd2, 24, 0, 1959 }, +{ 0x32, 24, 1, 1972 }, +{ 0x12, 25, 0, 1971 }, +{ 0x72, 24, 1, 1970 }, +{ 0x32, 25, 0, 1969 }, +{ 0x2a, 24, 0, 1962 }, +{ 0x6a, 24, 0, 1961 }, +{ 0x1a, 24, 1, 1976 }, +{ 0xa, 25, 0, 1975 }, +{ 0x3a, 24, 1, 1974 }, +{ 0x1a, 25, 0, 1973 }, +{ 0x16, 24, 0, 1964 }, +{ 0x36, 24, 0, 1963 }, +{ 0xe, 24, 1, 1980 }, +{ 0x6, 25, 0, 1979 }, +{ 0x1e, 24, 1, 1978 }, +{ 0xe, 25, 0, 1977 }, +{ 0x51, 24, 0, 1982 }, +{ 0xd1, 24, 0, 1981 }, +{ 0x31, 24, 1, 1992 }, +{ 0x11, 25, 0, 1991 }, +{ 0x71, 24, 1, 1990 }, +{ 0x31, 25, 0, 1989 }, +{ 0x29, 24, 0, 1984 }, +{ 0x69, 24, 0, 1983 }, +{ 0x19, 24, 1, 1996 }, +{ 0x9, 25, 0, 1995 }, +{ 0x39, 24, 1, 1994 }, +{ 0x19, 25, 0, 1993 }, +{ 0x15, 24, 0, 1986 }, +{ 0x35, 24, 0, 1985 }, +{ 0xd, 24, 1, 2000 }, +{ 0x5, 25, 0, 1999 }, +{ 0x1d, 24, 1, 1998 }, +{ 0xd, 25, 0, 1997 }, +{ 0xb, 24, 0, 1988 }, +{ 0x1b, 24, 0, 1987 }, +{ 0x7, 24, 1, 2004 }, +{ 0x3, 25, 0, 2003 }, +{ 0xf, 24, 1, 2002 }, +{ 0x7, 25, 0, 2001 }, +{ 0x51, 22, 1, 2030 }, +{ 0x50, 22, 0, 2006 }, +{ 0xd1, 22, 1, 2029 }, +{ 0xd0, 22, 0, 2005 }, +{ 0x31, 22, 1, 2040 }, +{ 0x30, 22, 1, 2016 }, +{ 0x11, 23, 1, 2039 }, +{ 0x10, 23, 0, 2015 }, +{ 0x71, 22, 1, 2038 }, +{ 0x70, 22, 1, 2014 }, +{ 0x31, 23, 1, 2037 }, +{ 0x30, 23, 0, 2013 }, +{ 0x29, 22, 1, 2032 }, +{ 0x28, 22, 0, 2008 }, +{ 0x69, 22, 1, 2031 }, +{ 0x68, 22, 0, 2007 }, +{ 0x19, 22, 1, 2044 }, +{ 0x18, 22, 1, 2020 }, +{ 0x9, 23, 1, 2043 }, +{ 0x8, 23, 0, 2019 }, +{ 0x39, 22, 1, 2042 }, +{ 0x38, 22, 1, 2018 }, +{ 0x19, 23, 1, 2041 }, +{ 0x18, 23, 0, 2017 }, +{ 0x15, 22, 1, 2034 }, +{ 0x14, 22, 0, 2010 }, +{ 0x35, 22, 1, 2033 }, +{ 0x34, 22, 0, 2009 }, +{ 0xd, 22, 1, 2048 }, +{ 0xc, 22, 1, 2024 }, +{ 0x5, 23, 1, 2047 }, +{ 0x4, 23, 0, 2023 }, +{ 0x1d, 22, 1, 2046 }, +{ 0x1c, 22, 1, 2022 }, +{ 0xd, 23, 1, 2045 }, +{ 0xc, 23, 0, 2021 }, +{ 0xb, 22, 1, 2036 }, +{ 0xa, 22, 0, 2012 }, +{ 0x1b, 22, 1, 2035 }, +{ 0x1a, 22, 0, 2011 }, +{ 0x7, 22, 1, 2052 }, +{ 0x6, 22, 1, 2028 }, +{ 0x3, 23, 1, 2051 }, +{ 0x2, 23, 0, 2027 }, +{ 0xf, 22, 1, 2050 }, +{ 0xe, 22, 1, 2026 }, +{ 0x7, 23, 1, 2049 }, +{ 0x6, 23, 0, 2025 }, +{ 0x8, 21, 0, 2054 }, +{ 0x18, 21, 0, 2053 }, +{ 0x1, 21, 1, 2058 }, +{ 0x2, 21, 0, 2057 }, +{ 0x3, 21, 1, 2056 }, +{ 0x4, 21, 0, 2055 }, +{ 0x1, 239, 0, 2059 }, +{ 0x1, 339, 0, 2060 }, +{ 0x14, 43, 0, 2063 }, +{ 0x34, 43, 0, 2061 }, +{ 0xc, 43, 0, 2064 }, +{ 0x1c, 43, 0, 2062 }, +{ 0x2, 43, 0, 2067 }, +{ 0x6, 43, 0, 2065 }, +{ 0x1, 43, 0, 2068 }, +{ 0x3, 43, 0, 2066 }, +{ 0x51, 19, 0, 2070 }, +{ 0xd1, 19, 0, 2069 }, +{ 0x31, 19, 1, 2080 }, +{ 0x11, 20, 0, 2079 }, +{ 0x71, 19, 1, 2078 }, +{ 0x31, 20, 0, 2077 }, +{ 0x29, 19, 0, 2072 }, +{ 0x69, 19, 0, 2071 }, +{ 0x19, 19, 1, 2084 }, +{ 0x9, 20, 0, 2083 }, +{ 0x39, 19, 1, 2082 }, +{ 0x19, 20, 0, 2081 }, +{ 0x15, 19, 0, 2074 }, +{ 0x35, 19, 0, 2073 }, +{ 0xd, 19, 1, 2088 }, +{ 0x5, 20, 0, 2087 }, +{ 0x1d, 19, 1, 2086 }, +{ 0xd, 20, 0, 2085 }, +{ 0xb, 19, 0, 2076 }, +{ 0x1b, 19, 0, 2075 }, +{ 0x7, 19, 1, 2092 }, +{ 0x3, 20, 0, 2091 }, +{ 0xf, 19, 1, 2090 }, +{ 0x7, 20, 0, 2089 }, +{ 0x1, 32, 0, 2093 }, +{ 0x2, 447, 0, 2094 }, +{ 0x1, 447, 0, 2095 }, +{ 0x1, 140, 0, 2096 }, +{ 0x2, 45, 0, 2097 }, +{ 0x1, 45, 0, 2098 }, +{ 0x1, 387, 0, 2099 }, +{ 0x2, 52, 0, 2100 }, +{ 0x1, 52, 0, 2101 }, +{ 0x1, 133, 0, 2102 }, +{ 0x51, 17, 0, 2104 }, +{ 0xd1, 17, 0, 2103 }, +{ 0x31, 17, 1, 2114 }, +{ 0x11, 18, 0, 2113 }, +{ 0x71, 17, 1, 2112 }, +{ 0x31, 18, 0, 2111 }, +{ 0x29, 17, 0, 2106 }, +{ 0x69, 17, 0, 2105 }, +{ 0x19, 17, 1, 2118 }, +{ 0x9, 18, 0, 2117 }, +{ 0x39, 17, 1, 2116 }, +{ 0x19, 18, 0, 2115 }, +{ 0x15, 17, 0, 2108 }, +{ 0x35, 17, 0, 2107 }, +{ 0xd, 17, 1, 2122 }, +{ 0x5, 18, 0, 2121 }, +{ 0x1d, 17, 1, 2120 }, +{ 0xd, 18, 0, 2119 }, +{ 0xb, 17, 0, 2110 }, +{ 0x1b, 17, 0, 2109 }, +{ 0x7, 17, 1, 2126 }, +{ 0x3, 18, 0, 2125 }, +{ 0xf, 17, 1, 2124 }, +{ 0x7, 18, 0, 2123 }, +{ 0xa20, 15, 0, 2128 }, +{ 0x1a20, 15, 0, 2127 }, +{ 0x620, 15, 1, 2138 }, +{ 0x220, 16, 0, 2137 }, +{ 0xe20, 15, 1, 2136 }, +{ 0x620, 16, 0, 2135 }, +{ 0x520, 15, 0, 2130 }, +{ 0xd20, 15, 0, 2129 }, +{ 0x320, 15, 1, 2142 }, +{ 0x120, 16, 0, 2141 }, +{ 0x720, 15, 1, 2140 }, +{ 0x320, 16, 0, 2139 }, +{ 0x2a0, 15, 0, 2132 }, +{ 0x6a0, 15, 0, 2131 }, +{ 0x1a0, 15, 1, 2146 }, +{ 0xa0, 16, 0, 2145 }, +{ 0x3a0, 15, 1, 2144 }, +{ 0x1a0, 16, 0, 2143 }, +{ 0x160, 15, 0, 2134 }, +{ 0x360, 15, 0, 2133 }, +{ 0xe0, 15, 1, 2150 }, +{ 0x60, 16, 0, 2149 }, +{ 0x1e0, 15, 1, 2148 }, +{ 0xe0, 16, 0, 2147 }, +{ 0x51, 15, 1, 2176 }, +{ 0x50, 15, 0, 2152 }, +{ 0xd1, 15, 1, 2175 }, +{ 0xd0, 15, 0, 2151 }, +{ 0x31, 15, 1, 2186 }, +{ 0x30, 15, 1, 2162 }, +{ 0x11, 16, 1, 2185 }, +{ 0x10, 16, 0, 2161 }, +{ 0x71, 15, 1, 2184 }, +{ 0x70, 15, 1, 2160 }, +{ 0x31, 16, 1, 2183 }, +{ 0x30, 16, 0, 2159 }, +{ 0x29, 15, 1, 2178 }, +{ 0x28, 15, 0, 2154 }, +{ 0x69, 15, 1, 2177 }, +{ 0x68, 15, 0, 2153 }, +{ 0x19, 15, 1, 2190 }, +{ 0x18, 15, 1, 2166 }, +{ 0x9, 16, 1, 2189 }, +{ 0x8, 16, 0, 2165 }, +{ 0x39, 15, 1, 2188 }, +{ 0x38, 15, 1, 2164 }, +{ 0x19, 16, 1, 2187 }, +{ 0x18, 16, 0, 2163 }, +{ 0x15, 15, 1, 2180 }, +{ 0x14, 15, 0, 2156 }, +{ 0x35, 15, 1, 2179 }, +{ 0x34, 15, 0, 2155 }, +{ 0xd, 15, 1, 2194 }, +{ 0xc, 15, 1, 2170 }, +{ 0x5, 16, 1, 2193 }, +{ 0x4, 16, 0, 2169 }, +{ 0x1d, 15, 1, 2192 }, +{ 0x1c, 15, 1, 2168 }, +{ 0xd, 16, 1, 2191 }, +{ 0xc, 16, 0, 2167 }, +{ 0xb, 15, 1, 2182 }, +{ 0xa, 15, 0, 2158 }, +{ 0x1b, 15, 1, 2181 }, +{ 0x1a, 15, 0, 2157 }, +{ 0x7, 15, 1, 2198 }, +{ 0x6, 15, 1, 2174 }, +{ 0x3, 16, 1, 2197 }, +{ 0x2, 16, 0, 2173 }, +{ 0xf, 15, 1, 2196 }, +{ 0xe, 15, 1, 2172 }, +{ 0x7, 16, 1, 2195 }, +{ 0x6, 16, 0, 2171 }, +{ 0x8, 14, 0, 2200 }, +{ 0x18, 14, 0, 2199 }, +{ 0x1, 14, 1, 2204 }, +{ 0x2, 14, 0, 2203 }, +{ 0x3, 14, 1, 2202 }, +{ 0x4, 14, 0, 2201 }, +{ 0x1, 109, 1, 2356 }, +{ 0x1, 110, 1, 2355 }, +{ 0x1, 111, 1, 2354 }, +{ 0x1, 112, 1, 2353 }, +{ 0x1, 113, 1, 2352 }, +{ 0x1, 114, 1, 2351 }, +{ 0x1, 115, 1, 2350 }, +{ 0x1, 116, 1, 2349 }, +{ 0x39, 41, 1, 22 }, +{ 0x19, 42, 0, 21 }, +{ 0x3, 109, 1, 2348 }, +{ 0x3, 110, 1, 2347 }, +{ 0x3, 111, 1, 2346 }, +{ 0x3, 112, 1, 2345 }, +{ 0x3, 113, 1, 2344 }, +{ 0x3, 114, 1, 2343 }, +{ 0x3, 115, 1, 2342 }, +{ 0x3, 116, 1, 2341 }, +{ 0x69, 41, 0, 11 }, +{ 0x14, 100, 1, 2336 }, +{ 0x22, 101, 1, 2333 }, +{ 0x44, 101, 1, 2335 }, +{ 0xa, 108, 1, 2334 }, +{ 0xd1, 41, 0, 9 }, +{ 0x34, 100, 1, 2208 }, +{ 0xc4, 101, 1, 2207 }, +{ 0x1c, 107, 1, 2205 }, +{ 0xe, 122, 0, 2206 }, +{ 0xc, 100, 1, 2496 }, +{ 0xa, 101, 1, 2493 }, +{ 0x14, 101, 1, 2495 }, +{ 0x6, 108, 0, 2494 }, +{ 0x2, 100, 1, 2220 }, +{ 0x2, 101, 1, 2219 }, +{ 0x2, 106, 1, 2218 }, +{ 0x2, 107, 0, 2217 }, +{ 0x12, 100, 1, 2216 }, +{ 0x42, 101, 1, 2215 }, +{ 0x6, 106, 1, 2214 }, +{ 0x6, 107, 0, 2213 }, +{ 0xa, 100, 1, 2340 }, +{ 0x12, 101, 1, 2339 }, +{ 0x24, 101, 1, 2337 }, +{ 0x5, 108, 1, 2338 }, +{ 0x71, 41, 1, 18 }, +{ 0x31, 42, 0, 17 }, +{ 0x1a, 100, 1, 2212 }, +{ 0x32, 101, 1, 2211 }, +{ 0x1a, 107, 1, 2209 }, +{ 0x7, 122, 0, 2210 }, +{ 0x6, 100, 1, 2500 }, +{ 0x6, 101, 1, 2499 }, +{ 0xc, 101, 1, 2497 }, +{ 0x3, 108, 0, 2498 }, +{ 0x1, 100, 1, 2516 }, +{ 0x1, 101, 1, 2515 }, +{ 0x1, 102, 1, 2514 }, +{ 0x1, 103, 1, 2513 }, +{ 0x1, 104, 1, 2512 }, +{ 0x1, 105, 1, 2511 }, +{ 0x1, 106, 1, 2510 }, +{ 0x1, 107, 0, 2509 }, +{ 0x3, 100, 1, 2508 }, +{ 0x3, 101, 1, 2507 }, +{ 0x3, 102, 1, 2506 }, +{ 0x3, 103, 1, 2505 }, +{ 0x3, 104, 1, 2504 }, +{ 0x3, 105, 1, 2503 }, +{ 0x3, 106, 1, 2502 }, +{ 0x3, 107, 0, 2501 }, +{ 0x8, 67, 1, 2380 }, +{ 0x8, 68, 1, 2379 }, +{ 0x2, 73, 1, 2374 }, +{ 0x2, 74, 1, 2373 }, +{ 0x1, 76, 1, 2378 }, +{ 0x1, 77, 1, 2377 }, +{ 0x1, 78, 1, 2376 }, +{ 0x1, 79, 1, 2375 }, +{ 0xf, 41, 1, 30 }, +{ 0x7, 42, 0, 29 }, +{ 0x18, 67, 1, 2372 }, +{ 0x18, 68, 1, 2371 }, +{ 0x6, 73, 1, 2366 }, +{ 0x6, 74, 1, 2365 }, +{ 0x3, 76, 1, 2370 }, +{ 0x3, 77, 1, 2369 }, +{ 0x3, 78, 1, 2368 }, +{ 0x3, 79, 1, 2367 }, +{ 0x1b, 41, 0, 15 }, +{ 0x14, 67, 1, 2360 }, +{ 0x22, 68, 1, 2357 }, +{ 0x44, 68, 1, 2359 }, +{ 0xa, 75, 1, 2358 }, +{ 0x35, 41, 0, 13 }, +{ 0x34, 67, 1, 2224 }, +{ 0xc4, 68, 1, 2223 }, +{ 0x38, 74, 1, 2221 }, +{ 0xe, 85, 0, 2222 }, +{ 0xc, 67, 1, 2520 }, +{ 0xa, 68, 1, 2517 }, +{ 0x14, 68, 1, 2519 }, +{ 0x6, 75, 0, 2518 }, +{ 0x2, 67, 1, 2236 }, +{ 0x2, 68, 1, 2235 }, +{ 0x4, 73, 1, 2234 }, +{ 0x4, 74, 0, 2233 }, +{ 0x12, 67, 1, 2232 }, +{ 0x42, 68, 1, 2231 }, +{ 0xc, 73, 1, 2230 }, +{ 0xc, 74, 0, 2229 }, +{ 0xa, 67, 1, 2364 }, +{ 0x12, 68, 1, 2363 }, +{ 0x24, 68, 1, 2361 }, +{ 0x5, 75, 1, 2362 }, +{ 0x1d, 41, 1, 26 }, +{ 0xd, 42, 0, 25 }, +{ 0x1a, 67, 1, 2228 }, +{ 0x32, 68, 1, 2227 }, +{ 0x34, 74, 1, 2225 }, +{ 0x7, 85, 0, 2226 }, +{ 0x6, 67, 1, 2524 }, +{ 0x6, 68, 1, 2523 }, +{ 0xc, 68, 1, 2521 }, +{ 0x3, 75, 0, 2522 }, +{ 0x1, 67, 1, 2540 }, +{ 0x1, 68, 1, 2539 }, +{ 0x1, 69, 1, 2538 }, +{ 0x1, 70, 1, 2537 }, +{ 0x1, 71, 1, 2536 }, +{ 0x1, 72, 1, 2535 }, +{ 0x1, 73, 1, 2534 }, +{ 0x1, 74, 0, 2533 }, +{ 0x3, 67, 1, 2532 }, +{ 0x3, 68, 1, 2531 }, +{ 0x3, 69, 1, 2530 }, +{ 0x3, 70, 1, 2529 }, +{ 0x3, 71, 1, 2528 }, +{ 0x3, 72, 1, 2527 }, +{ 0x3, 73, 1, 2526 }, +{ 0x3, 74, 0, 2525 }, +{ 0x28, 95, 1, 2388 }, +{ 0x44, 96, 1, 2383 }, +{ 0x88, 96, 1, 2387 }, +{ 0x44, 97, 1, 2382 }, +{ 0x88, 97, 1, 2386 }, +{ 0x44, 98, 1, 2381 }, +{ 0x88, 98, 1, 2385 }, +{ 0x28, 99, 0, 2384 }, +{ 0x68, 95, 1, 2244 }, +{ 0x188, 96, 1, 2243 }, +{ 0x188, 97, 1, 2242 }, +{ 0x188, 98, 1, 2241 }, +{ 0x38, 118, 1, 2240 }, +{ 0x38, 119, 1, 2239 }, +{ 0x38, 120, 1, 2238 }, +{ 0x38, 121, 0, 2237 }, +{ 0x18, 95, 1, 2548 }, +{ 0x14, 96, 1, 2543 }, +{ 0x28, 96, 1, 2547 }, +{ 0x14, 97, 1, 2542 }, +{ 0x28, 97, 1, 2546 }, +{ 0x14, 98, 1, 2541 }, +{ 0x28, 98, 1, 2545 }, +{ 0x18, 99, 0, 2544 }, +{ 0x14, 95, 1, 2396 }, +{ 0x24, 96, 1, 2395 }, +{ 0x48, 96, 1, 2391 }, +{ 0x24, 97, 1, 2394 }, +{ 0x48, 97, 1, 2390 }, +{ 0x24, 98, 1, 2393 }, +{ 0x48, 98, 1, 2389 }, +{ 0x14, 99, 0, 2392 }, +{ 0x34, 95, 1, 2252 }, +{ 0x64, 96, 1, 2251 }, +{ 0x64, 97, 1, 2250 }, +{ 0x64, 98, 1, 2249 }, +{ 0x1c, 118, 1, 2248 }, +{ 0x1c, 119, 1, 2247 }, +{ 0x1c, 120, 1, 2246 }, +{ 0x1c, 121, 0, 2245 }, +{ 0xc, 95, 1, 2556 }, +{ 0xc, 96, 1, 2555 }, +{ 0x18, 96, 1, 2551 }, +{ 0xc, 97, 1, 2554 }, +{ 0x18, 97, 1, 2550 }, +{ 0xc, 98, 1, 2553 }, +{ 0x18, 98, 1, 2549 }, +{ 0xc, 99, 0, 2552 }, +{ 0xa, 95, 1, 2404 }, +{ 0x11, 96, 1, 2399 }, +{ 0x22, 96, 1, 2403 }, +{ 0x11, 97, 1, 2398 }, +{ 0x22, 97, 1, 2402 }, +{ 0x11, 98, 1, 2397 }, +{ 0x22, 98, 1, 2401 }, +{ 0xa, 99, 0, 2400 }, +{ 0x1a, 95, 1, 2260 }, +{ 0x62, 96, 1, 2259 }, +{ 0x62, 97, 1, 2258 }, +{ 0x62, 98, 1, 2257 }, +{ 0xe, 118, 1, 2256 }, +{ 0xe, 119, 1, 2255 }, +{ 0xe, 120, 1, 2254 }, +{ 0xe, 121, 0, 2253 }, +{ 0x6, 95, 1, 2564 }, +{ 0x5, 96, 1, 2559 }, +{ 0xa, 96, 1, 2563 }, +{ 0x5, 97, 1, 2558 }, +{ 0xa, 97, 1, 2562 }, +{ 0x5, 98, 1, 2557 }, +{ 0xa, 98, 1, 2561 }, +{ 0x6, 99, 0, 2560 }, +{ 0x5, 95, 1, 2412 }, +{ 0x9, 96, 1, 2411 }, +{ 0x12, 96, 1, 2407 }, +{ 0x9, 97, 1, 2410 }, +{ 0x12, 97, 1, 2406 }, +{ 0x9, 98, 1, 2409 }, +{ 0x12, 98, 1, 2405 }, +{ 0x5, 99, 0, 2408 }, +{ 0xd, 95, 1, 2268 }, +{ 0x19, 96, 1, 2267 }, +{ 0x19, 97, 1, 2266 }, +{ 0x19, 98, 1, 2265 }, +{ 0x7, 118, 1, 2264 }, +{ 0x7, 119, 1, 2263 }, +{ 0x7, 120, 1, 2262 }, +{ 0x7, 121, 0, 2261 }, +{ 0x3, 95, 1, 2572 }, +{ 0x3, 96, 1, 2571 }, +{ 0x6, 96, 1, 2567 }, +{ 0x3, 97, 1, 2570 }, +{ 0x6, 97, 1, 2566 }, +{ 0x3, 98, 1, 2569 }, +{ 0x6, 98, 1, 2565 }, +{ 0x3, 99, 0, 2568 }, +{ 0x28, 62, 1, 2420 }, +{ 0x44, 63, 1, 2415 }, +{ 0x88, 63, 1, 2419 }, +{ 0x44, 64, 1, 2414 }, +{ 0x88, 64, 1, 2418 }, +{ 0x44, 65, 1, 2413 }, +{ 0x88, 65, 1, 2417 }, +{ 0x28, 66, 0, 2416 }, +{ 0x68, 62, 1, 2276 }, +{ 0x188, 63, 1, 2275 }, +{ 0x188, 64, 1, 2274 }, +{ 0x188, 65, 1, 2273 }, +{ 0x38, 81, 1, 2272 }, +{ 0x38, 82, 1, 2271 }, +{ 0x38, 83, 1, 2270 }, +{ 0x38, 84, 0, 2269 }, +{ 0x18, 62, 1, 2580 }, +{ 0x14, 63, 1, 2575 }, +{ 0x28, 63, 1, 2579 }, +{ 0x14, 64, 1, 2574 }, +{ 0x28, 64, 1, 2578 }, +{ 0x14, 65, 1, 2573 }, +{ 0x28, 65, 1, 2577 }, +{ 0x18, 66, 0, 2576 }, +{ 0x14, 62, 1, 2428 }, +{ 0x24, 63, 1, 2427 }, +{ 0x48, 63, 1, 2423 }, +{ 0x24, 64, 1, 2426 }, +{ 0x48, 64, 1, 2422 }, +{ 0x24, 65, 1, 2425 }, +{ 0x48, 65, 1, 2421 }, +{ 0x14, 66, 0, 2424 }, +{ 0x34, 62, 1, 2284 }, +{ 0x64, 63, 1, 2283 }, +{ 0x64, 64, 1, 2282 }, +{ 0x64, 65, 1, 2281 }, +{ 0x1c, 81, 1, 2280 }, +{ 0x1c, 82, 1, 2279 }, +{ 0x1c, 83, 1, 2278 }, +{ 0x1c, 84, 0, 2277 }, +{ 0xc, 62, 1, 2588 }, +{ 0xc, 63, 1, 2587 }, +{ 0x18, 63, 1, 2583 }, +{ 0xc, 64, 1, 2586 }, +{ 0x18, 64, 1, 2582 }, +{ 0xc, 65, 1, 2585 }, +{ 0x18, 65, 1, 2581 }, +{ 0xc, 66, 0, 2584 }, +{ 0xa, 62, 1, 2436 }, +{ 0x11, 63, 1, 2431 }, +{ 0x22, 63, 1, 2435 }, +{ 0x11, 64, 1, 2430 }, +{ 0x22, 64, 1, 2434 }, +{ 0x11, 65, 1, 2429 }, +{ 0x22, 65, 1, 2433 }, +{ 0xa, 66, 0, 2432 }, +{ 0x1a, 62, 1, 2292 }, +{ 0x62, 63, 1, 2291 }, +{ 0x62, 64, 1, 2290 }, +{ 0x62, 65, 1, 2289 }, +{ 0xe, 81, 1, 2288 }, +{ 0xe, 82, 1, 2287 }, +{ 0xe, 83, 1, 2286 }, +{ 0xe, 84, 0, 2285 }, +{ 0x6, 62, 1, 2596 }, +{ 0x5, 63, 1, 2591 }, +{ 0xa, 63, 1, 2595 }, +{ 0x5, 64, 1, 2590 }, +{ 0xa, 64, 1, 2594 }, +{ 0x5, 65, 1, 2589 }, +{ 0xa, 65, 1, 2593 }, +{ 0x6, 66, 0, 2592 }, +{ 0x5, 62, 1, 2444 }, +{ 0x9, 63, 1, 2443 }, +{ 0x12, 63, 1, 2439 }, +{ 0x9, 64, 1, 2442 }, +{ 0x12, 64, 1, 2438 }, +{ 0x9, 65, 1, 2441 }, +{ 0x12, 65, 1, 2437 }, +{ 0x5, 66, 0, 2440 }, +{ 0xd, 62, 1, 2300 }, +{ 0x19, 63, 1, 2299 }, +{ 0x19, 64, 1, 2298 }, +{ 0x19, 65, 1, 2297 }, +{ 0x7, 81, 1, 2296 }, +{ 0x7, 82, 1, 2295 }, +{ 0x7, 83, 1, 2294 }, +{ 0x7, 84, 0, 2293 }, +{ 0x3, 62, 1, 2604 }, +{ 0x3, 63, 1, 2603 }, +{ 0x6, 63, 1, 2599 }, +{ 0x3, 64, 1, 2602 }, +{ 0x6, 64, 1, 2598 }, +{ 0x3, 65, 1, 2601 }, +{ 0x6, 65, 1, 2597 }, +{ 0x3, 66, 0, 2600 }, +{ 0x8, 86, 1, 2468 }, +{ 0x8, 87, 1, 2467 }, +{ 0x2, 88, 1, 2466 }, +{ 0x2, 89, 1, 2465 }, +{ 0x2, 90, 1, 2464 }, +{ 0x2, 91, 1, 2463 }, +{ 0x2, 92, 1, 2462 }, +{ 0x2, 93, 0, 2461 }, +{ 0x18, 86, 1, 2460 }, +{ 0x18, 87, 1, 2459 }, +{ 0x6, 88, 1, 2458 }, +{ 0x6, 89, 1, 2457 }, +{ 0x6, 90, 1, 2456 }, +{ 0x6, 91, 1, 2455 }, +{ 0x6, 92, 1, 2454 }, +{ 0x6, 93, 0, 2453 }, +{ 0x14, 86, 1, 2448 }, +{ 0x22, 87, 1, 2445 }, +{ 0x44, 87, 1, 2447 }, +{ 0xa, 94, 0, 2446 }, +{ 0x34, 86, 1, 2304 }, +{ 0xc4, 87, 1, 2303 }, +{ 0x38, 93, 1, 2301 }, +{ 0xe, 117, 0, 2302 }, +{ 0xc, 86, 1, 2608 }, +{ 0xa, 87, 1, 2605 }, +{ 0x14, 87, 1, 2607 }, +{ 0x6, 94, 0, 2606 }, +{ 0x2, 86, 1, 2316 }, +{ 0x2, 87, 1, 2315 }, +{ 0x4, 92, 1, 2314 }, +{ 0x4, 93, 0, 2313 }, +{ 0x12, 86, 1, 2312 }, +{ 0x42, 87, 1, 2311 }, +{ 0xc, 92, 1, 2310 }, +{ 0xc, 93, 0, 2309 }, +{ 0xa, 86, 1, 2452 }, +{ 0x12, 87, 1, 2451 }, +{ 0x24, 87, 1, 2449 }, +{ 0x5, 94, 0, 2450 }, +{ 0x1a, 86, 1, 2308 }, +{ 0x32, 87, 1, 2307 }, +{ 0x34, 93, 1, 2305 }, +{ 0x7, 117, 0, 2306 }, +{ 0x6, 86, 1, 2612 }, +{ 0x6, 87, 1, 2611 }, +{ 0xc, 87, 1, 2609 }, +{ 0x3, 94, 0, 2610 }, +{ 0x1, 86, 1, 2628 }, +{ 0x1, 87, 1, 2627 }, +{ 0x1, 88, 1, 2626 }, +{ 0x1, 89, 1, 2625 }, +{ 0x1, 90, 1, 2624 }, +{ 0x1, 91, 1, 2623 }, +{ 0x1, 92, 1, 2622 }, +{ 0x1, 93, 0, 2621 }, +{ 0x3, 86, 1, 2620 }, +{ 0x3, 87, 1, 2619 }, +{ 0x3, 88, 1, 2618 }, +{ 0x3, 89, 1, 2617 }, +{ 0x3, 90, 1, 2616 }, +{ 0x3, 91, 1, 2615 }, +{ 0x3, 92, 1, 2614 }, +{ 0x3, 93, 0, 2613 }, +{ 0x8, 53, 1, 2492 }, +{ 0x8, 54, 1, 2491 }, +{ 0x2, 55, 1, 2490 }, +{ 0x2, 56, 1, 2489 }, +{ 0x2, 57, 1, 2488 }, +{ 0x2, 58, 1, 2487 }, +{ 0x2, 59, 1, 2486 }, +{ 0x2, 60, 0, 2485 }, +{ 0x18, 53, 1, 2484 }, +{ 0x18, 54, 1, 2483 }, +{ 0x6, 55, 1, 2482 }, +{ 0x6, 56, 1, 2481 }, +{ 0x6, 57, 1, 2480 }, +{ 0x6, 58, 1, 2479 }, +{ 0x6, 59, 1, 2478 }, +{ 0x6, 60, 0, 2477 }, +{ 0x14, 53, 1, 2472 }, +{ 0x22, 54, 1, 2469 }, +{ 0x44, 54, 1, 2471 }, +{ 0xa, 61, 0, 2470 }, +{ 0x34, 53, 1, 2320 }, +{ 0xc4, 54, 1, 2319 }, +{ 0x38, 60, 1, 2317 }, +{ 0xe, 80, 0, 2318 }, +{ 0xc, 53, 1, 2632 }, +{ 0xa, 54, 1, 2629 }, +{ 0x14, 54, 1, 2631 }, +{ 0x6, 61, 0, 2630 }, +{ 0x2, 53, 1, 2332 }, +{ 0x2, 54, 1, 2331 }, +{ 0x4, 59, 1, 2330 }, +{ 0x4, 60, 0, 2329 }, +{ 0x12, 53, 1, 2328 }, +{ 0x42, 54, 1, 2327 }, +{ 0xc, 59, 1, 2326 }, +{ 0xc, 60, 0, 2325 }, +{ 0xa, 53, 1, 2476 }, +{ 0x12, 54, 1, 2475 }, +{ 0x24, 54, 1, 2473 }, +{ 0x5, 61, 0, 2474 }, +{ 0x1a, 53, 1, 2324 }, +{ 0x32, 54, 1, 2323 }, +{ 0x34, 60, 1, 2321 }, +{ 0x7, 80, 0, 2322 }, +{ 0x6, 53, 1, 2636 }, +{ 0x6, 54, 1, 2635 }, +{ 0xc, 54, 1, 2633 }, +{ 0x3, 61, 0, 2634 }, +{ 0x1, 53, 1, 2652 }, +{ 0x1, 54, 1, 2651 }, +{ 0x1, 55, 1, 2650 }, +{ 0x1, 56, 1, 2649 }, +{ 0x1, 57, 1, 2648 }, +{ 0x1, 58, 1, 2647 }, +{ 0x1, 59, 1, 2646 }, +{ 0x1, 60, 0, 2645 }, +{ 0x3, 53, 1, 2644 }, +{ 0x3, 54, 1, 2643 }, +{ 0x3, 55, 1, 2642 }, +{ 0x3, 56, 1, 2641 }, +{ 0x3, 57, 1, 2640 }, +{ 0x3, 58, 1, 2639 }, +{ 0x3, 59, 1, 2638 }, +{ 0x3, 60, 0, 2637 }, +{ 0x1, 4, 0, 2653 }, +{ 0x1, 296, 0, 2654 }, +{ 0x1, 379, 0, 2655 }, +{ 0x1, 374, 0, 2656 }, +{ 0x2, 358, 0, 2657 }, +{ 0x1, 358, 0, 2660 }, +{ 0x2, 357, 0, 2658 }, +{ 0x1, 357, 0, 2661 }, +{ 0x2, 356, 0, 2659 }, +{ 0x1, 356, 0, 2662 }, +{ 0x1, 355, 0, 2663 }, +{ 0x1, 354, 0, 2664 }, +{ 0x2, 353, 0, 2665 }, +{ 0x1, 353, 0, 2667 }, +{ 0x2, 352, 0, 2666 }, +{ 0x1, 352, 0, 2668 }, +{ 0x1, 382, 0, 2675 }, +{ 0x8, 381, 0, 2669 }, +{ 0x4, 381, 0, 2671 }, +{ 0x2, 381, 0, 2673 }, +{ 0x1, 381, 0, 2676 }, +{ 0x8, 380, 0, 2670 }, +{ 0x4, 380, 0, 2672 }, +{ 0x2, 380, 0, 2674 }, +{ 0x1, 380, 0, 2677 }, +{ 0x1, 351, 0, 2684 }, +{ 0x8, 350, 0, 2678 }, +{ 0x4, 350, 0, 2680 }, +{ 0x2, 350, 0, 2682 }, +{ 0x1, 350, 0, 2685 }, +{ 0x8, 349, 0, 2679 }, +{ 0x4, 349, 0, 2681 }, +{ 0x2, 349, 1, 2683 }, +{ 0x4, 143, 0, 1377 }, +{ 0x1, 349, 0, 2686 }, +{ 0x1, 6, 0, 2687 }, +{ 0x1, 7, 0, 2688 }, +{ 0x1, 295, 0, 2689 }, +{ 0x1, 456, 0, 2690 }, +{ 0x1, 346, 0, 2691 }, +{ 0x1, 13, 0, 2692 }, +{ 0x1, 11, 0, 2693 }, +{ 0x1, 422, 0, 2694 }, +{ 0x1, 394, 0, 2695 }, +{ 0x1, 393, 0, 2696 }, +{ 0x1, 455, 0, 2697 }, +{ 0x1, 345, 0, 2698 }, +{ 0x1, 12, 0, 2699 }, +{ 0x1, 10, 0, 2700 }, +{ 0x1, 5, 0, 2701 }, +{ 0x1, 421, 0, 2702 }, +{ 0x1, 420, 0, 2703 }, +{ 0x1, 1, 0, 2704 }, +{ 0x1, 0, 0, 2705 }, +}; + + +/* ia64-opc.c -- Functions to access the compacted opcode table + Copyright 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + Written by Bob Manson of Cygnus Solutions, + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, see + . */ + +static const struct ia64_templ_desc ia64_templ_desc[16] = + { + { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, /* 0 */ + { 2, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, + { 0, { IA64_UNIT_M, IA64_UNIT_L, IA64_UNIT_X }, "MLX" }, + { 0, { 0, }, "-3-" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, /* 4 */ + { 1, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, + { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_I }, "MFI" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_F }, "MMF" }, + { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_B }, "MIB" }, /* 8 */ + { 0, { IA64_UNIT_M, IA64_UNIT_B, IA64_UNIT_B }, "MBB" }, + { 0, { 0, }, "-a-" }, + { 0, { IA64_UNIT_B, IA64_UNIT_B, IA64_UNIT_B }, "BBB" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_B }, "MMB" }, /* c */ + { 0, { 0, }, "-d-" }, + { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_B }, "MFB" }, + { 0, { 0, }, "-f-" }, + }; + +/* Apply the completer referred to by COMPLETER_INDEX to OPCODE, and + return the result. */ + +static ia64_insn +apply_completer (ia64_insn opcode, int completer_index) +{ + ia64_insn mask = completer_table[completer_index].mask; + ia64_insn bits = completer_table[completer_index].bits; + int shiftamt = (completer_table[completer_index].offset & 63); + + mask = mask << shiftamt; + bits = bits << shiftamt; + opcode = (opcode & ~mask) | bits; + return opcode; +} + +/* Extract BITS number of bits starting from OP_POINTER + BITOFFSET in + the dis_table array, and return its value. (BITOFFSET is numbered + starting from MSB to LSB, so a BITOFFSET of 0 indicates the MSB of the + first byte in OP_POINTER.) */ + +static int +extract_op_bits (int op_pointer, int bitoffset, int bits) +{ + int res = 0; + + op_pointer += (bitoffset / 8); + + if (bitoffset % 8) + { + unsigned int op = dis_table[op_pointer++]; + int numb = 8 - (bitoffset % 8); + int mask = (1 << numb) - 1; + int bata = (bits < numb) ? bits : numb; + int delta = numb - bata; + + res = (res << bata) | ((op & mask) >> delta); + bitoffset += bata; + bits -= bata; + } + while (bits >= 8) + { + res = (res << 8) | (dis_table[op_pointer++] & 255); + bits -= 8; + } + if (bits > 0) + { + unsigned int op = (dis_table[op_pointer++] & 255); + res = (res << bits) | (op >> (8 - bits)); + } + return res; +} + +/* Examine the state machine entry at OP_POINTER in the dis_table + array, and extract its values into OPVAL and OP. The length of the + state entry in bits is returned. */ + +static int +extract_op (int op_pointer, int *opval, unsigned int *op) +{ + int oplen = 5; + + *op = dis_table[op_pointer]; + + if ((*op) & 0x40) + { + opval[0] = extract_op_bits (op_pointer, oplen, 5); + oplen += 5; + } + switch ((*op) & 0x30) + { + case 0x10: + { + opval[1] = extract_op_bits (op_pointer, oplen, 8); + oplen += 8; + opval[1] += op_pointer; + break; + } + case 0x20: + { + opval[1] = extract_op_bits (op_pointer, oplen, 16); + if (! (opval[1] & 32768)) + { + opval[1] += op_pointer; + } + oplen += 16; + break; + } + case 0x30: + { + oplen--; + opval[2] = extract_op_bits (op_pointer, oplen, 12); + oplen += 12; + opval[2] |= 32768; + break; + } + } + if (((*op) & 0x08) && (((*op) & 0x30) != 0x30)) + { + opval[2] = extract_op_bits (op_pointer, oplen, 16); + oplen += 16; + if (! (opval[2] & 32768)) + { + opval[2] += op_pointer; + } + } + return oplen; +} + +/* Returns a non-zero value if the opcode in the main_table list at + PLACE matches OPCODE and is of type TYPE. */ + +static int +opcode_verify (ia64_insn opcode, int place, enum ia64_insn_type type) +{ + if (main_table[place].opcode_type != type) + { + return 0; + } + if (main_table[place].flags + & (IA64_OPCODE_F2_EQ_F3 | IA64_OPCODE_LEN_EQ_64MCNT)) + { + const struct ia64_operand *o1, *o2; + ia64_insn f2, f3; + + if (main_table[place].flags & IA64_OPCODE_F2_EQ_F3) + { + o1 = elf64_ia64_operands + IA64_OPND_F2; + o2 = elf64_ia64_operands + IA64_OPND_F3; + (*o1->extract) (o1, opcode, &f2); + (*o2->extract) (o2, opcode, &f3); + if (f2 != f3) + return 0; + } + else + { + ia64_insn len, count; + + /* length must equal 64-count: */ + o1 = elf64_ia64_operands + IA64_OPND_LEN6; + o2 = elf64_ia64_operands + main_table[place].operands[2]; + (*o1->extract) (o1, opcode, &len); + (*o2->extract) (o2, opcode, &count); + if (len != 64 - count) + return 0; + } + } + return 1; +} + +/* Find an instruction entry in the ia64_dis_names array that matches + opcode OPCODE and is of type TYPE. Returns either a positive index + into the array, or a negative value if an entry for OPCODE could + not be found. Checks all matches and returns the one with the highest + priority. */ + +static int +locate_opcode_ent (ia64_insn opcode, enum ia64_insn_type type) +{ + int currtest[41]; + int bitpos[41]; + int op_ptr[41]; + int currstatenum = 0; + short found_disent = -1; + short found_priority = -1; + + currtest[currstatenum] = 0; + op_ptr[currstatenum] = 0; + bitpos[currstatenum] = 40; + + while (1) + { + int op_pointer = op_ptr[currstatenum]; + unsigned int op; + int currbitnum = bitpos[currstatenum]; + int oplen; + int opval[3] = {0}; + int next_op; + int currbit; + + oplen = extract_op (op_pointer, opval, &op); + + bitpos[currstatenum] = currbitnum; + + /* Skip opval[0] bits in the instruction. */ + if (op & 0x40) + { + currbitnum -= opval[0]; + } + + /* The value of the current bit being tested. */ + currbit = opcode & (((ia64_insn) 1) << currbitnum) ? 1 : 0; + next_op = -1; + + /* We always perform the tests specified in the current state in + a particular order, falling through to the next test if the + previous one failed. */ + switch (currtest[currstatenum]) + { + case 0: + currtest[currstatenum]++; + if (currbit == 0 && (op & 0x80)) + { + /* Check for a zero bit. If this test solely checks for + a zero bit, we can check for up to 8 consecutive zero + bits (the number to check is specified by the lower 3 + bits in the state code.) + + If the state instruction matches, we go to the very + next state instruction; otherwise, try the next test. */ + + if ((op & 0xf8) == 0x80) + { + int count = op & 0x7; + int x; + + for (x = 0; x <= count; x++) + { + int i = + opcode & (((ia64_insn) 1) << (currbitnum - x)) ? 1 : 0; + if (i) + { + break; + } + } + if (x > count) + { + next_op = op_pointer + ((oplen + 7) / 8); + currbitnum -= count; + break; + } + } + else if (! currbit) + { + next_op = op_pointer + ((oplen + 7) / 8); + break; + } + } + /* FALLTHROUGH */ + case 1: + /* If the bit in the instruction is one, go to the state + instruction specified by opval[1]. */ + currtest[currstatenum]++; + if (currbit && (op & 0x30) != 0 && ((op & 0x30) != 0x30)) + { + next_op = opval[1]; + break; + } + /* FALLTHROUGH */ + case 2: + /* Don't care. Skip the current bit and go to the state + instruction specified by opval[2]. + + An encoding of 0x30 is special; this means that a 12-bit + offset into the ia64_dis_names[] array is specified. */ + currtest[currstatenum]++; + if ((op & 0x08) || ((op & 0x30) == 0x30)) + { + next_op = opval[2]; + break; + } + } + + /* If bit 15 is set in the address of the next state, an offset + in the ia64_dis_names array was specified instead. We then + check to see if an entry in the list of opcodes matches the + opcode we were given; if so, we have succeeded. */ + + if ((next_op >= 0) && (next_op & 32768)) + { + short disent = next_op & 32767; + short priority = -1; + + if (next_op > 65535) + { + abort (); + } + + /* Run through the list of opcodes to check, trying to find + one that matches. */ + while (disent >= 0) + { + int place = ia64_dis_names[disent].insn_index; + + priority = ia64_dis_names[disent].priority; + + if (opcode_verify (opcode, place, type) + && priority > found_priority) + { + break; + } + if (ia64_dis_names[disent].next_flag) + { + disent++; + } + else + { + disent = -1; + } + } + + if (disent >= 0) + { + found_disent = disent; + found_priority = priority; + } + /* Try the next test in this state, regardless of whether a match + was found. */ + next_op = -2; + } + + /* next_op == -1 is "back up to the previous state". + next_op == -2 is "stay in this state and try the next test". + Otherwise, transition to the state indicated by next_op. */ + + if (next_op == -1) + { + currstatenum--; + if (currstatenum < 0) + { + return found_disent; + } + } + else if (next_op >= 0) + { + currstatenum++; + bitpos[currstatenum] = currbitnum - 1; + op_ptr[currstatenum] = next_op; + currtest[currstatenum] = 0; + } + } +} + +/* Construct an ia64_opcode entry based on OPCODE, NAME and PLACE. */ + +static struct ia64_opcode * +make_ia64_opcode (ia64_insn opcode, const char *name, int place, int depind) +{ + struct ia64_opcode *res = + (struct ia64_opcode *) malloc (sizeof (struct ia64_opcode)); + res->name = strdup (name); + res->type = main_table[place].opcode_type; + res->num_outputs = main_table[place].num_outputs; + res->opcode = opcode; + res->mask = main_table[place].mask; + res->operands[0] = main_table[place].operands[0]; + res->operands[1] = main_table[place].operands[1]; + res->operands[2] = main_table[place].operands[2]; + res->operands[3] = main_table[place].operands[3]; + res->operands[4] = main_table[place].operands[4]; + res->flags = main_table[place].flags; + res->ent_index = place; + res->dependencies = &op_dependencies[depind]; + return res; +} + +/* Determine the ia64_opcode entry for the opcode specified by INSN + and TYPE. If a valid entry is not found, return NULL. */ +static struct ia64_opcode * +ia64_dis_opcode (ia64_insn insn, enum ia64_insn_type type) +{ + int disent = locate_opcode_ent (insn, type); + + if (disent < 0) + { + return NULL; + } + else + { + unsigned int cb = ia64_dis_names[disent].completer_index; + static char name[128]; + int place = ia64_dis_names[disent].insn_index; + int ci = main_table[place].completers; + ia64_insn tinsn = main_table[place].opcode; + + strcpy (name, ia64_strings [main_table[place].name_index]); + + while (cb) + { + if (cb & 1) + { + int cname = completer_table[ci].name_index; + + tinsn = apply_completer (tinsn, ci); + + if (ia64_strings[cname][0] != '\0') + { + strcat (name, "."); + strcat (name, ia64_strings[cname]); + } + if (cb != 1) + { + ci = completer_table[ci].subentries; + } + } + else + { + ci = completer_table[ci].alternative; + } + if (ci < 0) + { + abort (); + } + cb = cb >> 1; + } + if (tinsn != (insn & main_table[place].mask)) + { + abort (); + } + return make_ia64_opcode (insn, name, place, + completer_table[ci].dependencies); + } +} + +/* Free any resources used by ENT. */ +static void +ia64_free_opcode (struct ia64_opcode *ent) +{ + free ((void *)ent->name); + free (ent); +} + +/* Disassemble ia64 instruction. */ + +/* Return the instruction type for OPCODE found in unit UNIT. */ + +static enum ia64_insn_type +unit_to_type (ia64_insn opcode, enum ia64_unit unit) +{ + enum ia64_insn_type type; + int op; + + op = IA64_OP (opcode); + + if (op >= 8 && (unit == IA64_UNIT_I || unit == IA64_UNIT_M)) + { + type = IA64_TYPE_A; + } + else + { + switch (unit) + { + case IA64_UNIT_I: + type = IA64_TYPE_I; break; + case IA64_UNIT_M: + type = IA64_TYPE_M; break; + case IA64_UNIT_B: + type = IA64_TYPE_B; break; + case IA64_UNIT_F: + type = IA64_TYPE_F; break; + case IA64_UNIT_L: + case IA64_UNIT_X: + type = IA64_TYPE_X; break; + default: + type = -1; + } + } + return type; +} + +int +print_insn_ia64 (bfd_vma memaddr, struct disassemble_info *info) +{ + ia64_insn t0, t1, slot[3], template, s_bit, insn; + int slotnum, j, status, need_comma, retval, slot_multiplier; + const struct ia64_operand *odesc; + const struct ia64_opcode *idesc; + const char *err, *str, *tname; + uint64_t value; + bfd_byte bundle[16]; + enum ia64_unit unit; + char regname[16]; + + if (info->bytes_per_line == 0) + info->bytes_per_line = 6; + info->display_endian = info->endian; + + slot_multiplier = info->bytes_per_line; + retval = slot_multiplier; + + slotnum = (((long) memaddr) & 0xf) / slot_multiplier; + if (slotnum > 2) + return -1; + + memaddr -= (memaddr & 0xf); + status = (*info->read_memory_func) (memaddr, bundle, sizeof (bundle), info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + /* bundles are always in little-endian byte order */ + t0 = bfd_getl64 (bundle); + t1 = bfd_getl64 (bundle + 8); + s_bit = t0 & 1; + template = (t0 >> 1) & 0xf; + slot[0] = (t0 >> 5) & 0x1ffffffffffLL; + slot[1] = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18); + slot[2] = (t1 >> 23) & 0x1ffffffffffLL; + + tname = ia64_templ_desc[template].name; + if (slotnum == 0) + (*info->fprintf_func) (info->stream, "[%s] ", tname); + else + (*info->fprintf_func) (info->stream, " "); + + unit = ia64_templ_desc[template].exec_unit[slotnum]; + + if (template == 2 && slotnum == 1) + { + /* skip L slot in MLI template: */ + slotnum = 2; + retval += slot_multiplier; + } + + insn = slot[slotnum]; + + if (unit == IA64_UNIT_NIL) + goto decoding_failed; + + idesc = ia64_dis_opcode (insn, unit_to_type (insn, unit)); + if (idesc == NULL) + goto decoding_failed; + + /* print predicate, if any: */ + + if ((idesc->flags & IA64_OPCODE_NO_PRED) + || (insn & 0x3f) == 0) + (*info->fprintf_func) (info->stream, " "); + else + (*info->fprintf_func) (info->stream, "(p%02d) ", (int)(insn & 0x3f)); + + /* now the actual instruction: */ + + (*info->fprintf_func) (info->stream, "%s", idesc->name); + if (idesc->operands[0]) + (*info->fprintf_func) (info->stream, " "); + + need_comma = 0; + for (j = 0; j < NELEMS (idesc->operands) && idesc->operands[j]; ++j) + { + odesc = elf64_ia64_operands + idesc->operands[j]; + + if (need_comma) + (*info->fprintf_func) (info->stream, ","); + + if (odesc - elf64_ia64_operands == IA64_OPND_IMMU64) + { + /* special case of 64 bit immediate load: */ + value = ((insn >> 13) & 0x7f) | (((insn >> 27) & 0x1ff) << 7) + | (((insn >> 22) & 0x1f) << 16) | (((insn >> 21) & 0x1) << 21) + | (slot[1] << 22) | (((insn >> 36) & 0x1) << 63); + } + else if (odesc - elf64_ia64_operands == IA64_OPND_IMMU62) + { + /* 62-bit immediate for nop.x/break.x */ + value = ((slot[1] & 0x1ffffffffffLL) << 21) + | (((insn >> 36) & 0x1) << 20) + | ((insn >> 6) & 0xfffff); + } + else if (odesc - elf64_ia64_operands == IA64_OPND_TGT64) + { + /* 60-bit immediate for long branches. */ + value = (((insn >> 13) & 0xfffff) + | (((insn >> 36) & 1) << 59) + | (((slot[1] >> 2) & 0x7fffffffffLL) << 20)) << 4; + } + else + { + err = (*odesc->extract) (odesc, insn, &value); + if (err) + { + (*info->fprintf_func) (info->stream, "%s", err); + goto done; + } + } + + switch (odesc->class) + { + case IA64_OPND_CLASS_CST: + (*info->fprintf_func) (info->stream, "%s", odesc->str); + break; + + case IA64_OPND_CLASS_REG: + if (odesc->str[0] == 'a' && odesc->str[1] == 'r') + { + switch (value) + { + case 0: case 1: case 2: case 3: + case 4: case 5: case 6: case 7: + sprintf (regname, "ar.k%u", (unsigned int) value); + break; + case 16: strcpy (regname, "ar.rsc"); break; + case 17: strcpy (regname, "ar.bsp"); break; + case 18: strcpy (regname, "ar.bspstore"); break; + case 19: strcpy (regname, "ar.rnat"); break; + case 32: strcpy (regname, "ar.ccv"); break; + case 36: strcpy (regname, "ar.unat"); break; + case 40: strcpy (regname, "ar.fpsr"); break; + case 44: strcpy (regname, "ar.itc"); break; + case 64: strcpy (regname, "ar.pfs"); break; + case 65: strcpy (regname, "ar.lc"); break; + case 66: strcpy (regname, "ar.ec"); break; + default: + sprintf (regname, "ar%u", (unsigned int) value); + break; + } + (*info->fprintf_func) (info->stream, "%s", regname); + } + else + (*info->fprintf_func) (info->stream, "%s%d", odesc->str, (int)value); + break; + + case IA64_OPND_CLASS_IND: + (*info->fprintf_func) (info->stream, "%s[r%d]", odesc->str, (int)value); + break; + + case IA64_OPND_CLASS_ABS: + str = 0; + if (odesc - elf64_ia64_operands == IA64_OPND_MBTYPE4) + switch (value) + { + case 0x0: str = "@brcst"; break; + case 0x8: str = "@mix"; break; + case 0x9: str = "@shuf"; break; + case 0xa: str = "@alt"; break; + case 0xb: str = "@rev"; break; + } + + if (str) + (*info->fprintf_func) (info->stream, "%s", str); + else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_SIGNED) + (*info->fprintf_func) (info->stream, "%" PRId64, + (int64_t) value); + else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_UNSIGNED) + (*info->fprintf_func) (info->stream, "%" PRIu64, + (uint64_t) value); + else + (*info->fprintf_func) (info->stream, "0x%" PRIx64, + (uint64_t) value); + break; + + case IA64_OPND_CLASS_REL: + (*info->print_address_func) (memaddr + value, info); + break; + } + + need_comma = 1; + if (j + 1 == idesc->num_outputs) + { + (*info->fprintf_func) (info->stream, "="); + need_comma = 0; + } + } + if (slotnum + 1 == ia64_templ_desc[template].group_boundary + || ((slotnum == 2) && s_bit)) + (*info->fprintf_func) (info->stream, ";;"); + + done: + ia64_free_opcode ((struct ia64_opcode *)idesc); + failed: + if (slotnum == 2) + retval += 16 - 3*slot_multiplier; + return retval; + + decoding_failed: + (*info->fprintf_func) (info->stream, " data8 %#011llx", (long long) insn); + goto failed; +} diff --git a/disas/libvixl/LICENCE b/disas/libvixl/LICENCE new file mode 100644 index 0000000..b7e160a --- /dev/null +++ b/disas/libvixl/LICENCE @@ -0,0 +1,30 @@ +LICENCE +======= + +The software in this repository is covered by the following licence. + +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/disas/libvixl/Makefile.objs b/disas/libvixl/Makefile.objs new file mode 100644 index 0000000..17e6565 --- /dev/null +++ b/disas/libvixl/Makefile.objs @@ -0,0 +1,8 @@ +libvixl_OBJS = utils.o \ + a64/instructions-a64.o \ + a64/decoder-a64.o \ + a64/disasm-a64.o + +$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CFLAGS) + +common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS) diff --git a/disas/libvixl/README b/disas/libvixl/README new file mode 100644 index 0000000..58db41c --- /dev/null +++ b/disas/libvixl/README @@ -0,0 +1,12 @@ + +The code in this directory is a subset of libvixl: + https://github.com/armvixl/vixl +(specifically, it is the set of files needed for disassembly only, +taken from libvixl 1.7). +Bugfixes should preferably be sent upstream initially. + +The disassembler does not currently support the entire A64 instruction +set. Notably: + * No Advanced SIMD support. + * Limited support for system instructions. + * A few miscellaneous integer and floating point instructions are missing. diff --git a/disas/libvixl/a64/assembler-a64.h b/disas/libvixl/a64/assembler-a64.h new file mode 100644 index 0000000..35aaf20 --- /dev/null +++ b/disas/libvixl/a64/assembler-a64.h @@ -0,0 +1,2353 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_ASSEMBLER_A64_H_ +#define VIXL_A64_ASSEMBLER_A64_H_ + +#include +#include + +#include "globals.h" +#include "utils.h" +#include "code-buffer.h" +#include "a64/instructions-a64.h" + +namespace vixl { + +typedef uint64_t RegList; +static const int kRegListSizeInBits = sizeof(RegList) * 8; + + +// Registers. + +// Some CPURegister methods can return Register and FPRegister types, so we +// need to declare them in advance. +class Register; +class FPRegister; + + +class CPURegister { + public: + enum RegisterType { + // The kInvalid value is used to detect uninitialized static instances, + // which are always zero-initialized before any constructors are called. + kInvalid = 0, + kRegister, + kFPRegister, + kNoRegister + }; + + CPURegister() : code_(0), size_(0), type_(kNoRegister) { + VIXL_ASSERT(!IsValid()); + VIXL_ASSERT(IsNone()); + } + + CPURegister(unsigned code, unsigned size, RegisterType type) + : code_(code), size_(size), type_(type) { + VIXL_ASSERT(IsValidOrNone()); + } + + unsigned code() const { + VIXL_ASSERT(IsValid()); + return code_; + } + + RegisterType type() const { + VIXL_ASSERT(IsValidOrNone()); + return type_; + } + + RegList Bit() const { + VIXL_ASSERT(code_ < (sizeof(RegList) * 8)); + return IsValid() ? (static_cast(1) << code_) : 0; + } + + unsigned size() const { + VIXL_ASSERT(IsValid()); + return size_; + } + + int SizeInBytes() const { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(size() % 8 == 0); + return size_ / 8; + } + + int SizeInBits() const { + VIXL_ASSERT(IsValid()); + return size_; + } + + bool Is32Bits() const { + VIXL_ASSERT(IsValid()); + return size_ == 32; + } + + bool Is64Bits() const { + VIXL_ASSERT(IsValid()); + return size_ == 64; + } + + bool IsValid() const { + if (IsValidRegister() || IsValidFPRegister()) { + VIXL_ASSERT(!IsNone()); + return true; + } else { + VIXL_ASSERT(IsNone()); + return false; + } + } + + bool IsValidRegister() const { + return IsRegister() && + ((size_ == kWRegSize) || (size_ == kXRegSize)) && + ((code_ < kNumberOfRegisters) || (code_ == kSPRegInternalCode)); + } + + bool IsValidFPRegister() const { + return IsFPRegister() && + ((size_ == kSRegSize) || (size_ == kDRegSize)) && + (code_ < kNumberOfFPRegisters); + } + + bool IsNone() const { + // kNoRegister types should always have size 0 and code 0. + VIXL_ASSERT((type_ != kNoRegister) || (code_ == 0)); + VIXL_ASSERT((type_ != kNoRegister) || (size_ == 0)); + + return type_ == kNoRegister; + } + + bool Aliases(const CPURegister& other) const { + VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); + return (code_ == other.code_) && (type_ == other.type_); + } + + bool Is(const CPURegister& other) const { + VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); + return Aliases(other) && (size_ == other.size_); + } + + bool IsZero() const { + VIXL_ASSERT(IsValid()); + return IsRegister() && (code_ == kZeroRegCode); + } + + bool IsSP() const { + VIXL_ASSERT(IsValid()); + return IsRegister() && (code_ == kSPRegInternalCode); + } + + bool IsRegister() const { + return type_ == kRegister; + } + + bool IsFPRegister() const { + return type_ == kFPRegister; + } + + bool IsW() const { return IsValidRegister() && Is32Bits(); } + bool IsX() const { return IsValidRegister() && Is64Bits(); } + bool IsS() const { return IsValidFPRegister() && Is32Bits(); } + bool IsD() const { return IsValidFPRegister() && Is64Bits(); } + + const Register& W() const; + const Register& X() const; + const FPRegister& S() const; + const FPRegister& D() const; + + bool IsSameSizeAndType(const CPURegister& other) const { + return (size_ == other.size_) && (type_ == other.type_); + } + + protected: + unsigned code_; + unsigned size_; + RegisterType type_; + + private: + bool IsValidOrNone() const { + return IsValid() || IsNone(); + } +}; + + +class Register : public CPURegister { + public: + Register() : CPURegister() {} + explicit Register(const CPURegister& other) + : CPURegister(other.code(), other.size(), other.type()) { + VIXL_ASSERT(IsValidRegister()); + } + Register(unsigned code, unsigned size) + : CPURegister(code, size, kRegister) {} + + bool IsValid() const { + VIXL_ASSERT(IsRegister() || IsNone()); + return IsValidRegister(); + } + + static const Register& WRegFromCode(unsigned code); + static const Register& XRegFromCode(unsigned code); + + private: + static const Register wregisters[]; + static const Register xregisters[]; +}; + + +class FPRegister : public CPURegister { + public: + FPRegister() : CPURegister() {} + explicit FPRegister(const CPURegister& other) + : CPURegister(other.code(), other.size(), other.type()) { + VIXL_ASSERT(IsValidFPRegister()); + } + FPRegister(unsigned code, unsigned size) + : CPURegister(code, size, kFPRegister) {} + + bool IsValid() const { + VIXL_ASSERT(IsFPRegister() || IsNone()); + return IsValidFPRegister(); + } + + static const FPRegister& SRegFromCode(unsigned code); + static const FPRegister& DRegFromCode(unsigned code); + + private: + static const FPRegister sregisters[]; + static const FPRegister dregisters[]; +}; + + +// No*Reg is used to indicate an unused argument, or an error case. Note that +// these all compare equal (using the Is() method). The Register and FPRegister +// variants are provided for convenience. +const Register NoReg; +const FPRegister NoFPReg; +const CPURegister NoCPUReg; + + +#define DEFINE_REGISTERS(N) \ +const Register w##N(N, kWRegSize); \ +const Register x##N(N, kXRegSize); +REGISTER_CODE_LIST(DEFINE_REGISTERS) +#undef DEFINE_REGISTERS +const Register wsp(kSPRegInternalCode, kWRegSize); +const Register sp(kSPRegInternalCode, kXRegSize); + + +#define DEFINE_FPREGISTERS(N) \ +const FPRegister s##N(N, kSRegSize); \ +const FPRegister d##N(N, kDRegSize); +REGISTER_CODE_LIST(DEFINE_FPREGISTERS) +#undef DEFINE_FPREGISTERS + + +// Registers aliases. +const Register ip0 = x16; +const Register ip1 = x17; +const Register lr = x30; +const Register xzr = x31; +const Register wzr = w31; + + +// AreAliased returns true if any of the named registers overlap. Arguments +// set to NoReg are ignored. The system stack pointer may be specified. +bool AreAliased(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoReg, + const CPURegister& reg4 = NoReg, + const CPURegister& reg5 = NoReg, + const CPURegister& reg6 = NoReg, + const CPURegister& reg7 = NoReg, + const CPURegister& reg8 = NoReg); + + +// AreSameSizeAndType returns true if all of the specified registers have the +// same size, and are of the same type. The system stack pointer may be +// specified. Arguments set to NoReg are ignored, as are any subsequent +// arguments. At least one argument (reg1) must be valid (not NoCPUReg). +bool AreSameSizeAndType(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoCPUReg, + const CPURegister& reg4 = NoCPUReg, + const CPURegister& reg5 = NoCPUReg, + const CPURegister& reg6 = NoCPUReg, + const CPURegister& reg7 = NoCPUReg, + const CPURegister& reg8 = NoCPUReg); + + +// Lists of registers. +class CPURegList { + public: + explicit CPURegList(CPURegister reg1, + CPURegister reg2 = NoCPUReg, + CPURegister reg3 = NoCPUReg, + CPURegister reg4 = NoCPUReg) + : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), + size_(reg1.size()), type_(reg1.type()) { + VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); + VIXL_ASSERT(IsValid()); + } + + CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) + : list_(list), size_(size), type_(type) { + VIXL_ASSERT(IsValid()); + } + + CPURegList(CPURegister::RegisterType type, unsigned size, + unsigned first_reg, unsigned last_reg) + : size_(size), type_(type) { + VIXL_ASSERT(((type == CPURegister::kRegister) && + (last_reg < kNumberOfRegisters)) || + ((type == CPURegister::kFPRegister) && + (last_reg < kNumberOfFPRegisters))); + VIXL_ASSERT(last_reg >= first_reg); + list_ = (UINT64_C(1) << (last_reg + 1)) - 1; + list_ &= ~((UINT64_C(1) << first_reg) - 1); + VIXL_ASSERT(IsValid()); + } + + CPURegister::RegisterType type() const { + VIXL_ASSERT(IsValid()); + return type_; + } + + // Combine another CPURegList into this one. Registers that already exist in + // this list are left unchanged. The type and size of the registers in the + // 'other' list must match those in this list. + void Combine(const CPURegList& other) { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(other.type() == type_); + VIXL_ASSERT(other.RegisterSizeInBits() == size_); + list_ |= other.list(); + } + + // Remove every register in the other CPURegList from this one. Registers that + // do not exist in this list are ignored. The type and size of the registers + // in the 'other' list must match those in this list. + void Remove(const CPURegList& other) { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(other.type() == type_); + VIXL_ASSERT(other.RegisterSizeInBits() == size_); + list_ &= ~other.list(); + } + + // Variants of Combine and Remove which take a single register. + void Combine(const CPURegister& other) { + VIXL_ASSERT(other.type() == type_); + VIXL_ASSERT(other.size() == size_); + Combine(other.code()); + } + + void Remove(const CPURegister& other) { + VIXL_ASSERT(other.type() == type_); + VIXL_ASSERT(other.size() == size_); + Remove(other.code()); + } + + // Variants of Combine and Remove which take a single register by its code; + // the type and size of the register is inferred from this list. + void Combine(int code) { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); + list_ |= (UINT64_C(1) << code); + } + + void Remove(int code) { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); + list_ &= ~(UINT64_C(1) << code); + } + + static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2) { + VIXL_ASSERT(list_1.type_ == list_2.type_); + VIXL_ASSERT(list_1.size_ == list_2.size_); + return CPURegList(list_1.type_, list_1.size_, list_1.list_ | list_2.list_); + } + static CPURegList Union(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3); + static CPURegList Union(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3, + const CPURegList& list_4); + + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2) { + VIXL_ASSERT(list_1.type_ == list_2.type_); + VIXL_ASSERT(list_1.size_ == list_2.size_); + return CPURegList(list_1.type_, list_1.size_, list_1.list_ & list_2.list_); + } + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3); + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3, + const CPURegList& list_4); + + RegList list() const { + VIXL_ASSERT(IsValid()); + return list_; + } + + void set_list(RegList new_list) { + VIXL_ASSERT(IsValid()); + list_ = new_list; + } + + // Remove all callee-saved registers from the list. This can be useful when + // preparing registers for an AAPCS64 function call, for example. + void RemoveCalleeSaved(); + + CPURegister PopLowestIndex(); + CPURegister PopHighestIndex(); + + // AAPCS64 callee-saved registers. + static CPURegList GetCalleeSaved(unsigned size = kXRegSize); + static CPURegList GetCalleeSavedFP(unsigned size = kDRegSize); + + // AAPCS64 caller-saved registers. Note that this includes lr. + static CPURegList GetCallerSaved(unsigned size = kXRegSize); + static CPURegList GetCallerSavedFP(unsigned size = kDRegSize); + + bool IsEmpty() const { + VIXL_ASSERT(IsValid()); + return list_ == 0; + } + + bool IncludesAliasOf(const CPURegister& other) const { + VIXL_ASSERT(IsValid()); + return (type_ == other.type()) && ((other.Bit() & list_) != 0); + } + + bool IncludesAliasOf(int code) const { + VIXL_ASSERT(IsValid()); + return ((code & list_) != 0); + } + + int Count() const { + VIXL_ASSERT(IsValid()); + return CountSetBits(list_, kRegListSizeInBits); + } + + unsigned RegisterSizeInBits() const { + VIXL_ASSERT(IsValid()); + return size_; + } + + unsigned RegisterSizeInBytes() const { + int size_in_bits = RegisterSizeInBits(); + VIXL_ASSERT((size_in_bits % 8) == 0); + return size_in_bits / 8; + } + + unsigned TotalSizeInBytes() const { + VIXL_ASSERT(IsValid()); + return RegisterSizeInBytes() * Count(); + } + + private: + RegList list_; + unsigned size_; + CPURegister::RegisterType type_; + + bool IsValid() const; +}; + + +// AAPCS64 callee-saved registers. +extern const CPURegList kCalleeSaved; +extern const CPURegList kCalleeSavedFP; + + +// AAPCS64 caller-saved registers. Note that this includes lr. +extern const CPURegList kCallerSaved; +extern const CPURegList kCallerSavedFP; + + +// Operand. +class Operand { + public: + // # + // where is int64_t. + // This is allowed to be an implicit constructor because Operand is + // a wrapper class that doesn't normally perform any type conversion. + Operand(int64_t immediate); // NOLINT(runtime/explicit) + + // rm, { #} + // where is one of {LSL, LSR, ASR, ROR}. + // is uint6_t. + // This is allowed to be an implicit constructor because Operand is + // a wrapper class that doesn't normally perform any type conversion. + Operand(Register reg, + Shift shift = LSL, + unsigned shift_amount = 0); // NOLINT(runtime/explicit) + + // rm, { {#}} + // where is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. + // is uint2_t. + explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0); + + bool IsImmediate() const; + bool IsShiftedRegister() const; + bool IsExtendedRegister() const; + bool IsZero() const; + + // This returns an LSL shift (<= 4) operand as an equivalent extend operand, + // which helps in the encoding of instructions that use the stack pointer. + Operand ToExtendedRegister() const; + + int64_t immediate() const { + VIXL_ASSERT(IsImmediate()); + return immediate_; + } + + Register reg() const { + VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); + return reg_; + } + + Shift shift() const { + VIXL_ASSERT(IsShiftedRegister()); + return shift_; + } + + Extend extend() const { + VIXL_ASSERT(IsExtendedRegister()); + return extend_; + } + + unsigned shift_amount() const { + VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); + return shift_amount_; + } + + private: + int64_t immediate_; + Register reg_; + Shift shift_; + Extend extend_; + unsigned shift_amount_; +}; + + +// MemOperand represents the addressing mode of a load or store instruction. +class MemOperand { + public: + explicit MemOperand(Register base, + int64_t offset = 0, + AddrMode addrmode = Offset); + explicit MemOperand(Register base, + Register regoffset, + Shift shift = LSL, + unsigned shift_amount = 0); + explicit MemOperand(Register base, + Register regoffset, + Extend extend, + unsigned shift_amount = 0); + explicit MemOperand(Register base, + const Operand& offset, + AddrMode addrmode = Offset); + + const Register& base() const { return base_; } + const Register& regoffset() const { return regoffset_; } + int64_t offset() const { return offset_; } + AddrMode addrmode() const { return addrmode_; } + Shift shift() const { return shift_; } + Extend extend() const { return extend_; } + unsigned shift_amount() const { return shift_amount_; } + bool IsImmediateOffset() const; + bool IsRegisterOffset() const; + bool IsPreIndex() const; + bool IsPostIndex() const; + + private: + Register base_; + Register regoffset_; + int64_t offset_; + AddrMode addrmode_; + Shift shift_; + Extend extend_; + unsigned shift_amount_; +}; + + +class Label { + public: + Label() : location_(kLocationUnbound) {} + ~Label() { + // If the label has been linked to, it needs to be bound to a target. + VIXL_ASSERT(!IsLinked() || IsBound()); + } + + bool IsBound() const { return location_ >= 0; } + bool IsLinked() const { return !links_.empty(); } + + ptrdiff_t location() const { return location_; } + + private: + // The list of linked instructions is stored in a stack-like structure. We + // don't use std::stack directly because it's slow for the common case where + // only one or two instructions refer to a label, and labels themselves are + // short-lived. This class behaves like std::stack, but the first few links + // are preallocated (configured by kPreallocatedLinks). + // + // If more than N links are required, this falls back to std::stack. + class LinksStack { + public: + LinksStack() : size_(0), links_extended_(NULL) {} + ~LinksStack() { + delete links_extended_; + } + + size_t size() const { + return size_; + } + + bool empty() const { + return size_ == 0; + } + + void push(ptrdiff_t value) { + if (size_ < kPreallocatedLinks) { + links_[size_] = value; + } else { + if (links_extended_ == NULL) { + links_extended_ = new std::stack(); + } + VIXL_ASSERT(size_ == (links_extended_->size() + kPreallocatedLinks)); + links_extended_->push(value); + } + size_++; + } + + ptrdiff_t top() const { + return (size_ <= kPreallocatedLinks) ? links_[size_ - 1] + : links_extended_->top(); + } + + void pop() { + size_--; + if (size_ >= kPreallocatedLinks) { + links_extended_->pop(); + VIXL_ASSERT(size_ == (links_extended_->size() + kPreallocatedLinks)); + } + } + + private: + static const size_t kPreallocatedLinks = 4; + + size_t size_; + ptrdiff_t links_[kPreallocatedLinks]; + std::stack * links_extended_; + }; + + void Bind(ptrdiff_t location) { + // Labels can only be bound once. + VIXL_ASSERT(!IsBound()); + location_ = location; + } + + void AddLink(ptrdiff_t instruction) { + // If a label is bound, the assembler already has the information it needs + // to write the instruction, so there is no need to add it to links_. + VIXL_ASSERT(!IsBound()); + links_.push(instruction); + } + + ptrdiff_t GetAndRemoveNextLink() { + VIXL_ASSERT(IsLinked()); + ptrdiff_t link = links_.top(); + links_.pop(); + return link; + } + + // The offsets of the instructions that have linked to this label. + LinksStack links_; + // The label location. + ptrdiff_t location_; + + static const ptrdiff_t kLocationUnbound = -1; + + // It is not safe to copy labels, so disable the copy constructor by declaring + // it private (without an implementation). + Label(const Label&); + + // The Assembler class is responsible for binding and linking labels, since + // the stored offsets need to be consistent with the Assembler's buffer. + friend class Assembler; +}; + + +// A literal is a 32-bit or 64-bit piece of data stored in the instruction +// stream and loaded through a pc relative load. The same literal can be +// referred to by multiple instructions but a literal can only reside at one +// place in memory. A literal can be used by a load before or after being +// placed in memory. +// +// Internally an offset of 0 is associated with a literal which has been +// neither used nor placed. Then two possibilities arise: +// 1) the label is placed, the offset (stored as offset + 1) is used to +// resolve any subsequent load using the label. +// 2) the label is not placed and offset is the offset of the last load using +// the literal (stored as -offset -1). If multiple loads refer to this +// literal then the last load holds the offset of the preceding load and +// all loads form a chain. Once the offset is placed all the loads in the +// chain are resolved and future loads fall back to possibility 1. +class RawLiteral { + public: + RawLiteral() : size_(0), offset_(0), raw_value_(0) {} + + size_t size() { + VIXL_STATIC_ASSERT(kDRegSizeInBytes == kXRegSizeInBytes); + VIXL_STATIC_ASSERT(kSRegSizeInBytes == kWRegSizeInBytes); + VIXL_ASSERT((size_ == kXRegSizeInBytes) || (size_ == kWRegSizeInBytes)); + return size_; + } + uint64_t raw_value64() { + VIXL_ASSERT(size_ == kXRegSizeInBytes); + return raw_value_; + } + uint32_t raw_value32() { + VIXL_ASSERT(size_ == kWRegSizeInBytes); + VIXL_ASSERT(is_uint32(raw_value_) || is_int32(raw_value_)); + return static_cast(raw_value_); + } + bool IsUsed() { return offset_ < 0; } + bool IsPlaced() { return offset_ > 0; } + + protected: + ptrdiff_t offset() { + VIXL_ASSERT(IsPlaced()); + return offset_ - 1; + } + void set_offset(ptrdiff_t offset) { + VIXL_ASSERT(offset >= 0); + VIXL_ASSERT(IsWordAligned(offset)); + VIXL_ASSERT(!IsPlaced()); + offset_ = offset + 1; + } + ptrdiff_t last_use() { + VIXL_ASSERT(IsUsed()); + return -offset_ - 1; + } + void set_last_use(ptrdiff_t offset) { + VIXL_ASSERT(offset >= 0); + VIXL_ASSERT(IsWordAligned(offset)); + VIXL_ASSERT(!IsPlaced()); + offset_ = -offset - 1; + } + + size_t size_; + ptrdiff_t offset_; + uint64_t raw_value_; + + friend class Assembler; +}; + + +template +class Literal : public RawLiteral { + public: + explicit Literal(T value) { + size_ = sizeof(value); + memcpy(&raw_value_, &value, sizeof(value)); + } +}; + + +// Control whether or not position-independent code should be emitted. +enum PositionIndependentCodeOption { + // All code generated will be position-independent; all branches and + // references to labels generated with the Label class will use PC-relative + // addressing. + PositionIndependentCode, + + // Allow VIXL to generate code that refers to absolute addresses. With this + // option, it will not be possible to copy the code buffer and run it from a + // different address; code must be generated in its final location. + PositionDependentCode, + + // Allow VIXL to assume that the bottom 12 bits of the address will be + // constant, but that the top 48 bits may change. This allows `adrp` to + // function in systems which copy code between pages, but otherwise maintain + // 4KB page alignment. + PageOffsetDependentCode +}; + + +// Control how scaled- and unscaled-offset loads and stores are generated. +enum LoadStoreScalingOption { + // Prefer scaled-immediate-offset instructions, but emit unscaled-offset, + // register-offset, pre-index or post-index instructions if necessary. + PreferScaledOffset, + + // Prefer unscaled-immediate-offset instructions, but emit scaled-offset, + // register-offset, pre-index or post-index instructions if necessary. + PreferUnscaledOffset, + + // Require scaled-immediate-offset instructions. + RequireScaledOffset, + + // Require unscaled-immediate-offset instructions. + RequireUnscaledOffset +}; + + +// Assembler. +class Assembler { + public: + Assembler(size_t capacity, + PositionIndependentCodeOption pic = PositionIndependentCode); + Assembler(byte* buffer, size_t capacity, + PositionIndependentCodeOption pic = PositionIndependentCode); + + // The destructor asserts that one of the following is true: + // * The Assembler object has not been used. + // * Nothing has been emitted since the last Reset() call. + // * Nothing has been emitted since the last FinalizeCode() call. + ~Assembler(); + + // System functions. + + // Start generating code from the beginning of the buffer, discarding any code + // and data that has already been emitted into the buffer. + void Reset(); + + // Finalize a code buffer of generated instructions. This function must be + // called before executing or copying code from the buffer. + void FinalizeCode(); + + // Label. + // Bind a label to the current PC. + void bind(Label* label); + + // Bind a label to a specified offset from the start of the buffer. + void BindToOffset(Label* label, ptrdiff_t offset); + + // Place a literal at the current PC. + void place(RawLiteral* literal); + + ptrdiff_t CursorOffset() const { + return buffer_->CursorOffset(); + } + + ptrdiff_t BufferEndOffset() const { + return static_cast(buffer_->capacity()); + } + + // Return the address of an offset in the buffer. + template + T GetOffsetAddress(ptrdiff_t offset) { + VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); + return buffer_->GetOffsetAddress(offset); + } + + // Return the address of a bound label. + template + T GetLabelAddress(const Label * label) { + VIXL_ASSERT(label->IsBound()); + VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); + return GetOffsetAddress(label->location()); + } + + // Return the address of the cursor. + template + T GetCursorAddress() { + VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); + return GetOffsetAddress(CursorOffset()); + } + + // Return the address of the start of the buffer. + template + T GetStartAddress() { + VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); + return GetOffsetAddress(0); + } + + // Instruction set functions. + + // Branch / Jump instructions. + // Branch to register. + void br(const Register& xn); + + // Branch with link to register. + void blr(const Register& xn); + + // Branch to register with return hint. + void ret(const Register& xn = lr); + + // Unconditional branch to label. + void b(Label* label); + + // Conditional branch to label. + void b(Label* label, Condition cond); + + // Unconditional branch to PC offset. + void b(int imm26); + + // Conditional branch to PC offset. + void b(int imm19, Condition cond); + + // Branch with link to label. + void bl(Label* label); + + // Branch with link to PC offset. + void bl(int imm26); + + // Compare and branch to label if zero. + void cbz(const Register& rt, Label* label); + + // Compare and branch to PC offset if zero. + void cbz(const Register& rt, int imm19); + + // Compare and branch to label if not zero. + void cbnz(const Register& rt, Label* label); + + // Compare and branch to PC offset if not zero. + void cbnz(const Register& rt, int imm19); + + // Test bit and branch to label if zero. + void tbz(const Register& rt, unsigned bit_pos, Label* label); + + // Test bit and branch to PC offset if zero. + void tbz(const Register& rt, unsigned bit_pos, int imm14); + + // Test bit and branch to label if not zero. + void tbnz(const Register& rt, unsigned bit_pos, Label* label); + + // Test bit and branch to PC offset if not zero. + void tbnz(const Register& rt, unsigned bit_pos, int imm14); + + // Address calculation instructions. + // Calculate a PC-relative address. Unlike for branches the offset in adr is + // unscaled (i.e. the result can be unaligned). + + // Calculate the address of a label. + void adr(const Register& rd, Label* label); + + // Calculate the address of a PC offset. + void adr(const Register& rd, int imm21); + + // Calculate the page address of a label. + void adrp(const Register& rd, Label* label); + + // Calculate the page address of a PC offset. + void adrp(const Register& rd, int imm21); + + // Data Processing instructions. + // Add. + void add(const Register& rd, + const Register& rn, + const Operand& operand); + + // Add and update status flags. + void adds(const Register& rd, + const Register& rn, + const Operand& operand); + + // Compare negative. + void cmn(const Register& rn, const Operand& operand); + + // Subtract. + void sub(const Register& rd, + const Register& rn, + const Operand& operand); + + // Subtract and update status flags. + void subs(const Register& rd, + const Register& rn, + const Operand& operand); + + // Compare. + void cmp(const Register& rn, const Operand& operand); + + // Negate. + void neg(const Register& rd, + const Operand& operand); + + // Negate and update status flags. + void negs(const Register& rd, + const Operand& operand); + + // Add with carry bit. + void adc(const Register& rd, + const Register& rn, + const Operand& operand); + + // Add with carry bit and update status flags. + void adcs(const Register& rd, + const Register& rn, + const Operand& operand); + + // Subtract with carry bit. + void sbc(const Register& rd, + const Register& rn, + const Operand& operand); + + // Subtract with carry bit and update status flags. + void sbcs(const Register& rd, + const Register& rn, + const Operand& operand); + + // Negate with carry bit. + void ngc(const Register& rd, + const Operand& operand); + + // Negate with carry bit and update status flags. + void ngcs(const Register& rd, + const Operand& operand); + + // Logical instructions. + // Bitwise and (A & B). + void and_(const Register& rd, + const Register& rn, + const Operand& operand); + + // Bitwise and (A & B) and update status flags. + void ands(const Register& rd, + const Register& rn, + const Operand& operand); + + // Bit test and set flags. + void tst(const Register& rn, const Operand& operand); + + // Bit clear (A & ~B). + void bic(const Register& rd, + const Register& rn, + const Operand& operand); + + // Bit clear (A & ~B) and update status flags. + void bics(const Register& rd, + const Register& rn, + const Operand& operand); + + // Bitwise or (A | B). + void orr(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise nor (A | ~B). + void orn(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise eor/xor (A ^ B). + void eor(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise enor/xnor (A ^ ~B). + void eon(const Register& rd, const Register& rn, const Operand& operand); + + // Logical shift left by variable. + void lslv(const Register& rd, const Register& rn, const Register& rm); + + // Logical shift right by variable. + void lsrv(const Register& rd, const Register& rn, const Register& rm); + + // Arithmetic shift right by variable. + void asrv(const Register& rd, const Register& rn, const Register& rm); + + // Rotate right by variable. + void rorv(const Register& rd, const Register& rn, const Register& rm); + + // Bitfield instructions. + // Bitfield move. + void bfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Signed bitfield move. + void sbfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Unsigned bitfield move. + void ubfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Bfm aliases. + // Bitfield insert. + void bfi(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + bfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Bitfield extract and insert low. + void bfxil(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + bfm(rd, rn, lsb, lsb + width - 1); + } + + // Sbfm aliases. + // Arithmetic shift right. + void asr(const Register& rd, const Register& rn, unsigned shift) { + VIXL_ASSERT(shift < rd.size()); + sbfm(rd, rn, shift, rd.size() - 1); + } + + // Signed bitfield insert with zero at right. + void sbfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + sbfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Signed bitfield extract. + void sbfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + sbfm(rd, rn, lsb, lsb + width - 1); + } + + // Signed extend byte. + void sxtb(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 7); + } + + // Signed extend halfword. + void sxth(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 15); + } + + // Signed extend word. + void sxtw(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 31); + } + + // Ubfm aliases. + // Logical shift left. + void lsl(const Register& rd, const Register& rn, unsigned shift) { + unsigned reg_size = rd.size(); + VIXL_ASSERT(shift < reg_size); + ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); + } + + // Logical shift right. + void lsr(const Register& rd, const Register& rn, unsigned shift) { + VIXL_ASSERT(shift < rd.size()); + ubfm(rd, rn, shift, rd.size() - 1); + } + + // Unsigned bitfield insert with zero at right. + void ubfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + ubfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Unsigned bitfield extract. + void ubfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + ubfm(rd, rn, lsb, lsb + width - 1); + } + + // Unsigned extend byte. + void uxtb(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 7); + } + + // Unsigned extend halfword. + void uxth(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 15); + } + + // Unsigned extend word. + void uxtw(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 31); + } + + // Extract. + void extr(const Register& rd, + const Register& rn, + const Register& rm, + unsigned lsb); + + // Conditional select: rd = cond ? rn : rm. + void csel(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select increment: rd = cond ? rn : rm + 1. + void csinc(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select inversion: rd = cond ? rn : ~rm. + void csinv(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select negation: rd = cond ? rn : -rm. + void csneg(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional set: rd = cond ? 1 : 0. + void cset(const Register& rd, Condition cond); + + // Conditional set mask: rd = cond ? -1 : 0. + void csetm(const Register& rd, Condition cond); + + // Conditional increment: rd = cond ? rn + 1 : rn. + void cinc(const Register& rd, const Register& rn, Condition cond); + + // Conditional invert: rd = cond ? ~rn : rn. + void cinv(const Register& rd, const Register& rn, Condition cond); + + // Conditional negate: rd = cond ? -rn : rn. + void cneg(const Register& rd, const Register& rn, Condition cond); + + // Rotate right. + void ror(const Register& rd, const Register& rs, unsigned shift) { + extr(rd, rs, rs, shift); + } + + // Conditional comparison. + // Conditional compare negative. + void ccmn(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + + // Conditional compare. + void ccmp(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + + // Multiply. + void mul(const Register& rd, const Register& rn, const Register& rm); + + // Negated multiply. + void mneg(const Register& rd, const Register& rn, const Register& rm); + + // Signed long multiply: 32 x 32 -> 64-bit. + void smull(const Register& rd, const Register& rn, const Register& rm); + + // Signed multiply high: 64 x 64 -> 64-bit <127:64>. + void smulh(const Register& xd, const Register& xn, const Register& xm); + + // Multiply and accumulate. + void madd(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Multiply and subtract. + void msub(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + void smaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + void umaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed long multiply and subtract: 64 - (32 x 32) -> 64-bit. + void smsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply and subtract: 64 - (32 x 32) -> 64-bit. + void umsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed integer divide. + void sdiv(const Register& rd, const Register& rn, const Register& rm); + + // Unsigned integer divide. + void udiv(const Register& rd, const Register& rn, const Register& rm); + + // Bit reverse. + void rbit(const Register& rd, const Register& rn); + + // Reverse bytes in 16-bit half words. + void rev16(const Register& rd, const Register& rn); + + // Reverse bytes in 32-bit words. + void rev32(const Register& rd, const Register& rn); + + // Reverse bytes. + void rev(const Register& rd, const Register& rn); + + // Count leading zeroes. + void clz(const Register& rd, const Register& rn); + + // Count leading sign bits. + void cls(const Register& rd, const Register& rn); + + // Memory instructions. + // Load integer or FP register. + void ldr(const CPURegister& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Store integer or FP register. + void str(const CPURegister& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load word with sign extension. + void ldrsw(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load byte. + void ldrb(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Store byte. + void strb(const Register& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load byte with sign extension. + void ldrsb(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load half-word. + void ldrh(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Store half-word. + void strh(const Register& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load half-word with sign extension. + void ldrsh(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load integer or FP register (with unscaled offset). + void ldur(const CPURegister& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Store integer or FP register (with unscaled offset). + void stur(const CPURegister& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load word with sign extension. + void ldursw(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load byte (with unscaled offset). + void ldurb(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Store byte (with unscaled offset). + void sturb(const Register& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load byte with sign extension (and unscaled offset). + void ldursb(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load half-word (with unscaled offset). + void ldurh(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Store half-word (with unscaled offset). + void sturh(const Register& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load half-word with sign extension (and unscaled offset). + void ldursh(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load integer or FP register pair. + void ldp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src); + + // Store integer or FP register pair. + void stp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst); + + // Load word pair with sign extension. + void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src); + + // Load integer or FP register pair, non-temporal. + void ldnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src); + + // Store integer or FP register pair, non-temporal. + void stnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst); + + // Load integer or FP register from literal pool. + void ldr(const CPURegister& rt, RawLiteral* literal); + + // Load word with sign extension from literal pool. + void ldrsw(const Register& rt, RawLiteral* literal); + + // Load integer or FP register from pc + imm19 << 2. + void ldr(const CPURegister& rt, int imm19); + + // Load word with sign extension from pc + imm19 << 2. + void ldrsw(const Register& rt, int imm19); + + // Store exclusive byte. + void stxrb(const Register& rs, const Register& rt, const MemOperand& dst); + + // Store exclusive half-word. + void stxrh(const Register& rs, const Register& rt, const MemOperand& dst); + + // Store exclusive register. + void stxr(const Register& rs, const Register& rt, const MemOperand& dst); + + // Load exclusive byte. + void ldxrb(const Register& rt, const MemOperand& src); + + // Load exclusive half-word. + void ldxrh(const Register& rt, const MemOperand& src); + + // Load exclusive register. + void ldxr(const Register& rt, const MemOperand& src); + + // Store exclusive register pair. + void stxp(const Register& rs, + const Register& rt, + const Register& rt2, + const MemOperand& dst); + + // Load exclusive register pair. + void ldxp(const Register& rt, const Register& rt2, const MemOperand& src); + + // Store-release exclusive byte. + void stlxrb(const Register& rs, const Register& rt, const MemOperand& dst); + + // Store-release exclusive half-word. + void stlxrh(const Register& rs, const Register& rt, const MemOperand& dst); + + // Store-release exclusive register. + void stlxr(const Register& rs, const Register& rt, const MemOperand& dst); + + // Load-acquire exclusive byte. + void ldaxrb(const Register& rt, const MemOperand& src); + + // Load-acquire exclusive half-word. + void ldaxrh(const Register& rt, const MemOperand& src); + + // Load-acquire exclusive register. + void ldaxr(const Register& rt, const MemOperand& src); + + // Store-release exclusive register pair. + void stlxp(const Register& rs, + const Register& rt, + const Register& rt2, + const MemOperand& dst); + + // Load-acquire exclusive register pair. + void ldaxp(const Register& rt, const Register& rt2, const MemOperand& src); + + // Store-release byte. + void stlrb(const Register& rt, const MemOperand& dst); + + // Store-release half-word. + void stlrh(const Register& rt, const MemOperand& dst); + + // Store-release register. + void stlr(const Register& rt, const MemOperand& dst); + + // Load-acquire byte. + void ldarb(const Register& rt, const MemOperand& src); + + // Load-acquire half-word. + void ldarh(const Register& rt, const MemOperand& src); + + // Load-acquire register. + void ldar(const Register& rt, const MemOperand& src); + + // Prefetch memory. + void prfm(PrefetchOperation op, const MemOperand& addr, + LoadStoreScalingOption option = PreferScaledOffset); + + // Prefetch memory (with unscaled offset). + void prfum(PrefetchOperation op, const MemOperand& addr, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Prefetch memory in the literal pool. + void prfm(PrefetchOperation op, RawLiteral* literal); + + // Prefetch from pc + imm19 << 2. + void prfm(PrefetchOperation op, int imm19); + + // Move instructions. The default shift of -1 indicates that the move + // instruction will calculate an appropriate 16-bit immediate and left shift + // that is equal to the 64-bit immediate argument. If an explicit left shift + // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value. + // + // For movk, an explicit shift can be used to indicate which half word should + // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant + // half word with zero, whereas movk(x0, 0, 48) will overwrite the + // most-significant. + + // Move immediate and keep. + void movk(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVK); + } + + // Move inverted immediate. + void movn(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVN); + } + + // Move immediate. + void movz(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVZ); + } + + // Misc instructions. + // Monitor debug-mode breakpoint. + void brk(int code); + + // Halting debug-mode breakpoint. + void hlt(int code); + + // Move register to register. + void mov(const Register& rd, const Register& rn); + + // Move inverted operand to register. + void mvn(const Register& rd, const Operand& operand); + + // System instructions. + // Move to register from system register. + void mrs(const Register& rt, SystemRegister sysreg); + + // Move from register to system register. + void msr(SystemRegister sysreg, const Register& rt); + + // System hint. + void hint(SystemHint code); + + // Clear exclusive monitor. + void clrex(int imm4 = 0xf); + + // Data memory barrier. + void dmb(BarrierDomain domain, BarrierType type); + + // Data synchronization barrier. + void dsb(BarrierDomain domain, BarrierType type); + + // Instruction synchronization barrier. + void isb(); + + // Alias for system instructions. + // No-op. + void nop() { + hint(NOP); + } + + // FP instructions. + // Move double precision immediate to FP register. + void fmov(const FPRegister& fd, double imm); + + // Move single precision immediate to FP register. + void fmov(const FPRegister& fd, float imm); + + // Move FP register to register. + void fmov(const Register& rd, const FPRegister& fn); + + // Move register to FP register. + void fmov(const FPRegister& fd, const Register& rn); + + // Move FP register to FP register. + void fmov(const FPRegister& fd, const FPRegister& fn); + + // FP add. + void fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP subtract. + void fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP multiply. + void fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP fused multiply and add. + void fmadd(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa); + + // FP fused multiply and subtract. + void fmsub(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa); + + // FP fused multiply, add and negate. + void fnmadd(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa); + + // FP fused multiply, subtract and negate. + void fnmsub(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa); + + // FP divide. + void fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP maximum. + void fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP minimum. + void fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP maximum number. + void fmaxnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP minimum number. + void fminnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP absolute. + void fabs(const FPRegister& fd, const FPRegister& fn); + + // FP negate. + void fneg(const FPRegister& fd, const FPRegister& fn); + + // FP square root. + void fsqrt(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (nearest with ties to away). + void frinta(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (implicit rounding). + void frinti(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (toward minus infinity). + void frintm(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (nearest with ties to even). + void frintn(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (toward plus infinity). + void frintp(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (exact, implicit rounding). + void frintx(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (towards zero). + void frintz(const FPRegister& fd, const FPRegister& fn); + + // FP compare registers. + void fcmp(const FPRegister& fn, const FPRegister& fm); + + // FP compare immediate. + void fcmp(const FPRegister& fn, double value); + + // FP conditional compare. + void fccmp(const FPRegister& fn, + const FPRegister& fm, + StatusFlags nzcv, + Condition cond); + + // FP conditional select. + void fcsel(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + Condition cond); + + // Common FP Convert function. + void FPConvertToInt(const Register& rd, + const FPRegister& fn, + FPIntegerConvertOp op); + + // FP convert between single and double precision. + void fcvt(const FPRegister& fd, const FPRegister& fn); + + // Convert FP to signed integer (nearest with ties to away). + void fcvtas(const Register& rd, const FPRegister& fn); + + // Convert FP to unsigned integer (nearest with ties to away). + void fcvtau(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (round towards -infinity). + void fcvtms(const Register& rd, const FPRegister& fn); + + // Convert FP to unsigned integer (round towards -infinity). + void fcvtmu(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (nearest with ties to even). + void fcvtns(const Register& rd, const FPRegister& fn); + + // Convert FP to unsigned integer (nearest with ties to even). + void fcvtnu(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (round towards zero). + void fcvtzs(const Register& rd, const FPRegister& fn); + + // Convert FP to unsigned integer (round towards zero). + void fcvtzu(const Register& rd, const FPRegister& fn); + + // Convert signed integer or fixed point to FP. + void scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); + + // Convert unsigned integer or fixed point to FP. + void ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); + + // Emit generic instructions. + // Emit raw instructions into the instruction stream. + void dci(Instr raw_inst) { Emit(raw_inst); } + + // Emit 32 bits of data into the instruction stream. + void dc32(uint32_t data) { + VIXL_ASSERT(buffer_monitor_ > 0); + buffer_->Emit32(data); + } + + // Emit 64 bits of data into the instruction stream. + void dc64(uint64_t data) { + VIXL_ASSERT(buffer_monitor_ > 0); + buffer_->Emit64(data); + } + + // Copy a string into the instruction stream, including the terminating NULL + // character. The instruction pointer is then aligned correctly for + // subsequent instructions. + void EmitString(const char * string) { + VIXL_ASSERT(string != NULL); + VIXL_ASSERT(buffer_monitor_ > 0); + + buffer_->EmitString(string); + buffer_->Align(); + } + + // Code generation helpers. + + // Register encoding. + static Instr Rd(CPURegister rd) { + VIXL_ASSERT(rd.code() != kSPRegInternalCode); + return rd.code() << Rd_offset; + } + + static Instr Rn(CPURegister rn) { + VIXL_ASSERT(rn.code() != kSPRegInternalCode); + return rn.code() << Rn_offset; + } + + static Instr Rm(CPURegister rm) { + VIXL_ASSERT(rm.code() != kSPRegInternalCode); + return rm.code() << Rm_offset; + } + + static Instr Ra(CPURegister ra) { + VIXL_ASSERT(ra.code() != kSPRegInternalCode); + return ra.code() << Ra_offset; + } + + static Instr Rt(CPURegister rt) { + VIXL_ASSERT(rt.code() != kSPRegInternalCode); + return rt.code() << Rt_offset; + } + + static Instr Rt2(CPURegister rt2) { + VIXL_ASSERT(rt2.code() != kSPRegInternalCode); + return rt2.code() << Rt2_offset; + } + + static Instr Rs(CPURegister rs) { + VIXL_ASSERT(rs.code() != kSPRegInternalCode); + return rs.code() << Rs_offset; + } + + // These encoding functions allow the stack pointer to be encoded, and + // disallow the zero register. + static Instr RdSP(Register rd) { + VIXL_ASSERT(!rd.IsZero()); + return (rd.code() & kRegCodeMask) << Rd_offset; + } + + static Instr RnSP(Register rn) { + VIXL_ASSERT(!rn.IsZero()); + return (rn.code() & kRegCodeMask) << Rn_offset; + } + + // Flags encoding. + static Instr Flags(FlagsUpdate S) { + if (S == SetFlags) { + return 1 << FlagsUpdate_offset; + } else if (S == LeaveFlags) { + return 0 << FlagsUpdate_offset; + } + VIXL_UNREACHABLE(); + return 0; + } + + static Instr Cond(Condition cond) { + return cond << Condition_offset; + } + + // PC-relative address encoding. + static Instr ImmPCRelAddress(int imm21) { + VIXL_ASSERT(is_int21(imm21)); + Instr imm = static_cast(truncate_to_int21(imm21)); + Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; + Instr immlo = imm << ImmPCRelLo_offset; + return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); + } + + // Branch encoding. + static Instr ImmUncondBranch(int imm26) { + VIXL_ASSERT(is_int26(imm26)); + return truncate_to_int26(imm26) << ImmUncondBranch_offset; + } + + static Instr ImmCondBranch(int imm19) { + VIXL_ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmCondBranch_offset; + } + + static Instr ImmCmpBranch(int imm19) { + VIXL_ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmCmpBranch_offset; + } + + static Instr ImmTestBranch(int imm14) { + VIXL_ASSERT(is_int14(imm14)); + return truncate_to_int14(imm14) << ImmTestBranch_offset; + } + + static Instr ImmTestBranchBit(unsigned bit_pos) { + VIXL_ASSERT(is_uint6(bit_pos)); + // Subtract five from the shift offset, as we need bit 5 from bit_pos. + unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); + unsigned b40 = bit_pos << ImmTestBranchBit40_offset; + b5 &= ImmTestBranchBit5_mask; + b40 &= ImmTestBranchBit40_mask; + return b5 | b40; + } + + // Data Processing encoding. + static Instr SF(Register rd) { + return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits; + } + + static Instr ImmAddSub(int64_t imm) { + VIXL_ASSERT(IsImmAddSub(imm)); + if (is_uint12(imm)) { // No shift required. + return imm << ImmAddSub_offset; + } else { + return ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); + } + } + + static Instr ImmS(unsigned imms, unsigned reg_size) { + VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || + ((reg_size == kWRegSize) && is_uint5(imms))); + USE(reg_size); + return imms << ImmS_offset; + } + + static Instr ImmR(unsigned immr, unsigned reg_size) { + VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || + ((reg_size == kWRegSize) && is_uint5(immr))); + USE(reg_size); + VIXL_ASSERT(is_uint6(immr)); + return immr << ImmR_offset; + } + + static Instr ImmSetBits(unsigned imms, unsigned reg_size) { + VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + VIXL_ASSERT(is_uint6(imms)); + VIXL_ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); + USE(reg_size); + return imms << ImmSetBits_offset; + } + + static Instr ImmRotate(unsigned immr, unsigned reg_size) { + VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || + ((reg_size == kWRegSize) && is_uint5(immr))); + USE(reg_size); + return immr << ImmRotate_offset; + } + + static Instr ImmLLiteral(int imm19) { + VIXL_ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmLLiteral_offset; + } + + static Instr BitN(unsigned bitn, unsigned reg_size) { + VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + VIXL_ASSERT((reg_size == kXRegSize) || (bitn == 0)); + USE(reg_size); + return bitn << BitN_offset; + } + + static Instr ShiftDP(Shift shift) { + VIXL_ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); + return shift << ShiftDP_offset; + } + + static Instr ImmDPShift(unsigned amount) { + VIXL_ASSERT(is_uint6(amount)); + return amount << ImmDPShift_offset; + } + + static Instr ExtendMode(Extend extend) { + return extend << ExtendMode_offset; + } + + static Instr ImmExtendShift(unsigned left_shift) { + VIXL_ASSERT(left_shift <= 4); + return left_shift << ImmExtendShift_offset; + } + + static Instr ImmCondCmp(unsigned imm) { + VIXL_ASSERT(is_uint5(imm)); + return imm << ImmCondCmp_offset; + } + + static Instr Nzcv(StatusFlags nzcv) { + return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset; + } + + // MemOperand offset encoding. + static Instr ImmLSUnsigned(int imm12) { + VIXL_ASSERT(is_uint12(imm12)); + return imm12 << ImmLSUnsigned_offset; + } + + static Instr ImmLS(int imm9) { + VIXL_ASSERT(is_int9(imm9)); + return truncate_to_int9(imm9) << ImmLS_offset; + } + + static Instr ImmLSPair(int imm7, LSDataSize size) { + VIXL_ASSERT(((imm7 >> size) << size) == imm7); + int scaled_imm7 = imm7 >> size; + VIXL_ASSERT(is_int7(scaled_imm7)); + return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; + } + + static Instr ImmShiftLS(unsigned shift_amount) { + VIXL_ASSERT(is_uint1(shift_amount)); + return shift_amount << ImmShiftLS_offset; + } + + static Instr ImmPrefetchOperation(int imm5) { + VIXL_ASSERT(is_uint5(imm5)); + return imm5 << ImmPrefetchOperation_offset; + } + + static Instr ImmException(int imm16) { + VIXL_ASSERT(is_uint16(imm16)); + return imm16 << ImmException_offset; + } + + static Instr ImmSystemRegister(int imm15) { + VIXL_ASSERT(is_uint15(imm15)); + return imm15 << ImmSystemRegister_offset; + } + + static Instr ImmHint(int imm7) { + VIXL_ASSERT(is_uint7(imm7)); + return imm7 << ImmHint_offset; + } + + static Instr CRm(int imm4) { + VIXL_ASSERT(is_uint4(imm4)); + return imm4 << CRm_offset; + } + + static Instr ImmBarrierDomain(int imm2) { + VIXL_ASSERT(is_uint2(imm2)); + return imm2 << ImmBarrierDomain_offset; + } + + static Instr ImmBarrierType(int imm2) { + VIXL_ASSERT(is_uint2(imm2)); + return imm2 << ImmBarrierType_offset; + } + + static LSDataSize CalcLSDataSize(LoadStoreOp op) { + VIXL_ASSERT((SizeLS_offset + SizeLS_width) == (kInstructionSize * 8)); + return static_cast(op >> SizeLS_offset); + } + + // Move immediates encoding. + static Instr ImmMoveWide(uint64_t imm) { + VIXL_ASSERT(is_uint16(imm)); + return imm << ImmMoveWide_offset; + } + + static Instr ShiftMoveWide(int64_t shift) { + VIXL_ASSERT(is_uint2(shift)); + return shift << ShiftMoveWide_offset; + } + + // FP Immediates. + static Instr ImmFP32(float imm); + static Instr ImmFP64(double imm); + + // FP register type. + static Instr FPType(FPRegister fd) { + return fd.Is64Bits() ? FP64 : FP32; + } + + static Instr FPScale(unsigned scale) { + VIXL_ASSERT(is_uint6(scale)); + return scale << FPScale_offset; + } + + // Immediate field checking helpers. + static bool IsImmAddSub(int64_t immediate); + static bool IsImmConditionalCompare(int64_t immediate); + static bool IsImmFP32(float imm); + static bool IsImmFP64(double imm); + static bool IsImmLogical(uint64_t value, + unsigned width, + unsigned* n = NULL, + unsigned* imm_s = NULL, + unsigned* imm_r = NULL); + static bool IsImmLSPair(int64_t offset, LSDataSize size); + static bool IsImmLSScaled(int64_t offset, LSDataSize size); + static bool IsImmLSUnscaled(int64_t offset); + static bool IsImmMovn(uint64_t imm, unsigned reg_size); + static bool IsImmMovz(uint64_t imm, unsigned reg_size); + + // Size of the code generated since label to the current position. + size_t SizeOfCodeGeneratedSince(Label* label) const { + VIXL_ASSERT(label->IsBound()); + return buffer_->OffsetFrom(label->location()); + } + + size_t SizeOfCodeGenerated() const { + return buffer_->CursorOffset(); + } + + size_t BufferCapacity() const { return buffer_->capacity(); } + + size_t RemainingBufferSpace() const { return buffer_->RemainingBytes(); } + + void EnsureSpaceFor(size_t amount) { + if (buffer_->RemainingBytes() < amount) { + size_t capacity = buffer_->capacity(); + size_t size = buffer_->CursorOffset(); + do { + // TODO(all): refine. + capacity *= 2; + } while ((capacity - size) < amount); + buffer_->Grow(capacity); + } + } + +#ifdef VIXL_DEBUG + void AcquireBuffer() { + VIXL_ASSERT(buffer_monitor_ >= 0); + buffer_monitor_++; + } + + void ReleaseBuffer() { + buffer_monitor_--; + VIXL_ASSERT(buffer_monitor_ >= 0); + } +#endif + + PositionIndependentCodeOption pic() const { + return pic_; + } + + bool AllowPageOffsetDependentCode() const { + return (pic() == PageOffsetDependentCode) || + (pic() == PositionDependentCode); + } + + static const Register& AppropriateZeroRegFor(const CPURegister& reg) { + return reg.Is64Bits() ? xzr : wzr; + } + + + protected: + void LoadStore(const CPURegister& rt, + const MemOperand& addr, + LoadStoreOp op, + LoadStoreScalingOption option = PreferScaledOffset); + + void LoadStorePair(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairOp op); + + void Prefetch(PrefetchOperation op, + const MemOperand& addr, + LoadStoreScalingOption option = PreferScaledOffset); + + // TODO(all): The third parameter should be passed by reference but gcc 4.8.2 + // reports a bogus uninitialised warning then. + void Logical(const Register& rd, + const Register& rn, + const Operand operand, + LogicalOp op); + void LogicalImmediate(const Register& rd, + const Register& rn, + unsigned n, + unsigned imm_s, + unsigned imm_r, + LogicalOp op); + + void ConditionalCompare(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond, + ConditionalCompareOp op); + + void AddSubWithCarry(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubWithCarryOp op); + + + // Functions for emulating operands not directly supported by the instruction + // set. + void EmitShift(const Register& rd, + const Register& rn, + Shift shift, + unsigned amount); + void EmitExtendShift(const Register& rd, + const Register& rn, + Extend extend, + unsigned left_shift); + + void AddSub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubOp op); + + // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified + // registers. Only simple loads are supported; sign- and zero-extension (such + // as in LDPSW_x or LDRB_w) are not supported. + static LoadStoreOp LoadOpFor(const CPURegister& rt); + static LoadStorePairOp LoadPairOpFor(const CPURegister& rt, + const CPURegister& rt2); + static LoadStoreOp StoreOpFor(const CPURegister& rt); + static LoadStorePairOp StorePairOpFor(const CPURegister& rt, + const CPURegister& rt2); + static LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2); + static LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2); + static LoadLiteralOp LoadLiteralOpFor(const CPURegister& rt); + + + private: + // Instruction helpers. + void MoveWide(const Register& rd, + uint64_t imm, + int shift, + MoveWideImmediateOp mov_op); + void DataProcShiftedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op); + void DataProcExtendedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op); + void LoadStorePairNonTemporal(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairNonTemporalOp op); + void LoadLiteral(const CPURegister& rt, uint64_t imm, LoadLiteralOp op); + void ConditionalSelect(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond, + ConditionalSelectOp op); + void DataProcessing1Source(const Register& rd, + const Register& rn, + DataProcessing1SourceOp op); + void DataProcessing3Source(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra, + DataProcessing3SourceOp op); + void FPDataProcessing1Source(const FPRegister& fd, + const FPRegister& fn, + FPDataProcessing1SourceOp op); + void FPDataProcessing2Source(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + FPDataProcessing2SourceOp op); + void FPDataProcessing3Source(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa, + FPDataProcessing3SourceOp op); + + // Encode the specified MemOperand for the specified access size and scaling + // preference. + Instr LoadStoreMemOperand(const MemOperand& addr, + LSDataSize size, + LoadStoreScalingOption option); + + // Link the current (not-yet-emitted) instruction to the specified label, then + // return an offset to be encoded in the instruction. If the label is not yet + // bound, an offset of 0 is returned. + ptrdiff_t LinkAndGetByteOffsetTo(Label * label); + ptrdiff_t LinkAndGetInstructionOffsetTo(Label * label); + ptrdiff_t LinkAndGetPageOffsetTo(Label * label); + + // A common implementation for the LinkAndGetOffsetTo helpers. + template + ptrdiff_t LinkAndGetOffsetTo(Label* label); + + // Literal load offset are in words (32-bit). + ptrdiff_t LinkAndGetWordOffsetTo(RawLiteral* literal); + + // Emit the instruction in buffer_. + void Emit(Instr instruction) { + VIXL_STATIC_ASSERT(sizeof(instruction) == kInstructionSize); + VIXL_ASSERT(buffer_monitor_ > 0); + buffer_->Emit32(instruction); + } + + // Buffer where the code is emitted. + CodeBuffer* buffer_; + PositionIndependentCodeOption pic_; + +#ifdef VIXL_DEBUG + int64_t buffer_monitor_; +#endif +}; + + +// All Assembler emits MUST acquire/release the underlying code buffer. The +// helper scope below will do so and optionally ensure the buffer is big enough +// to receive the emit. It is possible to request the scope not to perform any +// checks (kNoCheck) if for example it is known in advance the buffer size is +// adequate or there is some other size checking mechanism in place. +class CodeBufferCheckScope { + public: + // Tell whether or not the scope needs to ensure the associated CodeBuffer + // has enough space for the requested size. + enum CheckPolicy { + kNoCheck, + kCheck + }; + + // Tell whether or not the scope should assert the amount of code emitted + // within the scope is consistent with the requested amount. + enum AssertPolicy { + kNoAssert, // No assert required. + kExactSize, // The code emitted must be exactly size bytes. + kMaximumSize // The code emitted must be at most size bytes. + }; + + CodeBufferCheckScope(Assembler* assm, + size_t size, + CheckPolicy check_policy = kCheck, + AssertPolicy assert_policy = kMaximumSize) + : assm_(assm) { + if (check_policy == kCheck) assm->EnsureSpaceFor(size); +#ifdef VIXL_DEBUG + assm->bind(&start_); + size_ = size; + assert_policy_ = assert_policy; + assm->AcquireBuffer(); +#else + USE(assert_policy); +#endif + } + + // This is a shortcut for CodeBufferCheckScope(assm, 0, kNoCheck, kNoAssert). + explicit CodeBufferCheckScope(Assembler* assm) : assm_(assm) { +#ifdef VIXL_DEBUG + size_ = 0; + assert_policy_ = kNoAssert; + assm->AcquireBuffer(); +#endif + } + + ~CodeBufferCheckScope() { +#ifdef VIXL_DEBUG + assm_->ReleaseBuffer(); + switch (assert_policy_) { + case kNoAssert: break; + case kExactSize: + VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) == size_); + break; + case kMaximumSize: + VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) <= size_); + break; + default: + VIXL_UNREACHABLE(); + } +#endif + } + + protected: + Assembler* assm_; +#ifdef VIXL_DEBUG + Label start_; + size_t size_; + AssertPolicy assert_policy_; +#endif +}; + +} // namespace vixl + +#endif // VIXL_A64_ASSEMBLER_A64_H_ diff --git a/disas/libvixl/a64/constants-a64.h b/disas/libvixl/a64/constants-a64.h new file mode 100644 index 0000000..bc1a2c4 --- /dev/null +++ b/disas/libvixl/a64/constants-a64.h @@ -0,0 +1,1213 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_CONSTANTS_A64_H_ +#define VIXL_A64_CONSTANTS_A64_H_ + +namespace vixl { + +const unsigned kNumberOfRegisters = 32; +const unsigned kNumberOfFPRegisters = 32; + +#define REGISTER_CODE_LIST(R) \ +R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ +R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ +R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ +R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) + +#define INSTRUCTION_FIELDS_LIST(V_) \ +/* Register fields */ \ +V_(Rd, 4, 0, Bits) /* Destination register. */ \ +V_(Rn, 9, 5, Bits) /* First source register. */ \ +V_(Rm, 20, 16, Bits) /* Second source register. */ \ +V_(Ra, 14, 10, Bits) /* Third source register. */ \ +V_(Rt, 4, 0, Bits) /* Load/store register. */ \ +V_(Rt2, 14, 10, Bits) /* Load/store second register. */ \ +V_(Rs, 20, 16, Bits) /* Exclusive access status. */ \ + \ +/* Common bits */ \ +V_(SixtyFourBits, 31, 31, Bits) \ +V_(FlagsUpdate, 29, 29, Bits) \ + \ +/* PC relative addressing */ \ +V_(ImmPCRelHi, 23, 5, SignedBits) \ +V_(ImmPCRelLo, 30, 29, Bits) \ + \ +/* Add/subtract/logical shift register */ \ +V_(ShiftDP, 23, 22, Bits) \ +V_(ImmDPShift, 15, 10, Bits) \ + \ +/* Add/subtract immediate */ \ +V_(ImmAddSub, 21, 10, Bits) \ +V_(ShiftAddSub, 23, 22, Bits) \ + \ +/* Add/substract extend */ \ +V_(ImmExtendShift, 12, 10, Bits) \ +V_(ExtendMode, 15, 13, Bits) \ + \ +/* Move wide */ \ +V_(ImmMoveWide, 20, 5, Bits) \ +V_(ShiftMoveWide, 22, 21, Bits) \ + \ +/* Logical immediate, bitfield and extract */ \ +V_(BitN, 22, 22, Bits) \ +V_(ImmRotate, 21, 16, Bits) \ +V_(ImmSetBits, 15, 10, Bits) \ +V_(ImmR, 21, 16, Bits) \ +V_(ImmS, 15, 10, Bits) \ + \ +/* Test and branch immediate */ \ +V_(ImmTestBranch, 18, 5, SignedBits) \ +V_(ImmTestBranchBit40, 23, 19, Bits) \ +V_(ImmTestBranchBit5, 31, 31, Bits) \ + \ +/* Conditionals */ \ +V_(Condition, 15, 12, Bits) \ +V_(ConditionBranch, 3, 0, Bits) \ +V_(Nzcv, 3, 0, Bits) \ +V_(ImmCondCmp, 20, 16, Bits) \ +V_(ImmCondBranch, 23, 5, SignedBits) \ + \ +/* Floating point */ \ +V_(FPType, 23, 22, Bits) \ +V_(ImmFP, 20, 13, Bits) \ +V_(FPScale, 15, 10, Bits) \ + \ +/* Load Store */ \ +V_(ImmLS, 20, 12, SignedBits) \ +V_(ImmLSUnsigned, 21, 10, Bits) \ +V_(ImmLSPair, 21, 15, SignedBits) \ +V_(SizeLS, 31, 30, Bits) \ +V_(ImmShiftLS, 12, 12, Bits) \ +V_(ImmPrefetchOperation, 4, 0, Bits) \ +V_(PrefetchHint, 4, 3, Bits) \ +V_(PrefetchTarget, 2, 1, Bits) \ +V_(PrefetchStream, 0, 0, Bits) \ + \ +/* Other immediates */ \ +V_(ImmUncondBranch, 25, 0, SignedBits) \ +V_(ImmCmpBranch, 23, 5, SignedBits) \ +V_(ImmLLiteral, 23, 5, SignedBits) \ +V_(ImmException, 20, 5, Bits) \ +V_(ImmHint, 11, 5, Bits) \ +V_(ImmBarrierDomain, 11, 10, Bits) \ +V_(ImmBarrierType, 9, 8, Bits) \ + \ +/* System (MRS, MSR) */ \ +V_(ImmSystemRegister, 19, 5, Bits) \ +V_(SysO0, 19, 19, Bits) \ +V_(SysOp1, 18, 16, Bits) \ +V_(SysOp2, 7, 5, Bits) \ +V_(CRn, 15, 12, Bits) \ +V_(CRm, 11, 8, Bits) \ + \ +/* Load-/store-exclusive */ \ +V_(LdStXLoad, 22, 22, Bits) \ +V_(LdStXNotExclusive, 23, 23, Bits) \ +V_(LdStXAcquireRelease, 15, 15, Bits) \ +V_(LdStXSizeLog2, 31, 30, Bits) \ +V_(LdStXPair, 21, 21, Bits) \ + + +#define SYSTEM_REGISTER_FIELDS_LIST(V_, M_) \ +/* NZCV */ \ +V_(Flags, 31, 28, Bits) \ +V_(N, 31, 31, Bits) \ +V_(Z, 30, 30, Bits) \ +V_(C, 29, 29, Bits) \ +V_(V, 28, 28, Bits) \ +M_(NZCV, Flags_mask) \ + \ +/* FPCR */ \ +V_(AHP, 26, 26, Bits) \ +V_(DN, 25, 25, Bits) \ +V_(FZ, 24, 24, Bits) \ +V_(RMode, 23, 22, Bits) \ +M_(FPCR, AHP_mask | DN_mask | FZ_mask | RMode_mask) + + +// Fields offsets. +#define DECLARE_FIELDS_OFFSETS(Name, HighBit, LowBit, X) \ +const int Name##_offset = LowBit; \ +const int Name##_width = HighBit - LowBit + 1; \ +const uint32_t Name##_mask = ((1 << Name##_width) - 1) << LowBit; +#define NOTHING(A, B) +INSTRUCTION_FIELDS_LIST(DECLARE_FIELDS_OFFSETS) +SYSTEM_REGISTER_FIELDS_LIST(DECLARE_FIELDS_OFFSETS, NOTHING) +#undef NOTHING +#undef DECLARE_FIELDS_BITS + +// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), formed +// from ImmPCRelLo and ImmPCRelHi. +const int ImmPCRel_mask = ImmPCRelLo_mask | ImmPCRelHi_mask; + +// Condition codes. +enum Condition { + eq = 0, + ne = 1, + hs = 2, + lo = 3, + mi = 4, + pl = 5, + vs = 6, + vc = 7, + hi = 8, + ls = 9, + ge = 10, + lt = 11, + gt = 12, + le = 13, + al = 14, + nv = 15 // Behaves as always/al. +}; + +inline Condition InvertCondition(Condition cond) { + // Conditions al and nv behave identically, as "always true". They can't be + // inverted, because there is no "always false" condition. + VIXL_ASSERT((cond != al) && (cond != nv)); + return static_cast(cond ^ 1); +} + +enum FlagsUpdate { + SetFlags = 1, + LeaveFlags = 0 +}; + +enum StatusFlags { + NoFlag = 0, + + // Derive the flag combinations from the system register bit descriptions. + NFlag = N_mask, + ZFlag = Z_mask, + CFlag = C_mask, + VFlag = V_mask, + NZFlag = NFlag | ZFlag, + NCFlag = NFlag | CFlag, + NVFlag = NFlag | VFlag, + ZCFlag = ZFlag | CFlag, + ZVFlag = ZFlag | VFlag, + CVFlag = CFlag | VFlag, + NZCFlag = NFlag | ZFlag | CFlag, + NZVFlag = NFlag | ZFlag | VFlag, + NCVFlag = NFlag | CFlag | VFlag, + ZCVFlag = ZFlag | CFlag | VFlag, + NZCVFlag = NFlag | ZFlag | CFlag | VFlag, + + // Floating-point comparison results. + FPEqualFlag = ZCFlag, + FPLessThanFlag = NFlag, + FPGreaterThanFlag = CFlag, + FPUnorderedFlag = CVFlag +}; + +enum Shift { + NO_SHIFT = -1, + LSL = 0x0, + LSR = 0x1, + ASR = 0x2, + ROR = 0x3 +}; + +enum Extend { + NO_EXTEND = -1, + UXTB = 0, + UXTH = 1, + UXTW = 2, + UXTX = 3, + SXTB = 4, + SXTH = 5, + SXTW = 6, + SXTX = 7 +}; + +enum SystemHint { + NOP = 0, + YIELD = 1, + WFE = 2, + WFI = 3, + SEV = 4, + SEVL = 5 +}; + +enum BarrierDomain { + OuterShareable = 0, + NonShareable = 1, + InnerShareable = 2, + FullSystem = 3 +}; + +enum BarrierType { + BarrierOther = 0, + BarrierReads = 1, + BarrierWrites = 2, + BarrierAll = 3 +}; + +enum PrefetchOperation { + PLDL1KEEP = 0x00, + PLDL1STRM = 0x01, + PLDL2KEEP = 0x02, + PLDL2STRM = 0x03, + PLDL3KEEP = 0x04, + PLDL3STRM = 0x05, + + PLIL1KEEP = 0x08, + PLIL1STRM = 0x09, + PLIL2KEEP = 0x0a, + PLIL2STRM = 0x0b, + PLIL3KEEP = 0x0c, + PLIL3STRM = 0x0d, + + PSTL1KEEP = 0x10, + PSTL1STRM = 0x11, + PSTL2KEEP = 0x12, + PSTL2STRM = 0x13, + PSTL3KEEP = 0x14, + PSTL3STRM = 0x15 +}; + +// System/special register names. +// This information is not encoded as one field but as the concatenation of +// multiple fields (Op0<0>, Op1, Crn, Crm, Op2). +enum SystemRegister { + NZCV = ((0x1 << SysO0_offset) | + (0x3 << SysOp1_offset) | + (0x4 << CRn_offset) | + (0x2 << CRm_offset) | + (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset, + FPCR = ((0x1 << SysO0_offset) | + (0x3 << SysOp1_offset) | + (0x4 << CRn_offset) | + (0x4 << CRm_offset) | + (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset +}; + +// Instruction enumerations. +// +// These are the masks that define a class of instructions, and the list of +// instructions within each class. Each enumeration has a Fixed, FMask and +// Mask value. +// +// Fixed: The fixed bits in this instruction class. +// FMask: The mask used to extract the fixed bits in the class. +// Mask: The mask used to identify the instructions within a class. +// +// The enumerations can be used like this: +// +// VIXL_ASSERT(instr->Mask(PCRelAddressingFMask) == PCRelAddressingFixed); +// switch(instr->Mask(PCRelAddressingMask)) { +// case ADR: Format("adr 'Xd, 'AddrPCRelByte"); break; +// case ADRP: Format("adrp 'Xd, 'AddrPCRelPage"); break; +// default: printf("Unknown instruction\n"); +// } + + +// Generic fields. +enum GenericInstrField { + SixtyFourBits = 0x80000000, + ThirtyTwoBits = 0x00000000, + FP32 = 0x00000000, + FP64 = 0x00400000 +}; + +// PC relative addressing. +enum PCRelAddressingOp { + PCRelAddressingFixed = 0x10000000, + PCRelAddressingFMask = 0x1F000000, + PCRelAddressingMask = 0x9F000000, + ADR = PCRelAddressingFixed | 0x00000000, + ADRP = PCRelAddressingFixed | 0x80000000 +}; + +// Add/sub (immediate, shifted and extended.) +const int kSFOffset = 31; +enum AddSubOp { + AddSubOpMask = 0x60000000, + AddSubSetFlagsBit = 0x20000000, + ADD = 0x00000000, + ADDS = ADD | AddSubSetFlagsBit, + SUB = 0x40000000, + SUBS = SUB | AddSubSetFlagsBit +}; + +#define ADD_SUB_OP_LIST(V) \ + V(ADD), \ + V(ADDS), \ + V(SUB), \ + V(SUBS) + +enum AddSubImmediateOp { + AddSubImmediateFixed = 0x11000000, + AddSubImmediateFMask = 0x1F000000, + AddSubImmediateMask = 0xFF000000, + #define ADD_SUB_IMMEDIATE(A) \ + A##_w_imm = AddSubImmediateFixed | A, \ + A##_x_imm = AddSubImmediateFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_IMMEDIATE) + #undef ADD_SUB_IMMEDIATE +}; + +enum AddSubShiftedOp { + AddSubShiftedFixed = 0x0B000000, + AddSubShiftedFMask = 0x1F200000, + AddSubShiftedMask = 0xFF200000, + #define ADD_SUB_SHIFTED(A) \ + A##_w_shift = AddSubShiftedFixed | A, \ + A##_x_shift = AddSubShiftedFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_SHIFTED) + #undef ADD_SUB_SHIFTED +}; + +enum AddSubExtendedOp { + AddSubExtendedFixed = 0x0B200000, + AddSubExtendedFMask = 0x1F200000, + AddSubExtendedMask = 0xFFE00000, + #define ADD_SUB_EXTENDED(A) \ + A##_w_ext = AddSubExtendedFixed | A, \ + A##_x_ext = AddSubExtendedFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_EXTENDED) + #undef ADD_SUB_EXTENDED +}; + +// Add/sub with carry. +enum AddSubWithCarryOp { + AddSubWithCarryFixed = 0x1A000000, + AddSubWithCarryFMask = 0x1FE00000, + AddSubWithCarryMask = 0xFFE0FC00, + ADC_w = AddSubWithCarryFixed | ADD, + ADC_x = AddSubWithCarryFixed | ADD | SixtyFourBits, + ADC = ADC_w, + ADCS_w = AddSubWithCarryFixed | ADDS, + ADCS_x = AddSubWithCarryFixed | ADDS | SixtyFourBits, + SBC_w = AddSubWithCarryFixed | SUB, + SBC_x = AddSubWithCarryFixed | SUB | SixtyFourBits, + SBC = SBC_w, + SBCS_w = AddSubWithCarryFixed | SUBS, + SBCS_x = AddSubWithCarryFixed | SUBS | SixtyFourBits +}; + + +// Logical (immediate and shifted register). +enum LogicalOp { + LogicalOpMask = 0x60200000, + NOT = 0x00200000, + AND = 0x00000000, + BIC = AND | NOT, + ORR = 0x20000000, + ORN = ORR | NOT, + EOR = 0x40000000, + EON = EOR | NOT, + ANDS = 0x60000000, + BICS = ANDS | NOT +}; + +// Logical immediate. +enum LogicalImmediateOp { + LogicalImmediateFixed = 0x12000000, + LogicalImmediateFMask = 0x1F800000, + LogicalImmediateMask = 0xFF800000, + AND_w_imm = LogicalImmediateFixed | AND, + AND_x_imm = LogicalImmediateFixed | AND | SixtyFourBits, + ORR_w_imm = LogicalImmediateFixed | ORR, + ORR_x_imm = LogicalImmediateFixed | ORR | SixtyFourBits, + EOR_w_imm = LogicalImmediateFixed | EOR, + EOR_x_imm = LogicalImmediateFixed | EOR | SixtyFourBits, + ANDS_w_imm = LogicalImmediateFixed | ANDS, + ANDS_x_imm = LogicalImmediateFixed | ANDS | SixtyFourBits +}; + +// Logical shifted register. +enum LogicalShiftedOp { + LogicalShiftedFixed = 0x0A000000, + LogicalShiftedFMask = 0x1F000000, + LogicalShiftedMask = 0xFF200000, + AND_w = LogicalShiftedFixed | AND, + AND_x = LogicalShiftedFixed | AND | SixtyFourBits, + AND_shift = AND_w, + BIC_w = LogicalShiftedFixed | BIC, + BIC_x = LogicalShiftedFixed | BIC | SixtyFourBits, + BIC_shift = BIC_w, + ORR_w = LogicalShiftedFixed | ORR, + ORR_x = LogicalShiftedFixed | ORR | SixtyFourBits, + ORR_shift = ORR_w, + ORN_w = LogicalShiftedFixed | ORN, + ORN_x = LogicalShiftedFixed | ORN | SixtyFourBits, + ORN_shift = ORN_w, + EOR_w = LogicalShiftedFixed | EOR, + EOR_x = LogicalShiftedFixed | EOR | SixtyFourBits, + EOR_shift = EOR_w, + EON_w = LogicalShiftedFixed | EON, + EON_x = LogicalShiftedFixed | EON | SixtyFourBits, + EON_shift = EON_w, + ANDS_w = LogicalShiftedFixed | ANDS, + ANDS_x = LogicalShiftedFixed | ANDS | SixtyFourBits, + ANDS_shift = ANDS_w, + BICS_w = LogicalShiftedFixed | BICS, + BICS_x = LogicalShiftedFixed | BICS | SixtyFourBits, + BICS_shift = BICS_w +}; + +// Move wide immediate. +enum MoveWideImmediateOp { + MoveWideImmediateFixed = 0x12800000, + MoveWideImmediateFMask = 0x1F800000, + MoveWideImmediateMask = 0xFF800000, + MOVN = 0x00000000, + MOVZ = 0x40000000, + MOVK = 0x60000000, + MOVN_w = MoveWideImmediateFixed | MOVN, + MOVN_x = MoveWideImmediateFixed | MOVN | SixtyFourBits, + MOVZ_w = MoveWideImmediateFixed | MOVZ, + MOVZ_x = MoveWideImmediateFixed | MOVZ | SixtyFourBits, + MOVK_w = MoveWideImmediateFixed | MOVK, + MOVK_x = MoveWideImmediateFixed | MOVK | SixtyFourBits +}; + +// Bitfield. +const int kBitfieldNOffset = 22; +enum BitfieldOp { + BitfieldFixed = 0x13000000, + BitfieldFMask = 0x1F800000, + BitfieldMask = 0xFF800000, + SBFM_w = BitfieldFixed | 0x00000000, + SBFM_x = BitfieldFixed | 0x80000000, + SBFM = SBFM_w, + BFM_w = BitfieldFixed | 0x20000000, + BFM_x = BitfieldFixed | 0xA0000000, + BFM = BFM_w, + UBFM_w = BitfieldFixed | 0x40000000, + UBFM_x = BitfieldFixed | 0xC0000000, + UBFM = UBFM_w + // Bitfield N field. +}; + +// Extract. +enum ExtractOp { + ExtractFixed = 0x13800000, + ExtractFMask = 0x1F800000, + ExtractMask = 0xFFA00000, + EXTR_w = ExtractFixed | 0x00000000, + EXTR_x = ExtractFixed | 0x80000000, + EXTR = EXTR_w +}; + +// Unconditional branch. +enum UnconditionalBranchOp { + UnconditionalBranchFixed = 0x14000000, + UnconditionalBranchFMask = 0x7C000000, + UnconditionalBranchMask = 0xFC000000, + B = UnconditionalBranchFixed | 0x00000000, + BL = UnconditionalBranchFixed | 0x80000000 +}; + +// Unconditional branch to register. +enum UnconditionalBranchToRegisterOp { + UnconditionalBranchToRegisterFixed = 0xD6000000, + UnconditionalBranchToRegisterFMask = 0xFE000000, + UnconditionalBranchToRegisterMask = 0xFFFFFC1F, + BR = UnconditionalBranchToRegisterFixed | 0x001F0000, + BLR = UnconditionalBranchToRegisterFixed | 0x003F0000, + RET = UnconditionalBranchToRegisterFixed | 0x005F0000 +}; + +// Compare and branch. +enum CompareBranchOp { + CompareBranchFixed = 0x34000000, + CompareBranchFMask = 0x7E000000, + CompareBranchMask = 0xFF000000, + CBZ_w = CompareBranchFixed | 0x00000000, + CBZ_x = CompareBranchFixed | 0x80000000, + CBZ = CBZ_w, + CBNZ_w = CompareBranchFixed | 0x01000000, + CBNZ_x = CompareBranchFixed | 0x81000000, + CBNZ = CBNZ_w +}; + +// Test and branch. +enum TestBranchOp { + TestBranchFixed = 0x36000000, + TestBranchFMask = 0x7E000000, + TestBranchMask = 0x7F000000, + TBZ = TestBranchFixed | 0x00000000, + TBNZ = TestBranchFixed | 0x01000000 +}; + +// Conditional branch. +enum ConditionalBranchOp { + ConditionalBranchFixed = 0x54000000, + ConditionalBranchFMask = 0xFE000000, + ConditionalBranchMask = 0xFF000010, + B_cond = ConditionalBranchFixed | 0x00000000 +}; + +// System. +// System instruction encoding is complicated because some instructions use op +// and CR fields to encode parameters. To handle this cleanly, the system +// instructions are split into more than one enum. + +enum SystemOp { + SystemFixed = 0xD5000000, + SystemFMask = 0xFFC00000 +}; + +enum SystemSysRegOp { + SystemSysRegFixed = 0xD5100000, + SystemSysRegFMask = 0xFFD00000, + SystemSysRegMask = 0xFFF00000, + MRS = SystemSysRegFixed | 0x00200000, + MSR = SystemSysRegFixed | 0x00000000 +}; + +enum SystemHintOp { + SystemHintFixed = 0xD503201F, + SystemHintFMask = 0xFFFFF01F, + SystemHintMask = 0xFFFFF01F, + HINT = SystemHintFixed | 0x00000000 +}; + +// Exception. +enum ExceptionOp { + ExceptionFixed = 0xD4000000, + ExceptionFMask = 0xFF000000, + ExceptionMask = 0xFFE0001F, + HLT = ExceptionFixed | 0x00400000, + BRK = ExceptionFixed | 0x00200000, + SVC = ExceptionFixed | 0x00000001, + HVC = ExceptionFixed | 0x00000002, + SMC = ExceptionFixed | 0x00000003, + DCPS1 = ExceptionFixed | 0x00A00001, + DCPS2 = ExceptionFixed | 0x00A00002, + DCPS3 = ExceptionFixed | 0x00A00003 +}; + +enum MemBarrierOp { + MemBarrierFixed = 0xD503309F, + MemBarrierFMask = 0xFFFFF09F, + MemBarrierMask = 0xFFFFF0FF, + DSB = MemBarrierFixed | 0x00000000, + DMB = MemBarrierFixed | 0x00000020, + ISB = MemBarrierFixed | 0x00000040 +}; + +enum SystemExclusiveMonitorOp { + SystemExclusiveMonitorFixed = 0xD503305F, + SystemExclusiveMonitorFMask = 0xFFFFF0FF, + SystemExclusiveMonitorMask = 0xFFFFF0FF, + CLREX = SystemExclusiveMonitorFixed +}; + +// Any load or store. +enum LoadStoreAnyOp { + LoadStoreAnyFMask = 0x0a000000, + LoadStoreAnyFixed = 0x08000000 +}; + +// Any load pair or store pair. +enum LoadStorePairAnyOp { + LoadStorePairAnyFMask = 0x3a000000, + LoadStorePairAnyFixed = 0x28000000 +}; + +#define LOAD_STORE_PAIR_OP_LIST(V) \ + V(STP, w, 0x00000000), \ + V(LDP, w, 0x00400000), \ + V(LDPSW, x, 0x40400000), \ + V(STP, x, 0x80000000), \ + V(LDP, x, 0x80400000), \ + V(STP, s, 0x04000000), \ + V(LDP, s, 0x04400000), \ + V(STP, d, 0x44000000), \ + V(LDP, d, 0x44400000) + +// Load/store pair (post, pre and offset.) +enum LoadStorePairOp { + LoadStorePairMask = 0xC4400000, + LoadStorePairLBit = 1 << 22, + #define LOAD_STORE_PAIR(A, B, C) \ + A##_##B = C + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR) + #undef LOAD_STORE_PAIR +}; + +enum LoadStorePairPostIndexOp { + LoadStorePairPostIndexFixed = 0x28800000, + LoadStorePairPostIndexFMask = 0x3B800000, + LoadStorePairPostIndexMask = 0xFFC00000, + #define LOAD_STORE_PAIR_POST_INDEX(A, B, C) \ + A##_##B##_post = LoadStorePairPostIndexFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_POST_INDEX) + #undef LOAD_STORE_PAIR_POST_INDEX +}; + +enum LoadStorePairPreIndexOp { + LoadStorePairPreIndexFixed = 0x29800000, + LoadStorePairPreIndexFMask = 0x3B800000, + LoadStorePairPreIndexMask = 0xFFC00000, + #define LOAD_STORE_PAIR_PRE_INDEX(A, B, C) \ + A##_##B##_pre = LoadStorePairPreIndexFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_PRE_INDEX) + #undef LOAD_STORE_PAIR_PRE_INDEX +}; + +enum LoadStorePairOffsetOp { + LoadStorePairOffsetFixed = 0x29000000, + LoadStorePairOffsetFMask = 0x3B800000, + LoadStorePairOffsetMask = 0xFFC00000, + #define LOAD_STORE_PAIR_OFFSET(A, B, C) \ + A##_##B##_off = LoadStorePairOffsetFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_OFFSET) + #undef LOAD_STORE_PAIR_OFFSET +}; + +enum LoadStorePairNonTemporalOp { + LoadStorePairNonTemporalFixed = 0x28000000, + LoadStorePairNonTemporalFMask = 0x3B800000, + LoadStorePairNonTemporalMask = 0xFFC00000, + STNP_w = LoadStorePairNonTemporalFixed | STP_w, + LDNP_w = LoadStorePairNonTemporalFixed | LDP_w, + STNP_x = LoadStorePairNonTemporalFixed | STP_x, + LDNP_x = LoadStorePairNonTemporalFixed | LDP_x, + STNP_s = LoadStorePairNonTemporalFixed | STP_s, + LDNP_s = LoadStorePairNonTemporalFixed | LDP_s, + STNP_d = LoadStorePairNonTemporalFixed | STP_d, + LDNP_d = LoadStorePairNonTemporalFixed | LDP_d +}; + +// Load literal. +enum LoadLiteralOp { + LoadLiteralFixed = 0x18000000, + LoadLiteralFMask = 0x3B000000, + LoadLiteralMask = 0xFF000000, + LDR_w_lit = LoadLiteralFixed | 0x00000000, + LDR_x_lit = LoadLiteralFixed | 0x40000000, + LDRSW_x_lit = LoadLiteralFixed | 0x80000000, + PRFM_lit = LoadLiteralFixed | 0xC0000000, + LDR_s_lit = LoadLiteralFixed | 0x04000000, + LDR_d_lit = LoadLiteralFixed | 0x44000000 +}; + +#define LOAD_STORE_OP_LIST(V) \ + V(ST, RB, w, 0x00000000), \ + V(ST, RH, w, 0x40000000), \ + V(ST, R, w, 0x80000000), \ + V(ST, R, x, 0xC0000000), \ + V(LD, RB, w, 0x00400000), \ + V(LD, RH, w, 0x40400000), \ + V(LD, R, w, 0x80400000), \ + V(LD, R, x, 0xC0400000), \ + V(LD, RSB, x, 0x00800000), \ + V(LD, RSH, x, 0x40800000), \ + V(LD, RSW, x, 0x80800000), \ + V(LD, RSB, w, 0x00C00000), \ + V(LD, RSH, w, 0x40C00000), \ + V(ST, R, s, 0x84000000), \ + V(ST, R, d, 0xC4000000), \ + V(LD, R, s, 0x84400000), \ + V(LD, R, d, 0xC4400000) + + +// Load/store (post, pre, offset and unsigned.) +enum LoadStoreOp { + LoadStoreOpMask = 0xC4C00000, + #define LOAD_STORE(A, B, C, D) \ + A##B##_##C = D + LOAD_STORE_OP_LIST(LOAD_STORE), + #undef LOAD_STORE + PRFM = 0xC0800000 +}; + +// Load/store unscaled offset. +enum LoadStoreUnscaledOffsetOp { + LoadStoreUnscaledOffsetFixed = 0x38000000, + LoadStoreUnscaledOffsetFMask = 0x3B200C00, + LoadStoreUnscaledOffsetMask = 0xFFE00C00, + PRFUM = LoadStoreUnscaledOffsetFixed | PRFM, + #define LOAD_STORE_UNSCALED(A, B, C, D) \ + A##U##B##_##C = LoadStoreUnscaledOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) + #undef LOAD_STORE_UNSCALED +}; + +// Load/store post index. +enum LoadStorePostIndex { + LoadStorePostIndexFixed = 0x38000400, + LoadStorePostIndexFMask = 0x3B200C00, + LoadStorePostIndexMask = 0xFFE00C00, + #define LOAD_STORE_POST_INDEX(A, B, C, D) \ + A##B##_##C##_post = LoadStorePostIndexFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_POST_INDEX) + #undef LOAD_STORE_POST_INDEX +}; + +// Load/store pre index. +enum LoadStorePreIndex { + LoadStorePreIndexFixed = 0x38000C00, + LoadStorePreIndexFMask = 0x3B200C00, + LoadStorePreIndexMask = 0xFFE00C00, + #define LOAD_STORE_PRE_INDEX(A, B, C, D) \ + A##B##_##C##_pre = LoadStorePreIndexFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_PRE_INDEX) + #undef LOAD_STORE_PRE_INDEX +}; + +// Load/store unsigned offset. +enum LoadStoreUnsignedOffset { + LoadStoreUnsignedOffsetFixed = 0x39000000, + LoadStoreUnsignedOffsetFMask = 0x3B000000, + LoadStoreUnsignedOffsetMask = 0xFFC00000, + PRFM_unsigned = LoadStoreUnsignedOffsetFixed | PRFM, + #define LOAD_STORE_UNSIGNED_OFFSET(A, B, C, D) \ + A##B##_##C##_unsigned = LoadStoreUnsignedOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_UNSIGNED_OFFSET) + #undef LOAD_STORE_UNSIGNED_OFFSET +}; + +// Load/store register offset. +enum LoadStoreRegisterOffset { + LoadStoreRegisterOffsetFixed = 0x38200800, + LoadStoreRegisterOffsetFMask = 0x3B200C00, + LoadStoreRegisterOffsetMask = 0xFFE00C00, + PRFM_reg = LoadStoreRegisterOffsetFixed | PRFM, + #define LOAD_STORE_REGISTER_OFFSET(A, B, C, D) \ + A##B##_##C##_reg = LoadStoreRegisterOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_REGISTER_OFFSET) + #undef LOAD_STORE_REGISTER_OFFSET +}; + +enum LoadStoreExclusive { + LoadStoreExclusiveFixed = 0x08000000, + LoadStoreExclusiveFMask = 0x3F000000, + LoadStoreExclusiveMask = 0xFFE08000, + STXRB_w = LoadStoreExclusiveFixed | 0x00000000, + STXRH_w = LoadStoreExclusiveFixed | 0x40000000, + STXR_w = LoadStoreExclusiveFixed | 0x80000000, + STXR_x = LoadStoreExclusiveFixed | 0xC0000000, + LDXRB_w = LoadStoreExclusiveFixed | 0x00400000, + LDXRH_w = LoadStoreExclusiveFixed | 0x40400000, + LDXR_w = LoadStoreExclusiveFixed | 0x80400000, + LDXR_x = LoadStoreExclusiveFixed | 0xC0400000, + STXP_w = LoadStoreExclusiveFixed | 0x80200000, + STXP_x = LoadStoreExclusiveFixed | 0xC0200000, + LDXP_w = LoadStoreExclusiveFixed | 0x80600000, + LDXP_x = LoadStoreExclusiveFixed | 0xC0600000, + STLXRB_w = LoadStoreExclusiveFixed | 0x00008000, + STLXRH_w = LoadStoreExclusiveFixed | 0x40008000, + STLXR_w = LoadStoreExclusiveFixed | 0x80008000, + STLXR_x = LoadStoreExclusiveFixed | 0xC0008000, + LDAXRB_w = LoadStoreExclusiveFixed | 0x00408000, + LDAXRH_w = LoadStoreExclusiveFixed | 0x40408000, + LDAXR_w = LoadStoreExclusiveFixed | 0x80408000, + LDAXR_x = LoadStoreExclusiveFixed | 0xC0408000, + STLXP_w = LoadStoreExclusiveFixed | 0x80208000, + STLXP_x = LoadStoreExclusiveFixed | 0xC0208000, + LDAXP_w = LoadStoreExclusiveFixed | 0x80608000, + LDAXP_x = LoadStoreExclusiveFixed | 0xC0608000, + STLRB_w = LoadStoreExclusiveFixed | 0x00808000, + STLRH_w = LoadStoreExclusiveFixed | 0x40808000, + STLR_w = LoadStoreExclusiveFixed | 0x80808000, + STLR_x = LoadStoreExclusiveFixed | 0xC0808000, + LDARB_w = LoadStoreExclusiveFixed | 0x00C08000, + LDARH_w = LoadStoreExclusiveFixed | 0x40C08000, + LDAR_w = LoadStoreExclusiveFixed | 0x80C08000, + LDAR_x = LoadStoreExclusiveFixed | 0xC0C08000 +}; + +// Conditional compare. +enum ConditionalCompareOp { + ConditionalCompareMask = 0x60000000, + CCMN = 0x20000000, + CCMP = 0x60000000 +}; + +// Conditional compare register. +enum ConditionalCompareRegisterOp { + ConditionalCompareRegisterFixed = 0x1A400000, + ConditionalCompareRegisterFMask = 0x1FE00800, + ConditionalCompareRegisterMask = 0xFFE00C10, + CCMN_w = ConditionalCompareRegisterFixed | CCMN, + CCMN_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMN, + CCMP_w = ConditionalCompareRegisterFixed | CCMP, + CCMP_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMP +}; + +// Conditional compare immediate. +enum ConditionalCompareImmediateOp { + ConditionalCompareImmediateFixed = 0x1A400800, + ConditionalCompareImmediateFMask = 0x1FE00800, + ConditionalCompareImmediateMask = 0xFFE00C10, + CCMN_w_imm = ConditionalCompareImmediateFixed | CCMN, + CCMN_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMN, + CCMP_w_imm = ConditionalCompareImmediateFixed | CCMP, + CCMP_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMP +}; + +// Conditional select. +enum ConditionalSelectOp { + ConditionalSelectFixed = 0x1A800000, + ConditionalSelectFMask = 0x1FE00000, + ConditionalSelectMask = 0xFFE00C00, + CSEL_w = ConditionalSelectFixed | 0x00000000, + CSEL_x = ConditionalSelectFixed | 0x80000000, + CSEL = CSEL_w, + CSINC_w = ConditionalSelectFixed | 0x00000400, + CSINC_x = ConditionalSelectFixed | 0x80000400, + CSINC = CSINC_w, + CSINV_w = ConditionalSelectFixed | 0x40000000, + CSINV_x = ConditionalSelectFixed | 0xC0000000, + CSINV = CSINV_w, + CSNEG_w = ConditionalSelectFixed | 0x40000400, + CSNEG_x = ConditionalSelectFixed | 0xC0000400, + CSNEG = CSNEG_w +}; + +// Data processing 1 source. +enum DataProcessing1SourceOp { + DataProcessing1SourceFixed = 0x5AC00000, + DataProcessing1SourceFMask = 0x5FE00000, + DataProcessing1SourceMask = 0xFFFFFC00, + RBIT = DataProcessing1SourceFixed | 0x00000000, + RBIT_w = RBIT, + RBIT_x = RBIT | SixtyFourBits, + REV16 = DataProcessing1SourceFixed | 0x00000400, + REV16_w = REV16, + REV16_x = REV16 | SixtyFourBits, + REV = DataProcessing1SourceFixed | 0x00000800, + REV_w = REV, + REV32_x = REV | SixtyFourBits, + REV_x = DataProcessing1SourceFixed | SixtyFourBits | 0x00000C00, + CLZ = DataProcessing1SourceFixed | 0x00001000, + CLZ_w = CLZ, + CLZ_x = CLZ | SixtyFourBits, + CLS = DataProcessing1SourceFixed | 0x00001400, + CLS_w = CLS, + CLS_x = CLS | SixtyFourBits +}; + +// Data processing 2 source. +enum DataProcessing2SourceOp { + DataProcessing2SourceFixed = 0x1AC00000, + DataProcessing2SourceFMask = 0x5FE00000, + DataProcessing2SourceMask = 0xFFE0FC00, + UDIV_w = DataProcessing2SourceFixed | 0x00000800, + UDIV_x = DataProcessing2SourceFixed | 0x80000800, + UDIV = UDIV_w, + SDIV_w = DataProcessing2SourceFixed | 0x00000C00, + SDIV_x = DataProcessing2SourceFixed | 0x80000C00, + SDIV = SDIV_w, + LSLV_w = DataProcessing2SourceFixed | 0x00002000, + LSLV_x = DataProcessing2SourceFixed | 0x80002000, + LSLV = LSLV_w, + LSRV_w = DataProcessing2SourceFixed | 0x00002400, + LSRV_x = DataProcessing2SourceFixed | 0x80002400, + LSRV = LSRV_w, + ASRV_w = DataProcessing2SourceFixed | 0x00002800, + ASRV_x = DataProcessing2SourceFixed | 0x80002800, + ASRV = ASRV_w, + RORV_w = DataProcessing2SourceFixed | 0x00002C00, + RORV_x = DataProcessing2SourceFixed | 0x80002C00, + RORV = RORV_w, + CRC32B = DataProcessing2SourceFixed | 0x00004000, + CRC32H = DataProcessing2SourceFixed | 0x00004400, + CRC32W = DataProcessing2SourceFixed | 0x00004800, + CRC32X = DataProcessing2SourceFixed | SixtyFourBits | 0x00004C00, + CRC32CB = DataProcessing2SourceFixed | 0x00005000, + CRC32CH = DataProcessing2SourceFixed | 0x00005400, + CRC32CW = DataProcessing2SourceFixed | 0x00005800, + CRC32CX = DataProcessing2SourceFixed | SixtyFourBits | 0x00005C00 +}; + +// Data processing 3 source. +enum DataProcessing3SourceOp { + DataProcessing3SourceFixed = 0x1B000000, + DataProcessing3SourceFMask = 0x1F000000, + DataProcessing3SourceMask = 0xFFE08000, + MADD_w = DataProcessing3SourceFixed | 0x00000000, + MADD_x = DataProcessing3SourceFixed | 0x80000000, + MADD = MADD_w, + MSUB_w = DataProcessing3SourceFixed | 0x00008000, + MSUB_x = DataProcessing3SourceFixed | 0x80008000, + MSUB = MSUB_w, + SMADDL_x = DataProcessing3SourceFixed | 0x80200000, + SMSUBL_x = DataProcessing3SourceFixed | 0x80208000, + SMULH_x = DataProcessing3SourceFixed | 0x80400000, + UMADDL_x = DataProcessing3SourceFixed | 0x80A00000, + UMSUBL_x = DataProcessing3SourceFixed | 0x80A08000, + UMULH_x = DataProcessing3SourceFixed | 0x80C00000 +}; + +// Floating point compare. +enum FPCompareOp { + FPCompareFixed = 0x1E202000, + FPCompareFMask = 0x5F203C00, + FPCompareMask = 0xFFE0FC1F, + FCMP_s = FPCompareFixed | 0x00000000, + FCMP_d = FPCompareFixed | FP64 | 0x00000000, + FCMP = FCMP_s, + FCMP_s_zero = FPCompareFixed | 0x00000008, + FCMP_d_zero = FPCompareFixed | FP64 | 0x00000008, + FCMP_zero = FCMP_s_zero, + FCMPE_s = FPCompareFixed | 0x00000010, + FCMPE_d = FPCompareFixed | FP64 | 0x00000010, + FCMPE_s_zero = FPCompareFixed | 0x00000018, + FCMPE_d_zero = FPCompareFixed | FP64 | 0x00000018 +}; + +// Floating point conditional compare. +enum FPConditionalCompareOp { + FPConditionalCompareFixed = 0x1E200400, + FPConditionalCompareFMask = 0x5F200C00, + FPConditionalCompareMask = 0xFFE00C10, + FCCMP_s = FPConditionalCompareFixed | 0x00000000, + FCCMP_d = FPConditionalCompareFixed | FP64 | 0x00000000, + FCCMP = FCCMP_s, + FCCMPE_s = FPConditionalCompareFixed | 0x00000010, + FCCMPE_d = FPConditionalCompareFixed | FP64 | 0x00000010, + FCCMPE = FCCMPE_s +}; + +// Floating point conditional select. +enum FPConditionalSelectOp { + FPConditionalSelectFixed = 0x1E200C00, + FPConditionalSelectFMask = 0x5F200C00, + FPConditionalSelectMask = 0xFFE00C00, + FCSEL_s = FPConditionalSelectFixed | 0x00000000, + FCSEL_d = FPConditionalSelectFixed | FP64 | 0x00000000, + FCSEL = FCSEL_s +}; + +// Floating point immediate. +enum FPImmediateOp { + FPImmediateFixed = 0x1E201000, + FPImmediateFMask = 0x5F201C00, + FPImmediateMask = 0xFFE01C00, + FMOV_s_imm = FPImmediateFixed | 0x00000000, + FMOV_d_imm = FPImmediateFixed | FP64 | 0x00000000 +}; + +// Floating point data processing 1 source. +enum FPDataProcessing1SourceOp { + FPDataProcessing1SourceFixed = 0x1E204000, + FPDataProcessing1SourceFMask = 0x5F207C00, + FPDataProcessing1SourceMask = 0xFFFFFC00, + FMOV_s = FPDataProcessing1SourceFixed | 0x00000000, + FMOV_d = FPDataProcessing1SourceFixed | FP64 | 0x00000000, + FMOV = FMOV_s, + FABS_s = FPDataProcessing1SourceFixed | 0x00008000, + FABS_d = FPDataProcessing1SourceFixed | FP64 | 0x00008000, + FABS = FABS_s, + FNEG_s = FPDataProcessing1SourceFixed | 0x00010000, + FNEG_d = FPDataProcessing1SourceFixed | FP64 | 0x00010000, + FNEG = FNEG_s, + FSQRT_s = FPDataProcessing1SourceFixed | 0x00018000, + FSQRT_d = FPDataProcessing1SourceFixed | FP64 | 0x00018000, + FSQRT = FSQRT_s, + FCVT_ds = FPDataProcessing1SourceFixed | 0x00028000, + FCVT_sd = FPDataProcessing1SourceFixed | FP64 | 0x00020000, + FRINTN_s = FPDataProcessing1SourceFixed | 0x00040000, + FRINTN_d = FPDataProcessing1SourceFixed | FP64 | 0x00040000, + FRINTN = FRINTN_s, + FRINTP_s = FPDataProcessing1SourceFixed | 0x00048000, + FRINTP_d = FPDataProcessing1SourceFixed | FP64 | 0x00048000, + FRINTP = FRINTP_s, + FRINTM_s = FPDataProcessing1SourceFixed | 0x00050000, + FRINTM_d = FPDataProcessing1SourceFixed | FP64 | 0x00050000, + FRINTM = FRINTM_s, + FRINTZ_s = FPDataProcessing1SourceFixed | 0x00058000, + FRINTZ_d = FPDataProcessing1SourceFixed | FP64 | 0x00058000, + FRINTZ = FRINTZ_s, + FRINTA_s = FPDataProcessing1SourceFixed | 0x00060000, + FRINTA_d = FPDataProcessing1SourceFixed | FP64 | 0x00060000, + FRINTA = FRINTA_s, + FRINTX_s = FPDataProcessing1SourceFixed | 0x00070000, + FRINTX_d = FPDataProcessing1SourceFixed | FP64 | 0x00070000, + FRINTX = FRINTX_s, + FRINTI_s = FPDataProcessing1SourceFixed | 0x00078000, + FRINTI_d = FPDataProcessing1SourceFixed | FP64 | 0x00078000, + FRINTI = FRINTI_s +}; + +// Floating point data processing 2 source. +enum FPDataProcessing2SourceOp { + FPDataProcessing2SourceFixed = 0x1E200800, + FPDataProcessing2SourceFMask = 0x5F200C00, + FPDataProcessing2SourceMask = 0xFFE0FC00, + FMUL = FPDataProcessing2SourceFixed | 0x00000000, + FMUL_s = FMUL, + FMUL_d = FMUL | FP64, + FDIV = FPDataProcessing2SourceFixed | 0x00001000, + FDIV_s = FDIV, + FDIV_d = FDIV | FP64, + FADD = FPDataProcessing2SourceFixed | 0x00002000, + FADD_s = FADD, + FADD_d = FADD | FP64, + FSUB = FPDataProcessing2SourceFixed | 0x00003000, + FSUB_s = FSUB, + FSUB_d = FSUB | FP64, + FMAX = FPDataProcessing2SourceFixed | 0x00004000, + FMAX_s = FMAX, + FMAX_d = FMAX | FP64, + FMIN = FPDataProcessing2SourceFixed | 0x00005000, + FMIN_s = FMIN, + FMIN_d = FMIN | FP64, + FMAXNM = FPDataProcessing2SourceFixed | 0x00006000, + FMAXNM_s = FMAXNM, + FMAXNM_d = FMAXNM | FP64, + FMINNM = FPDataProcessing2SourceFixed | 0x00007000, + FMINNM_s = FMINNM, + FMINNM_d = FMINNM | FP64, + FNMUL = FPDataProcessing2SourceFixed | 0x00008000, + FNMUL_s = FNMUL, + FNMUL_d = FNMUL | FP64 +}; + +// Floating point data processing 3 source. +enum FPDataProcessing3SourceOp { + FPDataProcessing3SourceFixed = 0x1F000000, + FPDataProcessing3SourceFMask = 0x5F000000, + FPDataProcessing3SourceMask = 0xFFE08000, + FMADD_s = FPDataProcessing3SourceFixed | 0x00000000, + FMSUB_s = FPDataProcessing3SourceFixed | 0x00008000, + FNMADD_s = FPDataProcessing3SourceFixed | 0x00200000, + FNMSUB_s = FPDataProcessing3SourceFixed | 0x00208000, + FMADD_d = FPDataProcessing3SourceFixed | 0x00400000, + FMSUB_d = FPDataProcessing3SourceFixed | 0x00408000, + FNMADD_d = FPDataProcessing3SourceFixed | 0x00600000, + FNMSUB_d = FPDataProcessing3SourceFixed | 0x00608000 +}; + +// Conversion between floating point and integer. +enum FPIntegerConvertOp { + FPIntegerConvertFixed = 0x1E200000, + FPIntegerConvertFMask = 0x5F20FC00, + FPIntegerConvertMask = 0xFFFFFC00, + FCVTNS = FPIntegerConvertFixed | 0x00000000, + FCVTNS_ws = FCVTNS, + FCVTNS_xs = FCVTNS | SixtyFourBits, + FCVTNS_wd = FCVTNS | FP64, + FCVTNS_xd = FCVTNS | SixtyFourBits | FP64, + FCVTNU = FPIntegerConvertFixed | 0x00010000, + FCVTNU_ws = FCVTNU, + FCVTNU_xs = FCVTNU | SixtyFourBits, + FCVTNU_wd = FCVTNU | FP64, + FCVTNU_xd = FCVTNU | SixtyFourBits | FP64, + FCVTPS = FPIntegerConvertFixed | 0x00080000, + FCVTPS_ws = FCVTPS, + FCVTPS_xs = FCVTPS | SixtyFourBits, + FCVTPS_wd = FCVTPS | FP64, + FCVTPS_xd = FCVTPS | SixtyFourBits | FP64, + FCVTPU = FPIntegerConvertFixed | 0x00090000, + FCVTPU_ws = FCVTPU, + FCVTPU_xs = FCVTPU | SixtyFourBits, + FCVTPU_wd = FCVTPU | FP64, + FCVTPU_xd = FCVTPU | SixtyFourBits | FP64, + FCVTMS = FPIntegerConvertFixed | 0x00100000, + FCVTMS_ws = FCVTMS, + FCVTMS_xs = FCVTMS | SixtyFourBits, + FCVTMS_wd = FCVTMS | FP64, + FCVTMS_xd = FCVTMS | SixtyFourBits | FP64, + FCVTMU = FPIntegerConvertFixed | 0x00110000, + FCVTMU_ws = FCVTMU, + FCVTMU_xs = FCVTMU | SixtyFourBits, + FCVTMU_wd = FCVTMU | FP64, + FCVTMU_xd = FCVTMU | SixtyFourBits | FP64, + FCVTZS = FPIntegerConvertFixed | 0x00180000, + FCVTZS_ws = FCVTZS, + FCVTZS_xs = FCVTZS | SixtyFourBits, + FCVTZS_wd = FCVTZS | FP64, + FCVTZS_xd = FCVTZS | SixtyFourBits | FP64, + FCVTZU = FPIntegerConvertFixed | 0x00190000, + FCVTZU_ws = FCVTZU, + FCVTZU_xs = FCVTZU | SixtyFourBits, + FCVTZU_wd = FCVTZU | FP64, + FCVTZU_xd = FCVTZU | SixtyFourBits | FP64, + SCVTF = FPIntegerConvertFixed | 0x00020000, + SCVTF_sw = SCVTF, + SCVTF_sx = SCVTF | SixtyFourBits, + SCVTF_dw = SCVTF | FP64, + SCVTF_dx = SCVTF | SixtyFourBits | FP64, + UCVTF = FPIntegerConvertFixed | 0x00030000, + UCVTF_sw = UCVTF, + UCVTF_sx = UCVTF | SixtyFourBits, + UCVTF_dw = UCVTF | FP64, + UCVTF_dx = UCVTF | SixtyFourBits | FP64, + FCVTAS = FPIntegerConvertFixed | 0x00040000, + FCVTAS_ws = FCVTAS, + FCVTAS_xs = FCVTAS | SixtyFourBits, + FCVTAS_wd = FCVTAS | FP64, + FCVTAS_xd = FCVTAS | SixtyFourBits | FP64, + FCVTAU = FPIntegerConvertFixed | 0x00050000, + FCVTAU_ws = FCVTAU, + FCVTAU_xs = FCVTAU | SixtyFourBits, + FCVTAU_wd = FCVTAU | FP64, + FCVTAU_xd = FCVTAU | SixtyFourBits | FP64, + FMOV_ws = FPIntegerConvertFixed | 0x00060000, + FMOV_sw = FPIntegerConvertFixed | 0x00070000, + FMOV_xd = FMOV_ws | SixtyFourBits | FP64, + FMOV_dx = FMOV_sw | SixtyFourBits | FP64 +}; + +// Conversion between fixed point and floating point. +enum FPFixedPointConvertOp { + FPFixedPointConvertFixed = 0x1E000000, + FPFixedPointConvertFMask = 0x5F200000, + FPFixedPointConvertMask = 0xFFFF0000, + FCVTZS_fixed = FPFixedPointConvertFixed | 0x00180000, + FCVTZS_ws_fixed = FCVTZS_fixed, + FCVTZS_xs_fixed = FCVTZS_fixed | SixtyFourBits, + FCVTZS_wd_fixed = FCVTZS_fixed | FP64, + FCVTZS_xd_fixed = FCVTZS_fixed | SixtyFourBits | FP64, + FCVTZU_fixed = FPFixedPointConvertFixed | 0x00190000, + FCVTZU_ws_fixed = FCVTZU_fixed, + FCVTZU_xs_fixed = FCVTZU_fixed | SixtyFourBits, + FCVTZU_wd_fixed = FCVTZU_fixed | FP64, + FCVTZU_xd_fixed = FCVTZU_fixed | SixtyFourBits | FP64, + SCVTF_fixed = FPFixedPointConvertFixed | 0x00020000, + SCVTF_sw_fixed = SCVTF_fixed, + SCVTF_sx_fixed = SCVTF_fixed | SixtyFourBits, + SCVTF_dw_fixed = SCVTF_fixed | FP64, + SCVTF_dx_fixed = SCVTF_fixed | SixtyFourBits | FP64, + UCVTF_fixed = FPFixedPointConvertFixed | 0x00030000, + UCVTF_sw_fixed = UCVTF_fixed, + UCVTF_sx_fixed = UCVTF_fixed | SixtyFourBits, + UCVTF_dw_fixed = UCVTF_fixed | FP64, + UCVTF_dx_fixed = UCVTF_fixed | SixtyFourBits | FP64 +}; + +// Unimplemented and unallocated instructions. These are defined to make fixed +// bit assertion easier. +enum UnimplementedOp { + UnimplementedFixed = 0x00000000, + UnimplementedFMask = 0x00000000 +}; + +enum UnallocatedOp { + UnallocatedFixed = 0x00000000, + UnallocatedFMask = 0x00000000 +}; + +} // namespace vixl + +#endif // VIXL_A64_CONSTANTS_A64_H_ diff --git a/disas/libvixl/a64/cpu-a64.h b/disas/libvixl/a64/cpu-a64.h new file mode 100644 index 0000000..59b7974 --- /dev/null +++ b/disas/libvixl/a64/cpu-a64.h @@ -0,0 +1,83 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_CPU_A64_H +#define VIXL_CPU_A64_H + +#include "globals.h" +#include "instructions-a64.h" + +namespace vixl { + +class CPU { + public: + // Initialise CPU support. + static void SetUp(); + + // Ensures the data at a given address and with a given size is the same for + // the I and D caches. I and D caches are not automatically coherent on ARM + // so this operation is required before any dynamically generated code can + // safely run. + static void EnsureIAndDCacheCoherency(void *address, size_t length); + + // Handle tagged pointers. + template + static T SetPointerTag(T pointer, uint64_t tag) { + VIXL_ASSERT(is_uintn(kAddressTagWidth, tag)); + + // Use C-style casts to get static_cast behaviour for integral types (T), + // and reinterpret_cast behaviour for other types. + + uint64_t raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); + + raw = (raw & ~kAddressTagMask) | (tag << kAddressTagOffset); + return (T)raw; + } + + template + static uint64_t GetPointerTag(T pointer) { + // Use C-style casts to get static_cast behaviour for integral types (T), + // and reinterpret_cast behaviour for other types. + + uint64_t raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); + + return (raw & kAddressTagMask) >> kAddressTagOffset; + } + + private: + // Return the content of the cache type register. + static uint32_t GetCacheType(); + + // I and D cache line size in bytes. + static unsigned icache_line_size_; + static unsigned dcache_line_size_; +}; + +} // namespace vixl + +#endif // VIXL_CPU_A64_H diff --git a/disas/libvixl/a64/decoder-a64.cc b/disas/libvixl/a64/decoder-a64.cc new file mode 100644 index 0000000..82591ca --- /dev/null +++ b/disas/libvixl/a64/decoder-a64.cc @@ -0,0 +1,707 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "globals.h" +#include "utils.h" +#include "a64/decoder-a64.h" + +namespace vixl { + +void Decoder::DecodeInstruction(const Instruction *instr) { + if (instr->Bits(28, 27) == 0) { + VisitUnallocated(instr); + } else { + switch (instr->Bits(27, 24)) { + // 0: PC relative addressing. + case 0x0: DecodePCRelAddressing(instr); break; + + // 1: Add/sub immediate. + case 0x1: DecodeAddSubImmediate(instr); break; + + // A: Logical shifted register. + // Add/sub with carry. + // Conditional compare register. + // Conditional compare immediate. + // Conditional select. + // Data processing 1 source. + // Data processing 2 source. + // B: Add/sub shifted register. + // Add/sub extended register. + // Data processing 3 source. + case 0xA: + case 0xB: DecodeDataProcessing(instr); break; + + // 2: Logical immediate. + // Move wide immediate. + case 0x2: DecodeLogical(instr); break; + + // 3: Bitfield. + // Extract. + case 0x3: DecodeBitfieldExtract(instr); break; + + // 4: Unconditional branch immediate. + // Exception generation. + // Compare and branch immediate. + // 5: Compare and branch immediate. + // Conditional branch. + // System. + // 6,7: Unconditional branch. + // Test and branch immediate. + case 0x4: + case 0x5: + case 0x6: + case 0x7: DecodeBranchSystemException(instr); break; + + // 8,9: Load/store register pair post-index. + // Load register literal. + // Load/store register unscaled immediate. + // Load/store register immediate post-index. + // Load/store register immediate pre-index. + // Load/store register offset. + // Load/store exclusive. + // C,D: Load/store register pair offset. + // Load/store register pair pre-index. + // Load/store register unsigned immediate. + // Advanced SIMD. + case 0x8: + case 0x9: + case 0xC: + case 0xD: DecodeLoadStore(instr); break; + + // E: FP fixed point conversion. + // FP integer conversion. + // FP data processing 1 source. + // FP compare. + // FP immediate. + // FP data processing 2 source. + // FP conditional compare. + // FP conditional select. + // Advanced SIMD. + // F: FP data processing 3 source. + // Advanced SIMD. + case 0xE: + case 0xF: DecodeFP(instr); break; + } + } +} + +void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { + visitors_.push_back(new_visitor); +} + + +void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { + visitors_.push_front(new_visitor); +} + + +void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor) { + std::list::iterator it; + for (it = visitors_.begin(); it != visitors_.end(); it++) { + if (*it == registered_visitor) { + visitors_.insert(it, new_visitor); + return; + } + } + // We reached the end of the list. The last element must be + // registered_visitor. + VIXL_ASSERT(*it == registered_visitor); + visitors_.insert(it, new_visitor); +} + + +void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor) { + std::list::iterator it; + for (it = visitors_.begin(); it != visitors_.end(); it++) { + if (*it == registered_visitor) { + it++; + visitors_.insert(it, new_visitor); + return; + } + } + // We reached the end of the list. The last element must be + // registered_visitor. + VIXL_ASSERT(*it == registered_visitor); + visitors_.push_back(new_visitor); +} + + +void Decoder::RemoveVisitor(DecoderVisitor* visitor) { + visitors_.remove(visitor); +} + + +void Decoder::DecodePCRelAddressing(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(27, 24) == 0x0); + // We know bit 28 is set, as = 0 is filtered out at the top level + // decode. + VIXL_ASSERT(instr->Bit(28) == 0x1); + VisitPCRelAddressing(instr); +} + + +void Decoder::DecodeBranchSystemException(const Instruction* instr) { + VIXL_ASSERT((instr->Bits(27, 24) == 0x4) || + (instr->Bits(27, 24) == 0x5) || + (instr->Bits(27, 24) == 0x6) || + (instr->Bits(27, 24) == 0x7) ); + + switch (instr->Bits(31, 29)) { + case 0: + case 4: { + VisitUnconditionalBranch(instr); + break; + } + case 1: + case 5: { + if (instr->Bit(25) == 0) { + VisitCompareBranch(instr); + } else { + VisitTestBranch(instr); + } + break; + } + case 2: { + if (instr->Bit(25) == 0) { + if ((instr->Bit(24) == 0x1) || + (instr->Mask(0x01000010) == 0x00000010)) { + VisitUnallocated(instr); + } else { + VisitConditionalBranch(instr); + } + } else { + VisitUnallocated(instr); + } + break; + } + case 6: { + if (instr->Bit(25) == 0) { + if (instr->Bit(24) == 0) { + if ((instr->Bits(4, 2) != 0) || + (instr->Mask(0x00E0001D) == 0x00200001) || + (instr->Mask(0x00E0001D) == 0x00400001) || + (instr->Mask(0x00E0001E) == 0x00200002) || + (instr->Mask(0x00E0001E) == 0x00400002) || + (instr->Mask(0x00E0001C) == 0x00600000) || + (instr->Mask(0x00E0001C) == 0x00800000) || + (instr->Mask(0x00E0001F) == 0x00A00000) || + (instr->Mask(0x00C0001C) == 0x00C00000)) { + VisitUnallocated(instr); + } else { + VisitException(instr); + } + } else { + if (instr->Bits(23, 22) == 0) { + const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0); + if ((instr->Bits(21, 19) == 0x4) || + (masked_003FF0E0 == 0x00033000) || + (masked_003FF0E0 == 0x003FF020) || + (masked_003FF0E0 == 0x003FF060) || + (masked_003FF0E0 == 0x003FF0E0) || + (instr->Mask(0x00388000) == 0x00008000) || + (instr->Mask(0x0038E000) == 0x00000000) || + (instr->Mask(0x0039E000) == 0x00002000) || + (instr->Mask(0x003AE000) == 0x00002000) || + (instr->Mask(0x003CE000) == 0x00042000) || + (instr->Mask(0x003FFFC0) == 0x000320C0) || + (instr->Mask(0x003FF100) == 0x00032100) || + (instr->Mask(0x003FF200) == 0x00032200) || + (instr->Mask(0x003FF400) == 0x00032400) || + (instr->Mask(0x003FF800) == 0x00032800) || + (instr->Mask(0x0038F000) == 0x00005000) || + (instr->Mask(0x0038E000) == 0x00006000)) { + VisitUnallocated(instr); + } else { + VisitSystem(instr); + } + } else { + VisitUnallocated(instr); + } + } + } else { + if ((instr->Bit(24) == 0x1) || + (instr->Bits(20, 16) != 0x1F) || + (instr->Bits(15, 10) != 0) || + (instr->Bits(4, 0) != 0) || + (instr->Bits(24, 21) == 0x3) || + (instr->Bits(24, 22) == 0x3)) { + VisitUnallocated(instr); + } else { + VisitUnconditionalBranchToRegister(instr); + } + } + break; + } + case 3: + case 7: { + VisitUnallocated(instr); + break; + } + } +} + + +void Decoder::DecodeLoadStore(const Instruction* instr) { + VIXL_ASSERT((instr->Bits(27, 24) == 0x8) || + (instr->Bits(27, 24) == 0x9) || + (instr->Bits(27, 24) == 0xC) || + (instr->Bits(27, 24) == 0xD) ); + + if (instr->Bit(24) == 0) { + if (instr->Bit(28) == 0) { + if (instr->Bit(29) == 0) { + if (instr->Bit(26) == 0) { + VisitLoadStoreExclusive(instr); + } else { + DecodeAdvSIMDLoadStore(instr); + } + } else { + if ((instr->Bits(31, 30) == 0x3) || + (instr->Mask(0xC4400000) == 0x40000000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + if (instr->Mask(0xC4400000) == 0xC0400000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePairNonTemporal(instr); + } + } else { + VisitLoadStorePairPostIndex(instr); + } + } + } + } else { + if (instr->Bit(29) == 0) { + if (instr->Mask(0xC4000000) == 0xC4000000) { + VisitUnallocated(instr); + } else { + VisitLoadLiteral(instr); + } + } else { + if ((instr->Mask(0x84C00000) == 0x80C00000) || + (instr->Mask(0x44800000) == 0x44800000) || + (instr->Mask(0x84800000) == 0x84800000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(21) == 0) { + switch (instr->Bits(11, 10)) { + case 0: { + VisitLoadStoreUnscaledOffset(instr); + break; + } + case 1: { + if (instr->Mask(0xC4C00000) == 0xC0800000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePostIndex(instr); + } + break; + } + case 2: { + // TODO: VisitLoadStoreRegisterOffsetUnpriv. + VisitUnimplemented(instr); + break; + } + case 3: { + if (instr->Mask(0xC4C00000) == 0xC0800000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePreIndex(instr); + } + break; + } + } + } else { + if (instr->Bits(11, 10) == 0x2) { + if (instr->Bit(14) == 0) { + VisitUnallocated(instr); + } else { + VisitLoadStoreRegisterOffset(instr); + } + } else { + VisitUnallocated(instr); + } + } + } + } + } + } else { + if (instr->Bit(28) == 0) { + if (instr->Bit(29) == 0) { + VisitUnallocated(instr); + } else { + if ((instr->Bits(31, 30) == 0x3) || + (instr->Mask(0xC4400000) == 0x40000000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + VisitLoadStorePairOffset(instr); + } else { + VisitLoadStorePairPreIndex(instr); + } + } + } + } else { + if (instr->Bit(29) == 0) { + VisitUnallocated(instr); + } else { + if ((instr->Mask(0x84C00000) == 0x80C00000) || + (instr->Mask(0x44800000) == 0x44800000) || + (instr->Mask(0x84800000) == 0x84800000)) { + VisitUnallocated(instr); + } else { + VisitLoadStoreUnsignedOffset(instr); + } + } + } + } +} + + +void Decoder::DecodeLogical(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(27, 24) == 0x2); + + if (instr->Mask(0x80400000) == 0x00400000) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + VisitLogicalImmediate(instr); + } else { + if (instr->Bits(30, 29) == 0x1) { + VisitUnallocated(instr); + } else { + VisitMoveWideImmediate(instr); + } + } + } +} + + +void Decoder::DecodeBitfieldExtract(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(27, 24) == 0x3); + + if ((instr->Mask(0x80400000) == 0x80000000) || + (instr->Mask(0x80400000) == 0x00400000) || + (instr->Mask(0x80008000) == 0x00008000)) { + VisitUnallocated(instr); + } else if (instr->Bit(23) == 0) { + if ((instr->Mask(0x80200000) == 0x00200000) || + (instr->Mask(0x60000000) == 0x60000000)) { + VisitUnallocated(instr); + } else { + VisitBitfield(instr); + } + } else { + if ((instr->Mask(0x60200000) == 0x00200000) || + (instr->Mask(0x60000000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitExtract(instr); + } + } +} + + +void Decoder::DecodeAddSubImmediate(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(27, 24) == 0x1); + if (instr->Bit(23) == 1) { + VisitUnallocated(instr); + } else { + VisitAddSubImmediate(instr); + } +} + + +void Decoder::DecodeDataProcessing(const Instruction* instr) { + VIXL_ASSERT((instr->Bits(27, 24) == 0xA) || + (instr->Bits(27, 24) == 0xB)); + + if (instr->Bit(24) == 0) { + if (instr->Bit(28) == 0) { + if (instr->Mask(0x80008000) == 0x00008000) { + VisitUnallocated(instr); + } else { + VisitLogicalShifted(instr); + } + } else { + switch (instr->Bits(23, 21)) { + case 0: { + if (instr->Mask(0x0000FC00) != 0) { + VisitUnallocated(instr); + } else { + VisitAddSubWithCarry(instr); + } + break; + } + case 2: { + if ((instr->Bit(29) == 0) || + (instr->Mask(0x00000410) != 0)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(11) == 0) { + VisitConditionalCompareRegister(instr); + } else { + VisitConditionalCompareImmediate(instr); + } + } + break; + } + case 4: { + if (instr->Mask(0x20000800) != 0x00000000) { + VisitUnallocated(instr); + } else { + VisitConditionalSelect(instr); + } + break; + } + case 6: { + if (instr->Bit(29) == 0x1) { + VisitUnallocated(instr); + } else { + if (instr->Bit(30) == 0) { + if ((instr->Bit(15) == 0x1) || + (instr->Bits(15, 11) == 0) || + (instr->Bits(15, 12) == 0x1) || + (instr->Bits(15, 12) == 0x3) || + (instr->Bits(15, 13) == 0x3) || + (instr->Mask(0x8000EC00) == 0x00004C00) || + (instr->Mask(0x8000E800) == 0x80004000) || + (instr->Mask(0x8000E400) == 0x80004000)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing2Source(instr); + } + } else { + if ((instr->Bit(13) == 1) || + (instr->Bits(20, 16) != 0) || + (instr->Bits(15, 14) != 0) || + (instr->Mask(0xA01FFC00) == 0x00000C00) || + (instr->Mask(0x201FF800) == 0x00001800)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing1Source(instr); + } + } + break; + } + } + case 1: + case 3: + case 5: + case 7: VisitUnallocated(instr); break; + } + } + } else { + if (instr->Bit(28) == 0) { + if (instr->Bit(21) == 0) { + if ((instr->Bits(23, 22) == 0x3) || + (instr->Mask(0x80008000) == 0x00008000)) { + VisitUnallocated(instr); + } else { + VisitAddSubShifted(instr); + } + } else { + if ((instr->Mask(0x00C00000) != 0x00000000) || + (instr->Mask(0x00001400) == 0x00001400) || + (instr->Mask(0x00001800) == 0x00001800)) { + VisitUnallocated(instr); + } else { + VisitAddSubExtended(instr); + } + } + } else { + if ((instr->Bit(30) == 0x1) || + (instr->Bits(30, 29) == 0x1) || + (instr->Mask(0xE0600000) == 0x00200000) || + (instr->Mask(0xE0608000) == 0x00400000) || + (instr->Mask(0x60608000) == 0x00408000) || + (instr->Mask(0x60E00000) == 0x00E00000) || + (instr->Mask(0x60E00000) == 0x00800000) || + (instr->Mask(0x60E00000) == 0x00600000)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing3Source(instr); + } + } + } +} + + +void Decoder::DecodeFP(const Instruction* instr) { + VIXL_ASSERT((instr->Bits(27, 24) == 0xE) || + (instr->Bits(27, 24) == 0xF)); + + if (instr->Bit(28) == 0) { + DecodeAdvSIMDDataProcessing(instr); + } else { + if (instr->Bit(29) == 1) { + VisitUnallocated(instr); + } else { + if (instr->Bits(31, 30) == 0x3) { + VisitUnallocated(instr); + } else if (instr->Bits(31, 30) == 0x1) { + DecodeAdvSIMDDataProcessing(instr); + } else { + if (instr->Bit(24) == 0) { + if (instr->Bit(21) == 0) { + if ((instr->Bit(23) == 1) || + (instr->Bit(18) == 1) || + (instr->Mask(0x80008000) == 0x00000000) || + (instr->Mask(0x000E0000) == 0x00000000) || + (instr->Mask(0x000E0000) == 0x000A0000) || + (instr->Mask(0x00160000) == 0x00000000) || + (instr->Mask(0x00160000) == 0x00120000)) { + VisitUnallocated(instr); + } else { + VisitFPFixedPointConvert(instr); + } + } else { + if (instr->Bits(15, 10) == 32) { + VisitUnallocated(instr); + } else if (instr->Bits(15, 10) == 0) { + if ((instr->Bits(23, 22) == 0x3) || + (instr->Mask(0x000E0000) == 0x000A0000) || + (instr->Mask(0x000E0000) == 0x000C0000) || + (instr->Mask(0x00160000) == 0x00120000) || + (instr->Mask(0x00160000) == 0x00140000) || + (instr->Mask(0x20C40000) == 0x00800000) || + (instr->Mask(0x20C60000) == 0x00840000) || + (instr->Mask(0xA0C60000) == 0x80060000) || + (instr->Mask(0xA0C60000) == 0x00860000) || + (instr->Mask(0xA0C60000) == 0x00460000) || + (instr->Mask(0xA0CE0000) == 0x80860000) || + (instr->Mask(0xA0CE0000) == 0x804E0000) || + (instr->Mask(0xA0CE0000) == 0x000E0000) || + (instr->Mask(0xA0D60000) == 0x00160000) || + (instr->Mask(0xA0D60000) == 0x80560000) || + (instr->Mask(0xA0D60000) == 0x80960000)) { + VisitUnallocated(instr); + } else { + VisitFPIntegerConvert(instr); + } + } else if (instr->Bits(14, 10) == 16) { + const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000); + if ((instr->Mask(0x80180000) != 0) || + (masked_A0DF8000 == 0x00020000) || + (masked_A0DF8000 == 0x00030000) || + (masked_A0DF8000 == 0x00068000) || + (masked_A0DF8000 == 0x00428000) || + (masked_A0DF8000 == 0x00430000) || + (masked_A0DF8000 == 0x00468000) || + (instr->Mask(0xA0D80000) == 0x00800000) || + (instr->Mask(0xA0DE0000) == 0x00C00000) || + (instr->Mask(0xA0DF0000) == 0x00C30000) || + (instr->Mask(0xA0DC0000) == 0x00C40000)) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing1Source(instr); + } + } else if (instr->Bits(13, 10) == 8) { + if ((instr->Bits(15, 14) != 0) || + (instr->Bits(2, 0) != 0) || + (instr->Mask(0x80800000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitFPCompare(instr); + } + } else if (instr->Bits(12, 10) == 4) { + if ((instr->Bits(9, 5) != 0) || + (instr->Mask(0x80800000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitFPImmediate(instr); + } + } else { + if (instr->Mask(0x80800000) != 0x00000000) { + VisitUnallocated(instr); + } else { + switch (instr->Bits(11, 10)) { + case 1: { + VisitFPConditionalCompare(instr); + break; + } + case 2: { + if ((instr->Bits(15, 14) == 0x3) || + (instr->Mask(0x00009000) == 0x00009000) || + (instr->Mask(0x0000A000) == 0x0000A000)) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing2Source(instr); + } + break; + } + case 3: { + VisitFPConditionalSelect(instr); + break; + } + default: VIXL_UNREACHABLE(); + } + } + } + } + } else { + // Bit 30 == 1 has been handled earlier. + VIXL_ASSERT(instr->Bit(30) == 0); + if (instr->Mask(0xA0800000) != 0) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing3Source(instr); + } + } + } + } + } +} + + +void Decoder::DecodeAdvSIMDLoadStore(const Instruction* instr) { + // TODO: Implement Advanced SIMD load/store instruction decode. + VIXL_ASSERT(instr->Bits(29, 25) == 0x6); + VisitUnimplemented(instr); +} + + +void Decoder::DecodeAdvSIMDDataProcessing(const Instruction* instr) { + // TODO: Implement Advanced SIMD data processing instruction decode. + VIXL_ASSERT(instr->Bits(27, 25) == 0x7); + VisitUnimplemented(instr); +} + + +#define DEFINE_VISITOR_CALLERS(A) \ + void Decoder::Visit##A(const Instruction *instr) { \ + VIXL_ASSERT(instr->Mask(A##FMask) == A##Fixed); \ + std::list::iterator it; \ + for (it = visitors_.begin(); it != visitors_.end(); it++) { \ + (*it)->Visit##A(instr); \ + } \ + } +VISITOR_LIST(DEFINE_VISITOR_CALLERS) +#undef DEFINE_VISITOR_CALLERS +} // namespace vixl diff --git a/disas/libvixl/a64/decoder-a64.h b/disas/libvixl/a64/decoder-a64.h new file mode 100644 index 0000000..fd08d6c --- /dev/null +++ b/disas/libvixl/a64/decoder-a64.h @@ -0,0 +1,239 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_DECODER_A64_H_ +#define VIXL_A64_DECODER_A64_H_ + +#include + +#include "globals.h" +#include "a64/instructions-a64.h" + + +// List macro containing all visitors needed by the decoder class. + +#define VISITOR_LIST(V) \ + V(PCRelAddressing) \ + V(AddSubImmediate) \ + V(LogicalImmediate) \ + V(MoveWideImmediate) \ + V(Bitfield) \ + V(Extract) \ + V(UnconditionalBranch) \ + V(UnconditionalBranchToRegister) \ + V(CompareBranch) \ + V(TestBranch) \ + V(ConditionalBranch) \ + V(System) \ + V(Exception) \ + V(LoadStorePairPostIndex) \ + V(LoadStorePairOffset) \ + V(LoadStorePairPreIndex) \ + V(LoadStorePairNonTemporal) \ + V(LoadLiteral) \ + V(LoadStoreUnscaledOffset) \ + V(LoadStorePostIndex) \ + V(LoadStorePreIndex) \ + V(LoadStoreRegisterOffset) \ + V(LoadStoreUnsignedOffset) \ + V(LoadStoreExclusive) \ + V(LogicalShifted) \ + V(AddSubShifted) \ + V(AddSubExtended) \ + V(AddSubWithCarry) \ + V(ConditionalCompareRegister) \ + V(ConditionalCompareImmediate) \ + V(ConditionalSelect) \ + V(DataProcessing1Source) \ + V(DataProcessing2Source) \ + V(DataProcessing3Source) \ + V(FPCompare) \ + V(FPConditionalCompare) \ + V(FPConditionalSelect) \ + V(FPImmediate) \ + V(FPDataProcessing1Source) \ + V(FPDataProcessing2Source) \ + V(FPDataProcessing3Source) \ + V(FPIntegerConvert) \ + V(FPFixedPointConvert) \ + V(Unallocated) \ + V(Unimplemented) + +namespace vixl { + +// The Visitor interface. Disassembler and simulator (and other tools) +// must provide implementations for all of these functions. +class DecoderVisitor { + public: + enum VisitorConstness { + kConstVisitor, + kNonConstVisitor + }; + explicit DecoderVisitor(VisitorConstness constness = kConstVisitor) + : constness_(constness) {} + + virtual ~DecoderVisitor() {} + + #define DECLARE(A) virtual void Visit##A(const Instruction* instr) = 0; + VISITOR_LIST(DECLARE) + #undef DECLARE + + bool IsConstVisitor() const { return constness_ == kConstVisitor; } + Instruction* MutableInstruction(const Instruction* instr) { + VIXL_ASSERT(!IsConstVisitor()); + return const_cast(instr); + } + + private: + const VisitorConstness constness_; +}; + + +class Decoder { + public: + Decoder() {} + + // Top-level wrappers around the actual decoding function. + void Decode(const Instruction* instr) { + std::list::iterator it; + for (it = visitors_.begin(); it != visitors_.end(); it++) { + VIXL_ASSERT((*it)->IsConstVisitor()); + } + DecodeInstruction(instr); + } + void Decode(Instruction* instr) { + DecodeInstruction(const_cast(instr)); + } + + // Register a new visitor class with the decoder. + // Decode() will call the corresponding visitor method from all registered + // visitor classes when decoding reaches the leaf node of the instruction + // decode tree. + // Visitors are called in order. + // A visitor can be registered multiple times. + // + // d.AppendVisitor(V1); + // d.AppendVisitor(V2); + // d.PrependVisitor(V2); + // d.AppendVisitor(V3); + // + // d.Decode(i); + // + // will call in order visitor methods in V2, V1, V2, V3. + void AppendVisitor(DecoderVisitor* visitor); + void PrependVisitor(DecoderVisitor* visitor); + // These helpers register `new_visitor` before or after the first instance of + // `registered_visiter` in the list. + // So if + // V1, V2, V1, V2 + // are registered in this order in the decoder, calls to + // d.InsertVisitorAfter(V3, V1); + // d.InsertVisitorBefore(V4, V2); + // will yield the order + // V1, V3, V4, V2, V1, V2 + // + // For more complex modifications of the order of registered visitors, one can + // directly access and modify the list of visitors via the `visitors()' + // accessor. + void InsertVisitorBefore(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor); + void InsertVisitorAfter(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor); + + // Remove all instances of a previously registered visitor class from the list + // of visitors stored by the decoder. + void RemoveVisitor(DecoderVisitor* visitor); + + #define DECLARE(A) void Visit##A(const Instruction* instr); + VISITOR_LIST(DECLARE) + #undef DECLARE + + + std::list* visitors() { return &visitors_; } + + private: + // Decodes an instruction and calls the visitor functions registered with the + // Decoder class. + void DecodeInstruction(const Instruction* instr); + + // Decode the PC relative addressing instruction, and call the corresponding + // visitors. + // On entry, instruction bits 27:24 = 0x0. + void DecodePCRelAddressing(const Instruction* instr); + + // Decode the add/subtract immediate instruction, and call the correspoding + // visitors. + // On entry, instruction bits 27:24 = 0x1. + void DecodeAddSubImmediate(const Instruction* instr); + + // Decode the branch, system command, and exception generation parts of + // the instruction tree, and call the corresponding visitors. + // On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}. + void DecodeBranchSystemException(const Instruction* instr); + + // Decode the load and store parts of the instruction tree, and call + // the corresponding visitors. + // On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}. + void DecodeLoadStore(const Instruction* instr); + + // Decode the logical immediate and move wide immediate parts of the + // instruction tree, and call the corresponding visitors. + // On entry, instruction bits 27:24 = 0x2. + void DecodeLogical(const Instruction* instr); + + // Decode the bitfield and extraction parts of the instruction tree, + // and call the corresponding visitors. + // On entry, instruction bits 27:24 = 0x3. + void DecodeBitfieldExtract(const Instruction* instr); + + // Decode the data processing parts of the instruction tree, and call the + // corresponding visitors. + // On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}. + void DecodeDataProcessing(const Instruction* instr); + + // Decode the floating point parts of the instruction tree, and call the + // corresponding visitors. + // On entry, instruction bits 27:24 = {0xE, 0xF}. + void DecodeFP(const Instruction* instr); + + // Decode the Advanced SIMD (NEON) load/store part of the instruction tree, + // and call the corresponding visitors. + // On entry, instruction bits 29:25 = 0x6. + void DecodeAdvSIMDLoadStore(const Instruction* instr); + + // Decode the Advanced SIMD (NEON) data processing part of the instruction + // tree, and call the corresponding visitors. + // On entry, instruction bits 27:25 = 0x7. + void DecodeAdvSIMDDataProcessing(const Instruction* instr); + + private: + // Visitors are registered in a list. + std::list visitors_; +}; + +} // namespace vixl + +#endif // VIXL_A64_DECODER_A64_H_ diff --git a/disas/libvixl/a64/disasm-a64.cc b/disas/libvixl/a64/disasm-a64.cc new file mode 100644 index 0000000..f7bc246 --- /dev/null +++ b/disas/libvixl/a64/disasm-a64.cc @@ -0,0 +1,1954 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "a64/disasm-a64.h" + +namespace vixl { + +Disassembler::Disassembler() { + buffer_size_ = 256; + buffer_ = reinterpret_cast(malloc(buffer_size_)); + buffer_pos_ = 0; + own_buffer_ = true; + code_address_offset_ = 0; +} + + +Disassembler::Disassembler(char* text_buffer, int buffer_size) { + buffer_size_ = buffer_size; + buffer_ = text_buffer; + buffer_pos_ = 0; + own_buffer_ = false; + code_address_offset_ = 0; +} + + +Disassembler::~Disassembler() { + if (own_buffer_) { + free(buffer_); + } +} + + +char* Disassembler::GetOutput() { + return buffer_; +} + + +void Disassembler::VisitAddSubImmediate(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && + (instr->ImmAddSub() == 0) ? true : false; + const char *mnemonic = ""; + const char *form = "'Rds, 'Rns, 'IAddSub"; + const char *form_cmp = "'Rns, 'IAddSub"; + const char *form_mov = "'Rds, 'Rns"; + + switch (instr->Mask(AddSubImmediateMask)) { + case ADD_w_imm: + case ADD_x_imm: { + mnemonic = "add"; + if (stack_op) { + mnemonic = "mov"; + form = form_mov; + } + break; + } + case ADDS_w_imm: + case ADDS_x_imm: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_imm: + case SUB_x_imm: mnemonic = "sub"; break; + case SUBS_w_imm: + case SUBS_x_imm: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubShifted(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm'HDP"; + const char *form_cmp = "'Rn, 'Rm'HDP"; + const char *form_neg = "'Rd, 'Rm'HDP"; + + switch (instr->Mask(AddSubShiftedMask)) { + case ADD_w_shift: + case ADD_x_shift: mnemonic = "add"; break; + case ADDS_w_shift: + case ADDS_x_shift: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_shift: + case SUB_x_shift: { + mnemonic = "sub"; + if (rn_is_zr) { + mnemonic = "neg"; + form = form_neg; + } + break; + } + case SUBS_w_shift: + case SUBS_x_shift: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } else if (rn_is_zr) { + mnemonic = "negs"; + form = form_neg; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubExtended(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + const char *mnemonic = ""; + Extend mode = static_cast(instr->ExtendMode()); + const char *form = ((mode == UXTX) || (mode == SXTX)) ? + "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; + const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? + "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; + + switch (instr->Mask(AddSubExtendedMask)) { + case ADD_w_ext: + case ADD_x_ext: mnemonic = "add"; break; + case ADDS_w_ext: + case ADDS_x_ext: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_ext: + case SUB_x_ext: mnemonic = "sub"; break; + case SUBS_w_ext: + case SUBS_x_ext: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubWithCarry(const Instruction* instr) { + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm"; + const char *form_neg = "'Rd, 'Rm"; + + switch (instr->Mask(AddSubWithCarryMask)) { + case ADC_w: + case ADC_x: mnemonic = "adc"; break; + case ADCS_w: + case ADCS_x: mnemonic = "adcs"; break; + case SBC_w: + case SBC_x: { + mnemonic = "sbc"; + if (rn_is_zr) { + mnemonic = "ngc"; + form = form_neg; + } + break; + } + case SBCS_w: + case SBCS_x: { + mnemonic = "sbcs"; + if (rn_is_zr) { + mnemonic = "ngcs"; + form = form_neg; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLogicalImmediate(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rds, 'Rn, 'ITri"; + + if (instr->ImmLogical() == 0) { + // The immediate encoded in the instruction is not in the expected format. + Format(instr, "unallocated", "(LogicalImmediate)"); + return; + } + + switch (instr->Mask(LogicalImmediateMask)) { + case AND_w_imm: + case AND_x_imm: mnemonic = "and"; break; + case ORR_w_imm: + case ORR_x_imm: { + mnemonic = "orr"; + unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize + : kWRegSize; + if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { + mnemonic = "mov"; + form = "'Rds, 'ITri"; + } + break; + } + case EOR_w_imm: + case EOR_x_imm: mnemonic = "eor"; break; + case ANDS_w_imm: + case ANDS_x_imm: { + mnemonic = "ands"; + if (rd_is_zr) { + mnemonic = "tst"; + form = "'Rn, 'ITri"; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { + VIXL_ASSERT((reg_size == kXRegSize) || + ((reg_size == kWRegSize) && (value <= 0xffffffff))); + + // Test for movz: 16 bits set at positions 0, 16, 32 or 48. + if (((value & UINT64_C(0xffffffffffff0000)) == 0) || + ((value & UINT64_C(0xffffffff0000ffff)) == 0) || + ((value & UINT64_C(0xffff0000ffffffff)) == 0) || + ((value & UINT64_C(0x0000ffffffffffff)) == 0)) { + return true; + } + + // Test for movn: NOT(16 bits set at positions 0, 16, 32 or 48). + if ((reg_size == kXRegSize) && + (((~value & UINT64_C(0xffffffffffff0000)) == 0) || + ((~value & UINT64_C(0xffffffff0000ffff)) == 0) || + ((~value & UINT64_C(0xffff0000ffffffff)) == 0) || + ((~value & UINT64_C(0x0000ffffffffffff)) == 0))) { + return true; + } + if ((reg_size == kWRegSize) && + (((value & 0xffff0000) == 0xffff0000) || + ((value & 0x0000ffff) == 0x0000ffff))) { + return true; + } + return false; +} + + +void Disassembler::VisitLogicalShifted(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm'HLo"; + + switch (instr->Mask(LogicalShiftedMask)) { + case AND_w: + case AND_x: mnemonic = "and"; break; + case BIC_w: + case BIC_x: mnemonic = "bic"; break; + case EOR_w: + case EOR_x: mnemonic = "eor"; break; + case EON_w: + case EON_x: mnemonic = "eon"; break; + case BICS_w: + case BICS_x: mnemonic = "bics"; break; + case ANDS_w: + case ANDS_x: { + mnemonic = "ands"; + if (rd_is_zr) { + mnemonic = "tst"; + form = "'Rn, 'Rm'HLo"; + } + break; + } + case ORR_w: + case ORR_x: { + mnemonic = "orr"; + if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { + mnemonic = "mov"; + form = "'Rd, 'Rm"; + } + break; + } + case ORN_w: + case ORN_x: { + mnemonic = "orn"; + if (rn_is_zr) { + mnemonic = "mvn"; + form = "'Rd, 'Rm'HLo"; + } + break; + } + default: VIXL_UNREACHABLE(); + } + + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalCompareRegister(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; + + switch (instr->Mask(ConditionalCompareRegisterMask)) { + case CCMN_w: + case CCMN_x: mnemonic = "ccmn"; break; + case CCMP_w: + case CCMP_x: mnemonic = "ccmp"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalCompareImmediate(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; + + switch (instr->Mask(ConditionalCompareImmediateMask)) { + case CCMN_w_imm: + case CCMN_x_imm: mnemonic = "ccmn"; break; + case CCMP_w_imm: + case CCMP_x_imm: mnemonic = "ccmp"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalSelect(const Instruction* instr) { + bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); + bool rn_is_rm = (instr->Rn() == instr->Rm()); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; + const char *form_test = "'Rd, 'CInv"; + const char *form_update = "'Rd, 'Rn, 'CInv"; + + Condition cond = static_cast(instr->Condition()); + bool invertible_cond = (cond != al) && (cond != nv); + + switch (instr->Mask(ConditionalSelectMask)) { + case CSEL_w: + case CSEL_x: mnemonic = "csel"; break; + case CSINC_w: + case CSINC_x: { + mnemonic = "csinc"; + if (rnm_is_zr && invertible_cond) { + mnemonic = "cset"; + form = form_test; + } else if (rn_is_rm && invertible_cond) { + mnemonic = "cinc"; + form = form_update; + } + break; + } + case CSINV_w: + case CSINV_x: { + mnemonic = "csinv"; + if (rnm_is_zr && invertible_cond) { + mnemonic = "csetm"; + form = form_test; + } else if (rn_is_rm && invertible_cond) { + mnemonic = "cinv"; + form = form_update; + } + break; + } + case CSNEG_w: + case CSNEG_x: { + mnemonic = "csneg"; + if (rn_is_rm && invertible_cond) { + mnemonic = "cneg"; + form = form_update; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitBitfield(const Instruction* instr) { + unsigned s = instr->ImmS(); + unsigned r = instr->ImmR(); + unsigned rd_size_minus_1 = + ((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1; + const char *mnemonic = ""; + const char *form = ""; + const char *form_shift_right = "'Rd, 'Rn, 'IBr"; + const char *form_extend = "'Rd, 'Wn"; + const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; + const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; + const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; + + switch (instr->Mask(BitfieldMask)) { + case SBFM_w: + case SBFM_x: { + mnemonic = "sbfx"; + form = form_bfx; + if (r == 0) { + form = form_extend; + if (s == 7) { + mnemonic = "sxtb"; + } else if (s == 15) { + mnemonic = "sxth"; + } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { + mnemonic = "sxtw"; + } else { + form = form_bfx; + } + } else if (s == rd_size_minus_1) { + mnemonic = "asr"; + form = form_shift_right; + } else if (s < r) { + mnemonic = "sbfiz"; + form = form_bfiz; + } + break; + } + case UBFM_w: + case UBFM_x: { + mnemonic = "ubfx"; + form = form_bfx; + if (r == 0) { + form = form_extend; + if (s == 7) { + mnemonic = "uxtb"; + } else if (s == 15) { + mnemonic = "uxth"; + } else { + form = form_bfx; + } + } + if (s == rd_size_minus_1) { + mnemonic = "lsr"; + form = form_shift_right; + } else if (r == s + 1) { + mnemonic = "lsl"; + form = form_lsl; + } else if (s < r) { + mnemonic = "ubfiz"; + form = form_bfiz; + } + break; + } + case BFM_w: + case BFM_x: { + mnemonic = "bfxil"; + form = form_bfx; + if (s < r) { + mnemonic = "bfi"; + form = form_bfiz; + } + } + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitExtract(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; + + switch (instr->Mask(ExtractMask)) { + case EXTR_w: + case EXTR_x: { + if (instr->Rn() == instr->Rm()) { + mnemonic = "ror"; + form = "'Rd, 'Rn, 'IExtract"; + } else { + mnemonic = "extr"; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitPCRelAddressing(const Instruction* instr) { + switch (instr->Mask(PCRelAddressingMask)) { + case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; + case ADRP: Format(instr, "adrp", "'Xd, 'AddrPCRelPage"); break; + default: Format(instr, "unimplemented", "(PCRelAddressing)"); + } +} + + +void Disassembler::VisitConditionalBranch(const Instruction* instr) { + switch (instr->Mask(ConditionalBranchMask)) { + case B_cond: Format(instr, "b.'CBrn", "'BImmCond"); break; + default: VIXL_UNREACHABLE(); + } +} + + +void Disassembler::VisitUnconditionalBranchToRegister( + const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Xn"; + + switch (instr->Mask(UnconditionalBranchToRegisterMask)) { + case BR: mnemonic = "br"; break; + case BLR: mnemonic = "blr"; break; + case RET: { + mnemonic = "ret"; + if (instr->Rn() == kLinkRegCode) { + form = NULL; + } + break; + } + default: form = "(UnconditionalBranchToRegister)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitUnconditionalBranch(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'BImmUncn"; + + switch (instr->Mask(UnconditionalBranchMask)) { + case B: mnemonic = "b"; break; + case BL: mnemonic = "bl"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing1Source(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn"; + + switch (instr->Mask(DataProcessing1SourceMask)) { + #define FORMAT(A, B) \ + case A##_w: \ + case A##_x: mnemonic = B; break; + FORMAT(RBIT, "rbit"); + FORMAT(REV16, "rev16"); + FORMAT(REV, "rev"); + FORMAT(CLZ, "clz"); + FORMAT(CLS, "cls"); + #undef FORMAT + case REV32_x: mnemonic = "rev32"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing2Source(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Rd, 'Rn, 'Rm"; + + switch (instr->Mask(DataProcessing2SourceMask)) { + #define FORMAT(A, B) \ + case A##_w: \ + case A##_x: mnemonic = B; break; + FORMAT(UDIV, "udiv"); + FORMAT(SDIV, "sdiv"); + FORMAT(LSLV, "lsl"); + FORMAT(LSRV, "lsr"); + FORMAT(ASRV, "asr"); + FORMAT(RORV, "ror"); + #undef FORMAT + default: form = "(DataProcessing2Source)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing3Source(const Instruction* instr) { + bool ra_is_zr = RaIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; + const char *form_rrr = "'Rd, 'Rn, 'Rm"; + const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; + const char *form_xww = "'Xd, 'Wn, 'Wm"; + const char *form_xxx = "'Xd, 'Xn, 'Xm"; + + switch (instr->Mask(DataProcessing3SourceMask)) { + case MADD_w: + case MADD_x: { + mnemonic = "madd"; + form = form_rrrr; + if (ra_is_zr) { + mnemonic = "mul"; + form = form_rrr; + } + break; + } + case MSUB_w: + case MSUB_x: { + mnemonic = "msub"; + form = form_rrrr; + if (ra_is_zr) { + mnemonic = "mneg"; + form = form_rrr; + } + break; + } + case SMADDL_x: { + mnemonic = "smaddl"; + if (ra_is_zr) { + mnemonic = "smull"; + form = form_xww; + } + break; + } + case SMSUBL_x: { + mnemonic = "smsubl"; + if (ra_is_zr) { + mnemonic = "smnegl"; + form = form_xww; + } + break; + } + case UMADDL_x: { + mnemonic = "umaddl"; + if (ra_is_zr) { + mnemonic = "umull"; + form = form_xww; + } + break; + } + case UMSUBL_x: { + mnemonic = "umsubl"; + if (ra_is_zr) { + mnemonic = "umnegl"; + form = form_xww; + } + break; + } + case SMULH_x: { + mnemonic = "smulh"; + form = form_xxx; + break; + } + case UMULH_x: { + mnemonic = "umulh"; + form = form_xxx; + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitCompareBranch(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rt, 'BImmCmpa"; + + switch (instr->Mask(CompareBranchMask)) { + case CBZ_w: + case CBZ_x: mnemonic = "cbz"; break; + case CBNZ_w: + case CBNZ_x: mnemonic = "cbnz"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitTestBranch(const Instruction* instr) { + const char *mnemonic = ""; + // If the top bit of the immediate is clear, the tested register is + // disassembled as Wt, otherwise Xt. As the top bit of the immediate is + // encoded in bit 31 of the instruction, we can reuse the Rt form, which + // uses bit 31 (normally "sf") to choose the register size. + const char *form = "'Rt, 'IS, 'BImmTest"; + + switch (instr->Mask(TestBranchMask)) { + case TBZ: mnemonic = "tbz"; break; + case TBNZ: mnemonic = "tbnz"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitMoveWideImmediate(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'IMoveImm"; + + // Print the shift separately for movk, to make it clear which half word will + // be overwritten. Movn and movz print the computed immediate, which includes + // shift calculation. + switch (instr->Mask(MoveWideImmediateMask)) { + case MOVN_w: + case MOVN_x: + if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) { + if ((instr->SixtyFourBits() == 0) && (instr->ImmMoveWide() == 0xffff)) { + mnemonic = "movn"; + } else { + mnemonic = "mov"; + form = "'Rd, 'IMoveNeg"; + } + } else { + mnemonic = "movn"; + } + break; + case MOVZ_w: + case MOVZ_x: + if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) + mnemonic = "mov"; + else + mnemonic = "movz"; + break; + case MOVK_w: + case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +#define LOAD_STORE_LIST(V) \ + V(STRB_w, "strb", "'Wt") \ + V(STRH_w, "strh", "'Wt") \ + V(STR_w, "str", "'Wt") \ + V(STR_x, "str", "'Xt") \ + V(LDRB_w, "ldrb", "'Wt") \ + V(LDRH_w, "ldrh", "'Wt") \ + V(LDR_w, "ldr", "'Wt") \ + V(LDR_x, "ldr", "'Xt") \ + V(LDRSB_x, "ldrsb", "'Xt") \ + V(LDRSH_x, "ldrsh", "'Xt") \ + V(LDRSW_x, "ldrsw", "'Xt") \ + V(LDRSB_w, "ldrsb", "'Wt") \ + V(LDRSH_w, "ldrsh", "'Wt") \ + V(STR_s, "str", "'St") \ + V(STR_d, "str", "'Dt") \ + V(LDR_s, "ldr", "'St") \ + V(LDR_d, "ldr", "'Dt") + +void Disassembler::VisitLoadStorePreIndex(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePreIndex)"; + + switch (instr->Mask(LoadStorePreIndexMask)) { + #define LS_PREINDEX(A, B, C) \ + case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; + LOAD_STORE_LIST(LS_PREINDEX) + #undef LS_PREINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePostIndex(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePostIndex)"; + + switch (instr->Mask(LoadStorePostIndexMask)) { + #define LS_POSTINDEX(A, B, C) \ + case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; + LOAD_STORE_LIST(LS_POSTINDEX) + #undef LS_POSTINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStoreUnsignedOffset)"; + + switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { + #define LS_UNSIGNEDOFFSET(A, B, C) \ + case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; + LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) + #undef LS_UNSIGNEDOFFSET + case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xns'ILU]"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreRegisterOffset(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStoreRegisterOffset)"; + + switch (instr->Mask(LoadStoreRegisterOffsetMask)) { + #define LS_REGISTEROFFSET(A, B, C) \ + case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; + LOAD_STORE_LIST(LS_REGISTEROFFSET) + #undef LS_REGISTEROFFSET + case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreUnscaledOffset(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Wt, ['Xns'ILS]"; + const char *form_x = "'Xt, ['Xns'ILS]"; + const char *form_s = "'St, ['Xns'ILS]"; + const char *form_d = "'Dt, ['Xns'ILS]"; + const char *form_prefetch = "'PrefOp, ['Xns'ILS]"; + + switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { + case STURB_w: mnemonic = "sturb"; break; + case STURH_w: mnemonic = "sturh"; break; + case STUR_w: mnemonic = "stur"; break; + case STUR_x: mnemonic = "stur"; form = form_x; break; + case STUR_s: mnemonic = "stur"; form = form_s; break; + case STUR_d: mnemonic = "stur"; form = form_d; break; + case LDURB_w: mnemonic = "ldurb"; break; + case LDURH_w: mnemonic = "ldurh"; break; + case LDUR_w: mnemonic = "ldur"; break; + case LDUR_x: mnemonic = "ldur"; form = form_x; break; + case LDUR_s: mnemonic = "ldur"; form = form_s; break; + case LDUR_d: mnemonic = "ldur"; form = form_d; break; + case LDURSB_x: form = form_x; // Fall through. + case LDURSB_w: mnemonic = "ldursb"; break; + case LDURSH_x: form = form_x; // Fall through. + case LDURSH_w: mnemonic = "ldursh"; break; + case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; + case PRFUM: mnemonic = "prfum"; form = form_prefetch; break; + default: form = "(LoadStoreUnscaledOffset)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadLiteral(const Instruction* instr) { + const char *mnemonic = "ldr"; + const char *form = "(LoadLiteral)"; + + switch (instr->Mask(LoadLiteralMask)) { + case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; + case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; + case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; + case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; + case LDRSW_x_lit: { + mnemonic = "ldrsw"; + form = "'Xt, 'ILLiteral 'LValue"; + break; + } + case PRFM_lit: { + mnemonic = "prfm"; + form = "'PrefOp, 'ILLiteral 'LValue"; + break; + } + default: mnemonic = "unimplemented"; + } + Format(instr, mnemonic, form); +} + + +#define LOAD_STORE_PAIR_LIST(V) \ + V(STP_w, "stp", "'Wt, 'Wt2", "4") \ + V(LDP_w, "ldp", "'Wt, 'Wt2", "4") \ + V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "4") \ + V(STP_x, "stp", "'Xt, 'Xt2", "8") \ + V(LDP_x, "ldp", "'Xt, 'Xt2", "8") \ + V(STP_s, "stp", "'St, 'St2", "4") \ + V(LDP_s, "ldp", "'St, 'St2", "4") \ + V(STP_d, "stp", "'Dt, 'Dt2", "8") \ + V(LDP_d, "ldp", "'Dt, 'Dt2", "8") + +void Disassembler::VisitLoadStorePairPostIndex(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairPostIndex)"; + + switch (instr->Mask(LoadStorePairPostIndexMask)) { + #define LSP_POSTINDEX(A, B, C, D) \ + case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; + LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) + #undef LSP_POSTINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairPreIndex(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairPreIndex)"; + + switch (instr->Mask(LoadStorePairPreIndexMask)) { + #define LSP_PREINDEX(A, B, C, D) \ + case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; + LOAD_STORE_PAIR_LIST(LSP_PREINDEX) + #undef LSP_PREINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairOffset(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairOffset)"; + + switch (instr->Mask(LoadStorePairOffsetMask)) { + #define LSP_OFFSET(A, B, C, D) \ + case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; + LOAD_STORE_PAIR_LIST(LSP_OFFSET) + #undef LSP_OFFSET + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairNonTemporal(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form; + + switch (instr->Mask(LoadStorePairNonTemporalMask)) { + case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; + case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; + case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; + case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; + case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; + case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; + case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; + case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; + default: form = "(LoadStorePairNonTemporal)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreExclusive(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form; + + switch (instr->Mask(LoadStoreExclusiveMask)) { + case STXRB_w: mnemonic = "stxrb"; form = "'Ws, 'Wt, ['Xns]"; break; + case STXRH_w: mnemonic = "stxrh"; form = "'Ws, 'Wt, ['Xns]"; break; + case STXR_w: mnemonic = "stxr"; form = "'Ws, 'Wt, ['Xns]"; break; + case STXR_x: mnemonic = "stxr"; form = "'Ws, 'Xt, ['Xns]"; break; + case LDXRB_w: mnemonic = "ldxrb"; form = "'Wt, ['Xns]"; break; + case LDXRH_w: mnemonic = "ldxrh"; form = "'Wt, ['Xns]"; break; + case LDXR_w: mnemonic = "ldxr"; form = "'Wt, ['Xns]"; break; + case LDXR_x: mnemonic = "ldxr"; form = "'Xt, ['Xns]"; break; + case STXP_w: mnemonic = "stxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; + case STXP_x: mnemonic = "stxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; + case LDXP_w: mnemonic = "ldxp"; form = "'Wt, 'Wt2, ['Xns]"; break; + case LDXP_x: mnemonic = "ldxp"; form = "'Xt, 'Xt2, ['Xns]"; break; + case STLXRB_w: mnemonic = "stlxrb"; form = "'Ws, 'Wt, ['Xns]"; break; + case STLXRH_w: mnemonic = "stlxrh"; form = "'Ws, 'Wt, ['Xns]"; break; + case STLXR_w: mnemonic = "stlxr"; form = "'Ws, 'Wt, ['Xns]"; break; + case STLXR_x: mnemonic = "stlxr"; form = "'Ws, 'Xt, ['Xns]"; break; + case LDAXRB_w: mnemonic = "ldaxrb"; form = "'Wt, ['Xns]"; break; + case LDAXRH_w: mnemonic = "ldaxrh"; form = "'Wt, ['Xns]"; break; + case LDAXR_w: mnemonic = "ldaxr"; form = "'Wt, ['Xns]"; break; + case LDAXR_x: mnemonic = "ldaxr"; form = "'Xt, ['Xns]"; break; + case STLXP_w: mnemonic = "stlxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; + case STLXP_x: mnemonic = "stlxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; + case LDAXP_w: mnemonic = "ldaxp"; form = "'Wt, 'Wt2, ['Xns]"; break; + case LDAXP_x: mnemonic = "ldaxp"; form = "'Xt, 'Xt2, ['Xns]"; break; + case STLRB_w: mnemonic = "stlrb"; form = "'Wt, ['Xns]"; break; + case STLRH_w: mnemonic = "stlrh"; form = "'Wt, ['Xns]"; break; + case STLR_w: mnemonic = "stlr"; form = "'Wt, ['Xns]"; break; + case STLR_x: mnemonic = "stlr"; form = "'Xt, ['Xns]"; break; + case LDARB_w: mnemonic = "ldarb"; form = "'Wt, ['Xns]"; break; + case LDARH_w: mnemonic = "ldarh"; form = "'Wt, ['Xns]"; break; + case LDAR_w: mnemonic = "ldar"; form = "'Wt, ['Xns]"; break; + case LDAR_x: mnemonic = "ldar"; form = "'Xt, ['Xns]"; break; + default: form = "(LoadStoreExclusive)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPCompare(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Fn, 'Fm"; + const char *form_zero = "'Fn, #0.0"; + + switch (instr->Mask(FPCompareMask)) { + case FCMP_s_zero: + case FCMP_d_zero: form = form_zero; // Fall through. + case FCMP_s: + case FCMP_d: mnemonic = "fcmp"; break; + default: form = "(FPCompare)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPConditionalCompare(const Instruction* instr) { + const char *mnemonic = "unmplemented"; + const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; + + switch (instr->Mask(FPConditionalCompareMask)) { + case FCCMP_s: + case FCCMP_d: mnemonic = "fccmp"; break; + case FCCMPE_s: + case FCCMPE_d: mnemonic = "fccmpe"; break; + default: form = "(FPConditionalCompare)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPConditionalSelect(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; + + switch (instr->Mask(FPConditionalSelectMask)) { + case FCSEL_s: + case FCSEL_d: mnemonic = "fcsel"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing1Source(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Fd, 'Fn"; + + switch (instr->Mask(FPDataProcessing1SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMOV, "fmov"); + FORMAT(FABS, "fabs"); + FORMAT(FNEG, "fneg"); + FORMAT(FSQRT, "fsqrt"); + FORMAT(FRINTN, "frintn"); + FORMAT(FRINTP, "frintp"); + FORMAT(FRINTM, "frintm"); + FORMAT(FRINTZ, "frintz"); + FORMAT(FRINTA, "frinta"); + FORMAT(FRINTX, "frintx"); + FORMAT(FRINTI, "frinti"); + #undef FORMAT + case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; + case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; + default: form = "(FPDataProcessing1Source)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing2Source(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm"; + + switch (instr->Mask(FPDataProcessing2SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMUL, "fmul"); + FORMAT(FDIV, "fdiv"); + FORMAT(FADD, "fadd"); + FORMAT(FSUB, "fsub"); + FORMAT(FMAX, "fmax"); + FORMAT(FMIN, "fmin"); + FORMAT(FMAXNM, "fmaxnm"); + FORMAT(FMINNM, "fminnm"); + FORMAT(FNMUL, "fnmul"); + #undef FORMAT + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing3Source(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; + + switch (instr->Mask(FPDataProcessing3SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMADD, "fmadd"); + FORMAT(FMSUB, "fmsub"); + FORMAT(FNMADD, "fnmadd"); + FORMAT(FNMSUB, "fnmsub"); + #undef FORMAT + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPImmediate(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "(FPImmediate)"; + + switch (instr->Mask(FPImmediateMask)) { + case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; + case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPIntegerConvert(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(FPIntegerConvert)"; + const char *form_rf = "'Rd, 'Fn"; + const char *form_fr = "'Fd, 'Rn"; + + switch (instr->Mask(FPIntegerConvertMask)) { + case FMOV_ws: + case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; + case FMOV_sw: + case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; + case FCVTAS_ws: + case FCVTAS_xs: + case FCVTAS_wd: + case FCVTAS_xd: mnemonic = "fcvtas"; form = form_rf; break; + case FCVTAU_ws: + case FCVTAU_xs: + case FCVTAU_wd: + case FCVTAU_xd: mnemonic = "fcvtau"; form = form_rf; break; + case FCVTMS_ws: + case FCVTMS_xs: + case FCVTMS_wd: + case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; + case FCVTMU_ws: + case FCVTMU_xs: + case FCVTMU_wd: + case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; + case FCVTNS_ws: + case FCVTNS_xs: + case FCVTNS_wd: + case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; + case FCVTNU_ws: + case FCVTNU_xs: + case FCVTNU_wd: + case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; + case FCVTZU_xd: + case FCVTZU_ws: + case FCVTZU_wd: + case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; + case FCVTZS_xd: + case FCVTZS_wd: + case FCVTZS_xs: + case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; + case SCVTF_sw: + case SCVTF_sx: + case SCVTF_dw: + case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; + case UCVTF_sw: + case UCVTF_sx: + case UCVTF_dw: + case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPFixedPointConvert(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Fn, 'IFPFBits"; + const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; + + switch (instr->Mask(FPFixedPointConvertMask)) { + case FCVTZS_ws_fixed: + case FCVTZS_xs_fixed: + case FCVTZS_wd_fixed: + case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; + case FCVTZU_ws_fixed: + case FCVTZU_xs_fixed: + case FCVTZU_wd_fixed: + case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; + case SCVTF_sw_fixed: + case SCVTF_sx_fixed: + case SCVTF_dw_fixed: + case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; + case UCVTF_sw_fixed: + case UCVTF_sx_fixed: + case UCVTF_dw_fixed: + case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitSystem(const Instruction* instr) { + // Some system instructions hijack their Op and Cp fields to represent a + // range of immediates instead of indicating a different instruction. This + // makes the decoding tricky. + const char *mnemonic = "unimplemented"; + const char *form = "(System)"; + + if (instr->Mask(SystemExclusiveMonitorFMask) == SystemExclusiveMonitorFixed) { + switch (instr->Mask(SystemExclusiveMonitorMask)) { + case CLREX: { + mnemonic = "clrex"; + form = (instr->CRm() == 0xf) ? NULL : "'IX"; + break; + } + } + } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { + switch (instr->Mask(SystemSysRegMask)) { + case MRS: { + mnemonic = "mrs"; + switch (instr->ImmSystemRegister()) { + case NZCV: form = "'Xt, nzcv"; break; + case FPCR: form = "'Xt, fpcr"; break; + default: form = "'Xt, (unknown)"; break; + } + break; + } + case MSR: { + mnemonic = "msr"; + switch (instr->ImmSystemRegister()) { + case NZCV: form = "nzcv, 'Xt"; break; + case FPCR: form = "fpcr, 'Xt"; break; + default: form = "(unknown), 'Xt"; break; + } + break; + } + } + } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { + switch (instr->ImmHint()) { + case NOP: { + mnemonic = "nop"; + form = NULL; + break; + } + } + } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { + switch (instr->Mask(MemBarrierMask)) { + case DMB: { + mnemonic = "dmb"; + form = "'M"; + break; + } + case DSB: { + mnemonic = "dsb"; + form = "'M"; + break; + } + case ISB: { + mnemonic = "isb"; + form = NULL; + break; + } + } + } + + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitException(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'IDebug"; + + switch (instr->Mask(ExceptionMask)) { + case HLT: mnemonic = "hlt"; break; + case BRK: mnemonic = "brk"; break; + case SVC: mnemonic = "svc"; break; + case HVC: mnemonic = "hvc"; break; + case SMC: mnemonic = "smc"; break; + case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; + case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; + case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; + default: form = "(Exception)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitUnimplemented(const Instruction* instr) { + Format(instr, "unimplemented", "(Unimplemented)"); +} + + +void Disassembler::VisitUnallocated(const Instruction* instr) { + Format(instr, "unallocated", "(Unallocated)"); +} + + +void Disassembler::ProcessOutput(const Instruction* /*instr*/) { + // The base disasm does nothing more than disassembling into a buffer. +} + + +void Disassembler::AppendRegisterNameToOutput(const Instruction* instr, + const CPURegister& reg) { + USE(instr); + VIXL_ASSERT(reg.IsValid()); + char reg_char; + + if (reg.IsRegister()) { + reg_char = reg.Is64Bits() ? 'x' : 'w'; + } else { + VIXL_ASSERT(reg.IsFPRegister()); + reg_char = reg.Is64Bits() ? 'd' : 's'; + } + + if (reg.IsFPRegister() || !(reg.Aliases(sp) || reg.Aliases(xzr))) { + // A normal register: w0 - w30, x0 - x30, s0 - s31, d0 - d31. + AppendToOutput("%c%d", reg_char, reg.code()); + } else if (reg.Aliases(sp)) { + // Disassemble w31/x31 as stack pointer wsp/sp. + AppendToOutput("%s", reg.Is64Bits() ? "sp" : "wsp"); + } else { + // Disassemble w31/x31 as zero register wzr/xzr. + AppendToOutput("%czr", reg_char); + } +} + + +void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr, + int64_t offset) { + USE(instr); + char sign = (offset < 0) ? '-' : '+'; + AppendToOutput("#%c0x%" PRIx64, sign, std::abs(offset)); +} + + +void Disassembler::AppendAddressToOutput(const Instruction* instr, + const void* addr) { + USE(instr); + AppendToOutput("(addr 0x%" PRIxPTR ")", reinterpret_cast(addr)); +} + + +void Disassembler::AppendCodeAddressToOutput(const Instruction* instr, + const void* addr) { + AppendAddressToOutput(instr, addr); +} + + +void Disassembler::AppendDataAddressToOutput(const Instruction* instr, + const void* addr) { + AppendAddressToOutput(instr, addr); +} + + +void Disassembler::AppendCodeRelativeAddressToOutput(const Instruction* instr, + const void* addr) { + USE(instr); + int64_t rel_addr = CodeRelativeAddress(addr); + if (rel_addr >= 0) { + AppendToOutput("(addr 0x%" PRIx64 ")", rel_addr); + } else { + AppendToOutput("(addr -0x%" PRIx64 ")", -rel_addr); + } +} + + +void Disassembler::AppendCodeRelativeCodeAddressToOutput( + const Instruction* instr, const void* addr) { + AppendCodeRelativeAddressToOutput(instr, addr); +} + + +void Disassembler::AppendCodeRelativeDataAddressToOutput( + const Instruction* instr, const void* addr) { + AppendCodeRelativeAddressToOutput(instr, addr); +} + + +void Disassembler::MapCodeAddress(int64_t base_address, + const Instruction* instr_address) { + set_code_address_offset( + base_address - reinterpret_cast(instr_address)); +} +int64_t Disassembler::CodeRelativeAddress(const void* addr) { + return reinterpret_cast(addr) + code_address_offset(); +} + + +void Disassembler::Format(const Instruction* instr, const char* mnemonic, + const char* format) { + VIXL_ASSERT(mnemonic != NULL); + ResetOutput(); + Substitute(instr, mnemonic); + if (format != NULL) { + buffer_[buffer_pos_++] = ' '; + Substitute(instr, format); + } + buffer_[buffer_pos_] = 0; + ProcessOutput(instr); +} + + +void Disassembler::Substitute(const Instruction* instr, const char* string) { + char chr = *string++; + while (chr != '\0') { + if (chr == '\'') { + string += SubstituteField(instr, string); + } else { + buffer_[buffer_pos_++] = chr; + } + chr = *string++; + } +} + + +int Disassembler::SubstituteField(const Instruction* instr, + const char* format) { + switch (format[0]) { + case 'R': // Register. X or W, selected by sf bit. + case 'F': // FP Register. S or D, selected by type field. + case 'W': + case 'X': + case 'S': + case 'D': return SubstituteRegisterField(instr, format); + case 'I': return SubstituteImmediateField(instr, format); + case 'L': return SubstituteLiteralField(instr, format); + case 'H': return SubstituteShiftField(instr, format); + case 'P': return SubstitutePrefetchField(instr, format); + case 'C': return SubstituteConditionField(instr, format); + case 'E': return SubstituteExtendField(instr, format); + case 'A': return SubstitutePCRelAddressField(instr, format); + case 'B': return SubstituteBranchTargetField(instr, format); + case 'O': return SubstituteLSRegOffsetField(instr, format); + case 'M': return SubstituteBarrierField(instr, format); + default: { + VIXL_UNREACHABLE(); + return 1; + } + } +} + + +int Disassembler::SubstituteRegisterField(const Instruction* instr, + const char* format) { + unsigned reg_num = 0; + unsigned field_len = 2; + switch (format[1]) { + case 'd': reg_num = instr->Rd(); break; + case 'n': reg_num = instr->Rn(); break; + case 'm': reg_num = instr->Rm(); break; + case 'a': reg_num = instr->Ra(); break; + case 's': reg_num = instr->Rs(); break; + case 't': { + if (format[2] == '2') { + reg_num = instr->Rt2(); + field_len = 3; + } else { + reg_num = instr->Rt(); + } + break; + } + default: VIXL_UNREACHABLE(); + } + + // Increase field length for registers tagged as stack. + if (format[2] == 's') { + field_len = 3; + } + + CPURegister::RegisterType reg_type; + unsigned reg_size; + + if (format[0] == 'R') { + // Register type is R: use sf bit to choose X and W. + reg_type = CPURegister::kRegister; + reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + } else if (format[0] == 'F') { + // Floating-point register: use type field to choose S or D. + reg_type = CPURegister::kFPRegister; + reg_size = ((instr->FPType() & 1) == 0) ? kSRegSize : kDRegSize; + } else { + // The register type is specified. + switch (format[0]) { + case 'W': + reg_type = CPURegister::kRegister; reg_size = kWRegSize; break; + case 'X': + reg_type = CPURegister::kRegister; reg_size = kXRegSize; break; + case 'S': + reg_type = CPURegister::kFPRegister; reg_size = kSRegSize; break; + case 'D': + reg_type = CPURegister::kFPRegister; reg_size = kDRegSize; break; + default: + VIXL_UNREACHABLE(); + reg_type = CPURegister::kRegister; + reg_size = kXRegSize; + } + } + + if ((reg_type == CPURegister::kRegister) && + (reg_num == kZeroRegCode) && (format[2] == 's')) { + reg_num = kSPRegInternalCode; + } + + AppendRegisterNameToOutput(instr, CPURegister(reg_num, reg_size, reg_type)); + + return field_len; +} + + +int Disassembler::SubstituteImmediateField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'I'); + + switch (format[1]) { + case 'M': { // IMoveImm, IMoveNeg or IMoveLSL. + if (format[5] == 'L') { + AppendToOutput("#0x%" PRIx64, instr->ImmMoveWide()); + if (instr->ShiftMoveWide() > 0) { + AppendToOutput(", lsl #%" PRId64, 16 * instr->ShiftMoveWide()); + } + } else { + VIXL_ASSERT((format[5] == 'I') || (format[5] == 'N')); + uint64_t imm = instr->ImmMoveWide() << (16 * instr->ShiftMoveWide()); + if (format[5] == 'N') + imm = ~imm; + if (!instr->SixtyFourBits()) + imm &= UINT64_C(0xffffffff); + AppendToOutput("#0x%" PRIx64, imm); + } + return 8; + } + case 'L': { + switch (format[2]) { + case 'L': { // ILLiteral - Immediate Load Literal. + AppendToOutput("pc%+" PRId64, + instr->ImmLLiteral() << kLiteralEntrySizeLog2); + return 9; + } + case 'S': { // ILS - Immediate Load/Store. + if (instr->ImmLS() != 0) { + AppendToOutput(", #%" PRId64, instr->ImmLS()); + } + return 3; + } + case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. + if (instr->ImmLSPair() != 0) { + // format[3] is the scale value. Convert to a number. + int scale = format[3] - 0x30; + AppendToOutput(", #%" PRId64, instr->ImmLSPair() * scale); + } + return 4; + } + case 'U': { // ILU - Immediate Load/Store Unsigned. + if (instr->ImmLSUnsigned() != 0) { + AppendToOutput(", #%" PRIu64, + instr->ImmLSUnsigned() << instr->SizeLS()); + } + return 3; + } + } + } + case 'C': { // ICondB - Immediate Conditional Branch. + int64_t offset = instr->ImmCondBranch() << 2; + AppendPCRelativeOffsetToOutput(instr, offset); + return 6; + } + case 'A': { // IAddSub. + VIXL_ASSERT(instr->ShiftAddSub() <= 1); + int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); + AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); + return 7; + } + case 'F': { // IFPSingle, IFPDouble or IFPFBits. + if (format[3] == 'F') { // IFPFbits. + AppendToOutput("#%" PRId64, 64 - instr->FPScale()); + return 8; + } else { + AppendToOutput("#0x%" PRIx64 " (%.4f)", instr->ImmFP(), + format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); + return 9; + } + } + case 'T': { // ITri - Immediate Triangular Encoded. + AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); + return 4; + } + case 'N': { // INzcv. + int nzcv = (instr->Nzcv() << Flags_offset); + AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', + ((nzcv & ZFlag) == 0) ? 'z' : 'Z', + ((nzcv & CFlag) == 0) ? 'c' : 'C', + ((nzcv & VFlag) == 0) ? 'v' : 'V'); + return 5; + } + case 'P': { // IP - Conditional compare. + AppendToOutput("#%" PRId64, instr->ImmCondCmp()); + return 2; + } + case 'B': { // Bitfields. + return SubstituteBitfieldImmediateField(instr, format); + } + case 'E': { // IExtract. + AppendToOutput("#%" PRId64, instr->ImmS()); + return 8; + } + case 'S': { // IS - Test and branch bit. + AppendToOutput("#%" PRId64, (instr->ImmTestBranchBit5() << 5) | + instr->ImmTestBranchBit40()); + return 2; + } + case 'D': { // IDebug - HLT and BRK instructions. + AppendToOutput("#0x%" PRIx64, instr->ImmException()); + return 6; + } + case 'X': { // IX - CLREX instruction. + AppendToOutput("#0x%" PRIx64, instr->CRm()); + return 2; + } + default: { + VIXL_UNIMPLEMENTED(); + return 0; + } + } +} + + +int Disassembler::SubstituteBitfieldImmediateField(const Instruction* instr, + const char* format) { + VIXL_ASSERT((format[0] == 'I') && (format[1] == 'B')); + unsigned r = instr->ImmR(); + unsigned s = instr->ImmS(); + + switch (format[2]) { + case 'r': { // IBr. + AppendToOutput("#%d", r); + return 3; + } + case 's': { // IBs+1 or IBs-r+1. + if (format[3] == '+') { + AppendToOutput("#%d", s + 1); + return 5; + } else { + VIXL_ASSERT(format[3] == '-'); + AppendToOutput("#%d", s - r + 1); + return 7; + } + } + case 'Z': { // IBZ-r. + VIXL_ASSERT((format[3] == '-') && (format[4] == 'r')); + unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize; + AppendToOutput("#%d", reg_size - r); + return 5; + } + default: { + VIXL_UNREACHABLE(); + return 0; + } + } +} + + +int Disassembler::SubstituteLiteralField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(strncmp(format, "LValue", 6) == 0); + USE(format); + + const void * address = instr->LiteralAddress(); + switch (instr->Mask(LoadLiteralMask)) { + case LDR_w_lit: + case LDR_x_lit: + case LDRSW_x_lit: + case LDR_s_lit: + case LDR_d_lit: + AppendCodeRelativeDataAddressToOutput(instr, address); + break; + case PRFM_lit: { + // Use the prefetch hint to decide how to print the address. + switch (instr->PrefetchHint()) { + case 0x0: // PLD: prefetch for load. + case 0x2: // PST: prepare for store. + AppendCodeRelativeDataAddressToOutput(instr, address); + break; + case 0x1: // PLI: preload instructions. + AppendCodeRelativeCodeAddressToOutput(instr, address); + break; + case 0x3: // Unallocated hint. + AppendCodeRelativeAddressToOutput(instr, address); + break; + } + break; + } + default: + VIXL_UNREACHABLE(); + } + + return 6; +} + + +int Disassembler::SubstituteShiftField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'H'); + VIXL_ASSERT(instr->ShiftDP() <= 0x3); + + switch (format[1]) { + case 'D': { // HDP. + VIXL_ASSERT(instr->ShiftDP() != ROR); + } // Fall through. + case 'L': { // HLo. + if (instr->ImmDPShift() != 0) { + const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; + AppendToOutput(", %s #%" PRId64, shift_type[instr->ShiftDP()], + instr->ImmDPShift()); + } + return 3; + } + default: + VIXL_UNIMPLEMENTED(); + return 0; + } +} + + +int Disassembler::SubstituteConditionField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'C'); + const char* condition_code[] = { "eq", "ne", "hs", "lo", + "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", + "gt", "le", "al", "nv" }; + int cond; + switch (format[1]) { + case 'B': cond = instr->ConditionBranch(); break; + case 'I': { + cond = InvertCondition(static_cast(instr->Condition())); + break; + } + default: cond = instr->Condition(); + } + AppendToOutput("%s", condition_code[cond]); + return 4; +} + + +int Disassembler::SubstitutePCRelAddressField(const Instruction* instr, + const char* format) { + VIXL_ASSERT((strcmp(format, "AddrPCRelByte") == 0) || // Used by `adr`. + (strcmp(format, "AddrPCRelPage") == 0)); // Used by `adrp`. + + int64_t offset = instr->ImmPCRel(); + + // Compute the target address based on the effective address (after applying + // code_address_offset). This is required for correct behaviour of adrp. + const Instruction* base = instr + code_address_offset(); + if (format[9] == 'P') { + offset *= kPageSize; + base = AlignDown(base, kPageSize); + } + // Strip code_address_offset before printing, so we can use the + // semantically-correct AppendCodeRelativeAddressToOutput. + const void* target = + reinterpret_cast(base + offset - code_address_offset()); + + AppendPCRelativeOffsetToOutput(instr, offset); + AppendToOutput(" "); + AppendCodeRelativeAddressToOutput(instr, target); + return 13; +} + + +int Disassembler::SubstituteBranchTargetField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(strncmp(format, "BImm", 4) == 0); + + int64_t offset = 0; + switch (format[5]) { + // BImmUncn - unconditional branch immediate. + case 'n': offset = instr->ImmUncondBranch(); break; + // BImmCond - conditional branch immediate. + case 'o': offset = instr->ImmCondBranch(); break; + // BImmCmpa - compare and branch immediate. + case 'm': offset = instr->ImmCmpBranch(); break; + // BImmTest - test and branch immediate. + case 'e': offset = instr->ImmTestBranch(); break; + default: VIXL_UNIMPLEMENTED(); + } + offset <<= kInstructionSizeLog2; + const void* target_address = reinterpret_cast(instr + offset); + VIXL_STATIC_ASSERT(sizeof(*instr) == 1); + + AppendPCRelativeOffsetToOutput(instr, offset); + AppendToOutput(" "); + AppendCodeRelativeCodeAddressToOutput(instr, target_address); + + return 8; +} + + +int Disassembler::SubstituteExtendField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(strncmp(format, "Ext", 3) == 0); + VIXL_ASSERT(instr->ExtendMode() <= 7); + USE(format); + + const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", + "sxtb", "sxth", "sxtw", "sxtx" }; + + // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit + // registers becomes lsl. + if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && + (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || + (instr->ExtendMode() == UXTX))) { + if (instr->ImmExtendShift() > 0) { + AppendToOutput(", lsl #%" PRId64, instr->ImmExtendShift()); + } + } else { + AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); + if (instr->ImmExtendShift() > 0) { + AppendToOutput(" #%" PRId64, instr->ImmExtendShift()); + } + } + return 3; +} + + +int Disassembler::SubstituteLSRegOffsetField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(strncmp(format, "Offsetreg", 9) == 0); + const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", + "undefined", "undefined", "sxtw", "sxtx" }; + USE(format); + + unsigned shift = instr->ImmShiftLS(); + Extend ext = static_cast(instr->ExtendMode()); + char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; + + unsigned rm = instr->Rm(); + if (rm == kZeroRegCode) { + AppendToOutput("%czr", reg_type); + } else { + AppendToOutput("%c%d", reg_type, rm); + } + + // Extend mode UXTX is an alias for shift mode LSL here. + if (!((ext == UXTX) && (shift == 0))) { + AppendToOutput(", %s", extend_mode[ext]); + if (shift != 0) { + AppendToOutput(" #%" PRId64, instr->SizeLS()); + } + } + return 9; +} + + +int Disassembler::SubstitutePrefetchField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'P'); + USE(format); + + static const char* hints[] = {"ld", "li", "st"}; + static const char* stream_options[] = {"keep", "strm"}; + + unsigned hint = instr->PrefetchHint(); + unsigned target = instr->PrefetchTarget() + 1; + unsigned stream = instr->PrefetchStream(); + + if ((hint >= (sizeof(hints) / sizeof(hints[0]))) || (target > 3)) { + // Unallocated prefetch operations. + int prefetch_mode = instr->ImmPrefetchOperation(); + AppendToOutput("#0b%c%c%c%c%c", + (prefetch_mode & (1 << 4)) ? '1' : '0', + (prefetch_mode & (1 << 3)) ? '1' : '0', + (prefetch_mode & (1 << 2)) ? '1' : '0', + (prefetch_mode & (1 << 1)) ? '1' : '0', + (prefetch_mode & (1 << 0)) ? '1' : '0'); + } else { + VIXL_ASSERT(stream < (sizeof(stream_options) / sizeof(stream_options[0]))); + AppendToOutput("p%sl%d%s", hints[hint], target, stream_options[stream]); + } + return 6; +} + +int Disassembler::SubstituteBarrierField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'M'); + USE(format); + + static const char* options[4][4] = { + { "sy (0b0000)", "oshld", "oshst", "osh" }, + { "sy (0b0100)", "nshld", "nshst", "nsh" }, + { "sy (0b1000)", "ishld", "ishst", "ish" }, + { "sy (0b1100)", "ld", "st", "sy" } + }; + int domain = instr->ImmBarrierDomain(); + int type = instr->ImmBarrierType(); + + AppendToOutput("%s", options[domain][type]); + return 1; +} + +void Disassembler::ResetOutput() { + buffer_pos_ = 0; + buffer_[buffer_pos_] = 0; +} + + +void Disassembler::AppendToOutput(const char* format, ...) { + va_list args; + va_start(args, format); + buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_, format, args); + va_end(args); +} + + +void PrintDisassembler::ProcessOutput(const Instruction* instr) { + fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", + reinterpret_cast(instr), + instr->InstructionBits(), + GetOutput()); +} +} // namespace vixl diff --git a/disas/libvixl/a64/disasm-a64.h b/disas/libvixl/a64/disasm-a64.h new file mode 100644 index 0000000..ddfe98b --- /dev/null +++ b/disas/libvixl/a64/disasm-a64.h @@ -0,0 +1,176 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_DISASM_A64_H +#define VIXL_A64_DISASM_A64_H + +#include "globals.h" +#include "utils.h" +#include "instructions-a64.h" +#include "decoder-a64.h" +#include "assembler-a64.h" + +namespace vixl { + +class Disassembler: public DecoderVisitor { + public: + Disassembler(); + Disassembler(char* text_buffer, int buffer_size); + virtual ~Disassembler(); + char* GetOutput(); + + // Declare all Visitor functions. + #define DECLARE(A) virtual void Visit##A(const Instruction* instr); + VISITOR_LIST(DECLARE) + #undef DECLARE + + protected: + virtual void ProcessOutput(const Instruction* instr); + + // Default output functions. The functions below implement a default way of + // printing elements in the disassembly. A sub-class can override these to + // customize the disassembly output. + + // Prints the name of a register. + virtual void AppendRegisterNameToOutput(const Instruction* instr, + const CPURegister& reg); + + // Prints a PC-relative offset. This is used for example when disassembling + // branches to immediate offsets. + virtual void AppendPCRelativeOffsetToOutput(const Instruction* instr, + int64_t offset); + + // Prints an address, in the general case. It can be code or data. This is + // used for example to print the target address of an ADR instruction. + virtual void AppendCodeRelativeAddressToOutput(const Instruction* instr, + const void* addr); + + // Prints the address of some code. + // This is used for example to print the target address of a branch to an + // immediate offset. + // A sub-class can for example override this method to lookup the address and + // print an appropriate name. + virtual void AppendCodeRelativeCodeAddressToOutput(const Instruction* instr, + const void* addr); + + // Prints the address of some data. + // This is used for example to print the source address of a load literal + // instruction. + virtual void AppendCodeRelativeDataAddressToOutput(const Instruction* instr, + const void* addr); + + // Same as the above, but for addresses that are not relative to the code + // buffer. They are currently not used by VIXL. + virtual void AppendAddressToOutput(const Instruction* instr, + const void* addr); + virtual void AppendCodeAddressToOutput(const Instruction* instr, + const void* addr); + virtual void AppendDataAddressToOutput(const Instruction* instr, + const void* addr); + + public: + // Get/Set the offset that should be added to code addresses when printing + // code-relative addresses in the AppendCodeRelativeAddressToOutput() + // helpers. + // Below is an example of how a branch immediate instruction in memory at + // address 0xb010200 would disassemble with different offsets. + // Base address | Disassembly + // 0x0 | 0xb010200: b #+0xcc (addr 0xb0102cc) + // 0x10000 | 0xb000200: b #+0xcc (addr 0xb0002cc) + // 0xb010200 | 0x0: b #+0xcc (addr 0xcc) + void MapCodeAddress(int64_t base_address, const Instruction* instr_address); + int64_t CodeRelativeAddress(const void* instr); + + private: + void Format( + const Instruction* instr, const char* mnemonic, const char* format); + void Substitute(const Instruction* instr, const char* string); + int SubstituteField(const Instruction* instr, const char* format); + int SubstituteRegisterField(const Instruction* instr, const char* format); + int SubstituteImmediateField(const Instruction* instr, const char* format); + int SubstituteLiteralField(const Instruction* instr, const char* format); + int SubstituteBitfieldImmediateField( + const Instruction* instr, const char* format); + int SubstituteShiftField(const Instruction* instr, const char* format); + int SubstituteExtendField(const Instruction* instr, const char* format); + int SubstituteConditionField(const Instruction* instr, const char* format); + int SubstitutePCRelAddressField(const Instruction* instr, const char* format); + int SubstituteBranchTargetField(const Instruction* instr, const char* format); + int SubstituteLSRegOffsetField(const Instruction* instr, const char* format); + int SubstitutePrefetchField(const Instruction* instr, const char* format); + int SubstituteBarrierField(const Instruction* instr, const char* format); + + bool RdIsZROrSP(const Instruction* instr) const { + return (instr->Rd() == kZeroRegCode); + } + + bool RnIsZROrSP(const Instruction* instr) const { + return (instr->Rn() == kZeroRegCode); + } + + bool RmIsZROrSP(const Instruction* instr) const { + return (instr->Rm() == kZeroRegCode); + } + + bool RaIsZROrSP(const Instruction* instr) const { + return (instr->Ra() == kZeroRegCode); + } + + bool IsMovzMovnImm(unsigned reg_size, uint64_t value); + + int64_t code_address_offset() const { return code_address_offset_; } + + protected: + void ResetOutput(); + void AppendToOutput(const char* string, ...) PRINTF_CHECK(2, 3); + + void set_code_address_offset(int64_t code_address_offset) { + code_address_offset_ = code_address_offset; + } + + char* buffer_; + uint32_t buffer_pos_; + uint32_t buffer_size_; + bool own_buffer_; + + int64_t code_address_offset_; +}; + + +class PrintDisassembler: public Disassembler { + public: + explicit PrintDisassembler(FILE* stream) : stream_(stream) { } + virtual ~PrintDisassembler() { } + + protected: + virtual void ProcessOutput(const Instruction* instr); + + private: + FILE *stream_; +}; +} // namespace vixl + +#endif // VIXL_A64_DISASM_A64_H diff --git a/disas/libvixl/a64/instructions-a64.cc b/disas/libvixl/a64/instructions-a64.cc new file mode 100644 index 0000000..b091886 --- /dev/null +++ b/disas/libvixl/a64/instructions-a64.cc @@ -0,0 +1,314 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "a64/instructions-a64.h" +#include "a64/assembler-a64.h" + +namespace vixl { + + +// Floating-point infinity values. +const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000); +const float kFP32NegativeInfinity = rawbits_to_float(0xff800000); +const double kFP64PositiveInfinity = + rawbits_to_double(UINT64_C(0x7ff0000000000000)); +const double kFP64NegativeInfinity = + rawbits_to_double(UINT64_C(0xfff0000000000000)); + + +// The default NaN values (for FPCR.DN=1). +const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000)); +const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000); + + +static uint64_t RotateRight(uint64_t value, + unsigned int rotate, + unsigned int width) { + VIXL_ASSERT(width <= 64); + rotate &= 63; + return ((value & ((UINT64_C(1) << rotate) - 1)) << + (width - rotate)) | (value >> rotate); +} + + +static uint64_t RepeatBitsAcrossReg(unsigned reg_size, + uint64_t value, + unsigned width) { + VIXL_ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) || + (width == 32)); + VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + uint64_t result = value & ((UINT64_C(1) << width) - 1); + for (unsigned i = width; i < reg_size; i *= 2) { + result |= (result << i); + } + return result; +} + + +bool Instruction::IsLoad() const { + if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { + return false; + } + + if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { + return Mask(LoadStorePairLBit) != 0; + } else { + LoadStoreOp op = static_cast(Mask(LoadStoreOpMask)); + switch (op) { + case LDRB_w: + case LDRH_w: + case LDR_w: + case LDR_x: + case LDRSB_w: + case LDRSB_x: + case LDRSH_w: + case LDRSH_x: + case LDRSW_x: + case LDR_s: + case LDR_d: return true; + default: return false; + } + } +} + + +bool Instruction::IsStore() const { + if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { + return false; + } + + if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { + return Mask(LoadStorePairLBit) == 0; + } else { + LoadStoreOp op = static_cast(Mask(LoadStoreOpMask)); + switch (op) { + case STRB_w: + case STRH_w: + case STR_w: + case STR_x: + case STR_s: + case STR_d: return true; + default: return false; + } + } +} + + +// Logical immediates can't encode zero, so a return value of zero is used to +// indicate a failure case. Specifically, where the constraints on imm_s are +// not met. +uint64_t Instruction::ImmLogical() const { + unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize; + int64_t n = BitN(); + int64_t imm_s = ImmSetBits(); + int64_t imm_r = ImmRotate(); + + // An integer is constructed from the n, imm_s and imm_r bits according to + // the following table: + // + // N imms immr size S R + // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) + // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) + // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) + // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) + // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) + // 0 11110s xxxxxr 2 UInt(s) UInt(r) + // (s bits must not be all set) + // + // A pattern is constructed of size bits, where the least significant S+1 + // bits are set. The pattern is rotated right by R, and repeated across a + // 32 or 64-bit value, depending on destination register width. + // + + if (n == 1) { + if (imm_s == 0x3F) { + return 0; + } + uint64_t bits = (UINT64_C(1) << (imm_s + 1)) - 1; + return RotateRight(bits, imm_r, 64); + } else { + if ((imm_s >> 1) == 0x1F) { + return 0; + } + for (int width = 0x20; width >= 0x2; width >>= 1) { + if ((imm_s & width) == 0) { + int mask = width - 1; + if ((imm_s & mask) == mask) { + return 0; + } + uint64_t bits = (UINT64_C(1) << ((imm_s & mask) + 1)) - 1; + return RepeatBitsAcrossReg(reg_size, + RotateRight(bits, imm_r & mask, width), + width); + } + } + } + VIXL_UNREACHABLE(); + return 0; +} + + +float Instruction::ImmFP32() const { + // ImmFP: abcdefgh (8 bits) + // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) + // where B is b ^ 1 + uint32_t bits = ImmFP(); + uint32_t bit7 = (bits >> 7) & 0x1; + uint32_t bit6 = (bits >> 6) & 0x1; + uint32_t bit5_to_0 = bits & 0x3f; + uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); + + return rawbits_to_float(result); +} + + +double Instruction::ImmFP64() const { + // ImmFP: abcdefgh (8 bits) + // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 + // 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) + // where B is b ^ 1 + uint32_t bits = ImmFP(); + uint64_t bit7 = (bits >> 7) & 0x1; + uint64_t bit6 = (bits >> 6) & 0x1; + uint64_t bit5_to_0 = bits & 0x3f; + uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); + + return rawbits_to_double(result); +} + + +LSDataSize CalcLSPairDataSize(LoadStorePairOp op) { + switch (op) { + case STP_x: + case LDP_x: + case STP_d: + case LDP_d: return LSDoubleWord; + default: return LSWord; + } +} + + +const Instruction* Instruction::ImmPCOffsetTarget() const { + const Instruction * base = this; + ptrdiff_t offset; + if (IsPCRelAddressing()) { + // ADR and ADRP. + offset = ImmPCRel(); + if (Mask(PCRelAddressingMask) == ADRP) { + base = AlignDown(base, kPageSize); + offset *= kPageSize; + } else { + VIXL_ASSERT(Mask(PCRelAddressingMask) == ADR); + } + } else { + // All PC-relative branches. + VIXL_ASSERT(BranchType() != UnknownBranchType); + // Relative branch offsets are instruction-size-aligned. + offset = ImmBranch() << kInstructionSizeLog2; + } + return base + offset; +} + + +inline int Instruction::ImmBranch() const { + switch (BranchType()) { + case CondBranchType: return ImmCondBranch(); + case UncondBranchType: return ImmUncondBranch(); + case CompareBranchType: return ImmCmpBranch(); + case TestBranchType: return ImmTestBranch(); + default: VIXL_UNREACHABLE(); + } + return 0; +} + + +void Instruction::SetImmPCOffsetTarget(const Instruction* target) { + if (IsPCRelAddressing()) { + SetPCRelImmTarget(target); + } else { + SetBranchImmTarget(target); + } +} + + +void Instruction::SetPCRelImmTarget(const Instruction* target) { + int32_t imm21; + if ((Mask(PCRelAddressingMask) == ADR)) { + imm21 = target - this; + } else { + VIXL_ASSERT(Mask(PCRelAddressingMask) == ADRP); + uintptr_t this_page = reinterpret_cast(this) / kPageSize; + uintptr_t target_page = reinterpret_cast(target) / kPageSize; + imm21 = target_page - this_page; + } + Instr imm = Assembler::ImmPCRelAddress(imm21); + + SetInstructionBits(Mask(~ImmPCRel_mask) | imm); +} + + +void Instruction::SetBranchImmTarget(const Instruction* target) { + VIXL_ASSERT(((target - this) & 3) == 0); + Instr branch_imm = 0; + uint32_t imm_mask = 0; + int offset = (target - this) >> kInstructionSizeLog2; + switch (BranchType()) { + case CondBranchType: { + branch_imm = Assembler::ImmCondBranch(offset); + imm_mask = ImmCondBranch_mask; + break; + } + case UncondBranchType: { + branch_imm = Assembler::ImmUncondBranch(offset); + imm_mask = ImmUncondBranch_mask; + break; + } + case CompareBranchType: { + branch_imm = Assembler::ImmCmpBranch(offset); + imm_mask = ImmCmpBranch_mask; + break; + } + case TestBranchType: { + branch_imm = Assembler::ImmTestBranch(offset); + imm_mask = ImmTestBranch_mask; + break; + } + default: VIXL_UNREACHABLE(); + } + SetInstructionBits(Mask(~imm_mask) | branch_imm); +} + + +void Instruction::SetImmLLiteral(const Instruction* source) { + VIXL_ASSERT(IsWordAligned(source)); + ptrdiff_t offset = (source - this) >> kLiteralEntrySizeLog2; + Instr imm = Assembler::ImmLLiteral(offset); + Instr mask = ImmLLiteral_mask; + + SetInstructionBits(Mask(~mask) | imm); +} +} // namespace vixl + diff --git a/disas/libvixl/a64/instructions-a64.h b/disas/libvixl/a64/instructions-a64.h new file mode 100644 index 0000000..f1d883c --- /dev/null +++ b/disas/libvixl/a64/instructions-a64.h @@ -0,0 +1,384 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_INSTRUCTIONS_A64_H_ +#define VIXL_A64_INSTRUCTIONS_A64_H_ + +#include "globals.h" +#include "utils.h" +#include "a64/constants-a64.h" + +namespace vixl { +// ISA constants. -------------------------------------------------------------- + +typedef uint32_t Instr; +const unsigned kInstructionSize = 4; +const unsigned kInstructionSizeLog2 = 2; +const unsigned kLiteralEntrySize = 4; +const unsigned kLiteralEntrySizeLog2 = 2; +const unsigned kMaxLoadLiteralRange = 1 * MBytes; + +// This is the nominal page size (as used by the adrp instruction); the actual +// size of the memory pages allocated by the kernel is likely to differ. +const unsigned kPageSize = 4 * KBytes; +const unsigned kPageSizeLog2 = 12; + +const unsigned kWRegSize = 32; +const unsigned kWRegSizeLog2 = 5; +const unsigned kWRegSizeInBytes = kWRegSize / 8; +const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3; +const unsigned kXRegSize = 64; +const unsigned kXRegSizeLog2 = 6; +const unsigned kXRegSizeInBytes = kXRegSize / 8; +const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3; +const unsigned kSRegSize = 32; +const unsigned kSRegSizeLog2 = 5; +const unsigned kSRegSizeInBytes = kSRegSize / 8; +const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3; +const unsigned kDRegSize = 64; +const unsigned kDRegSizeLog2 = 6; +const unsigned kDRegSizeInBytes = kDRegSize / 8; +const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3; +const uint64_t kWRegMask = UINT64_C(0xffffffff); +const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff); +const uint64_t kSRegMask = UINT64_C(0xffffffff); +const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff); +const uint64_t kSSignMask = UINT64_C(0x80000000); +const uint64_t kDSignMask = UINT64_C(0x8000000000000000); +const uint64_t kWSignMask = UINT64_C(0x80000000); +const uint64_t kXSignMask = UINT64_C(0x8000000000000000); +const uint64_t kByteMask = UINT64_C(0xff); +const uint64_t kHalfWordMask = UINT64_C(0xffff); +const uint64_t kWordMask = UINT64_C(0xffffffff); +const uint64_t kXMaxUInt = UINT64_C(0xffffffffffffffff); +const uint64_t kWMaxUInt = UINT64_C(0xffffffff); +const int64_t kXMaxInt = INT64_C(0x7fffffffffffffff); +const int64_t kXMinInt = INT64_C(0x8000000000000000); +const int32_t kWMaxInt = INT32_C(0x7fffffff); +const int32_t kWMinInt = INT32_C(0x80000000); +const unsigned kLinkRegCode = 30; +const unsigned kZeroRegCode = 31; +const unsigned kSPRegInternalCode = 63; +const unsigned kRegCodeMask = 0x1f; + +const unsigned kAddressTagOffset = 56; +const unsigned kAddressTagWidth = 8; +const uint64_t kAddressTagMask = + ((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset; +VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000)); + +// AArch64 floating-point specifics. These match IEEE-754. +const unsigned kDoubleMantissaBits = 52; +const unsigned kDoubleExponentBits = 11; +const unsigned kFloatMantissaBits = 23; +const unsigned kFloatExponentBits = 8; + +// Floating-point infinity values. +extern const float kFP32PositiveInfinity; +extern const float kFP32NegativeInfinity; +extern const double kFP64PositiveInfinity; +extern const double kFP64NegativeInfinity; + +// The default NaN values (for FPCR.DN=1). +extern const double kFP64DefaultNaN; +extern const float kFP32DefaultNaN; + + +enum LSDataSize { + LSByte = 0, + LSHalfword = 1, + LSWord = 2, + LSDoubleWord = 3 +}; + +LSDataSize CalcLSPairDataSize(LoadStorePairOp op); + +enum ImmBranchType { + UnknownBranchType = 0, + CondBranchType = 1, + UncondBranchType = 2, + CompareBranchType = 3, + TestBranchType = 4 +}; + +enum AddrMode { + Offset, + PreIndex, + PostIndex +}; + +enum FPRounding { + // The first four values are encodable directly by FPCR. + FPTieEven = 0x0, + FPPositiveInfinity = 0x1, + FPNegativeInfinity = 0x2, + FPZero = 0x3, + + // The final rounding mode is only available when explicitly specified by the + // instruction (such as with fcvta). It cannot be set in FPCR. + FPTieAway +}; + +enum Reg31Mode { + Reg31IsStackPointer, + Reg31IsZeroRegister +}; + +// Instructions. --------------------------------------------------------------- + +class Instruction { + public: + Instr InstructionBits() const { + return *(reinterpret_cast(this)); + } + + void SetInstructionBits(Instr new_instr) { + *(reinterpret_cast(this)) = new_instr; + } + + int Bit(int pos) const { + return (InstructionBits() >> pos) & 1; + } + + uint32_t Bits(int msb, int lsb) const { + return unsigned_bitextract_32(msb, lsb, InstructionBits()); + } + + int32_t SignedBits(int msb, int lsb) const { + int32_t bits = *(reinterpret_cast(this)); + return signed_bitextract_32(msb, lsb, bits); + } + + Instr Mask(uint32_t mask) const { + return InstructionBits() & mask; + } + + #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ + int64_t Name() const { return Func(HighBit, LowBit); } + INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) + #undef DEFINE_GETTER + + // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), + // formed from ImmPCRelLo and ImmPCRelHi. + int ImmPCRel() const { + int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo()); + int const width = ImmPCRelLo_width + ImmPCRelHi_width; + return signed_bitextract_32(width-1, 0, offset); + } + + uint64_t ImmLogical() const; + float ImmFP32() const; + double ImmFP64() const; + + LSDataSize SizeLSPair() const { + return CalcLSPairDataSize( + static_cast(Mask(LoadStorePairMask))); + } + + // Helpers. + bool IsCondBranchImm() const { + return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; + } + + bool IsUncondBranchImm() const { + return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; + } + + bool IsCompareBranch() const { + return Mask(CompareBranchFMask) == CompareBranchFixed; + } + + bool IsTestBranch() const { + return Mask(TestBranchFMask) == TestBranchFixed; + } + + bool IsPCRelAddressing() const { + return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; + } + + bool IsLogicalImmediate() const { + return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; + } + + bool IsAddSubImmediate() const { + return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; + } + + bool IsAddSubExtended() const { + return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; + } + + bool IsLoadOrStore() const { + return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; + } + + bool IsLoad() const; + bool IsStore() const; + + bool IsLoadLiteral() const { + // This includes PRFM_lit. + return Mask(LoadLiteralFMask) == LoadLiteralFixed; + } + + bool IsMovn() const { + return (Mask(MoveWideImmediateMask) == MOVN_x) || + (Mask(MoveWideImmediateMask) == MOVN_w); + } + + // Indicate whether Rd can be the stack pointer or the zero register. This + // does not check that the instruction actually has an Rd field. + Reg31Mode RdMode() const { + // The following instructions use sp or wsp as Rd: + // Add/sub (immediate) when not setting the flags. + // Add/sub (extended) when not setting the flags. + // Logical (immediate) when not setting the flags. + // Otherwise, r31 is the zero register. + if (IsAddSubImmediate() || IsAddSubExtended()) { + if (Mask(AddSubSetFlagsBit)) { + return Reg31IsZeroRegister; + } else { + return Reg31IsStackPointer; + } + } + if (IsLogicalImmediate()) { + // Of the logical (immediate) instructions, only ANDS (and its aliases) + // can set the flags. The others can all write into sp. + // Note that some logical operations are not available to + // immediate-operand instructions, so we have to combine two masks here. + if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) { + return Reg31IsZeroRegister; + } else { + return Reg31IsStackPointer; + } + } + return Reg31IsZeroRegister; + } + + // Indicate whether Rn can be the stack pointer or the zero register. This + // does not check that the instruction actually has an Rn field. + Reg31Mode RnMode() const { + // The following instructions use sp or wsp as Rn: + // All loads and stores. + // Add/sub (immediate). + // Add/sub (extended). + // Otherwise, r31 is the zero register. + if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) { + return Reg31IsStackPointer; + } + return Reg31IsZeroRegister; + } + + ImmBranchType BranchType() const { + if (IsCondBranchImm()) { + return CondBranchType; + } else if (IsUncondBranchImm()) { + return UncondBranchType; + } else if (IsCompareBranch()) { + return CompareBranchType; + } else if (IsTestBranch()) { + return TestBranchType; + } else { + return UnknownBranchType; + } + } + + // Find the target of this instruction. 'this' may be a branch or a + // PC-relative addressing instruction. + const Instruction* ImmPCOffsetTarget() const; + + // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or + // a PC-relative addressing instruction. + void SetImmPCOffsetTarget(const Instruction* target); + // Patch a literal load instruction to load from 'source'. + void SetImmLLiteral(const Instruction* source); + + // Calculate the address of a literal referred to by a load-literal + // instruction, and return it as the specified type. + // + // The literal itself is safely mutable only if the backing buffer is safely + // mutable. + template + T LiteralAddress() const { + uint64_t base_raw = reinterpret_cast(this); + ptrdiff_t offset = ImmLLiteral() << kLiteralEntrySizeLog2; + uint64_t address_raw = base_raw + offset; + + // Cast the address using a C-style cast. A reinterpret_cast would be + // appropriate, but it can't cast one integral type to another. + T address = (T)(address_raw); + + // Assert that the address can be represented by the specified type. + VIXL_ASSERT((uint64_t)(address) == address_raw); + + return address; + } + + uint32_t Literal32() const { + uint32_t literal; + memcpy(&literal, LiteralAddress(), sizeof(literal)); + return literal; + } + + uint64_t Literal64() const { + uint64_t literal; + memcpy(&literal, LiteralAddress(), sizeof(literal)); + return literal; + } + + float LiteralFP32() const { + return rawbits_to_float(Literal32()); + } + + double LiteralFP64() const { + return rawbits_to_double(Literal64()); + } + + const Instruction* NextInstruction() const { + return this + kInstructionSize; + } + + const Instruction* InstructionAtOffset(int64_t offset) const { + VIXL_ASSERT(IsWordAligned(this + offset)); + return this + offset; + } + + template static Instruction* Cast(T src) { + return reinterpret_cast(src); + } + + template static const Instruction* CastConst(T src) { + return reinterpret_cast(src); + } + + private: + int ImmBranch() const; + + void SetPCRelImmTarget(const Instruction* target); + void SetBranchImmTarget(const Instruction* target); +}; +} // namespace vixl + +#endif // VIXL_A64_INSTRUCTIONS_A64_H_ diff --git a/disas/libvixl/code-buffer.h b/disas/libvixl/code-buffer.h new file mode 100644 index 0000000..da6233d --- /dev/null +++ b/disas/libvixl/code-buffer.h @@ -0,0 +1,113 @@ +// Copyright 2014, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_CODE_BUFFER_H +#define VIXL_CODE_BUFFER_H + +#include +#include "globals.h" + +namespace vixl { + +class CodeBuffer { + public: + explicit CodeBuffer(size_t capacity = 4 * KBytes); + CodeBuffer(void* buffer, size_t capacity); + ~CodeBuffer(); + + void Reset(); + + ptrdiff_t OffsetFrom(ptrdiff_t offset) const { + ptrdiff_t cursor_offset = cursor_ - buffer_; + VIXL_ASSERT((offset >= 0) && (offset <= cursor_offset)); + return cursor_offset - offset; + } + + ptrdiff_t CursorOffset() const { + return OffsetFrom(0); + } + + template + T GetOffsetAddress(ptrdiff_t offset) const { + VIXL_ASSERT((offset >= 0) && (offset <= (cursor_ - buffer_))); + return reinterpret_cast(buffer_ + offset); + } + + size_t RemainingBytes() const { + VIXL_ASSERT((cursor_ >= buffer_) && (cursor_ <= (buffer_ + capacity_))); + return (buffer_ + capacity_) - cursor_; + } + + // A code buffer can emit: + // * 32-bit data: instruction and constant. + // * 64-bit data: constant. + // * string: debug info. + void Emit32(uint32_t data) { Emit(data); } + + void Emit64(uint64_t data) { Emit(data); } + + void EmitString(const char* string); + + // Align to kInstructionSize. + void Align(); + + size_t capacity() const { return capacity_; } + + bool IsManaged() const { return managed_; } + + void Grow(size_t new_capacity); + + bool IsDirty() const { return dirty_; } + + void SetClean() { dirty_ = false; } + + private: + template + void Emit(T value) { + VIXL_ASSERT(RemainingBytes() >= sizeof(value)); + dirty_ = true; + memcpy(cursor_, &value, sizeof(value)); + cursor_ += sizeof(value); + } + + // Backing store of the buffer. + byte* buffer_; + // If true the backing store is allocated and deallocated by the buffer. The + // backing store can then grow on demand. If false the backing store is + // provided by the user and cannot be resized internally. + bool managed_; + // Pointer to the next location to be written. + byte* cursor_; + // True if there has been any write since the buffer was created or cleaned. + bool dirty_; + // Capacity in bytes of the backing store. + size_t capacity_; +}; + +} // namespace vixl + +#endif // VIXL_CODE_BUFFER_H + diff --git a/disas/libvixl/globals.h b/disas/libvixl/globals.h new file mode 100644 index 0000000..0c24931 --- /dev/null +++ b/disas/libvixl/globals.h @@ -0,0 +1,85 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_GLOBALS_H +#define VIXL_GLOBALS_H + +// Get standard C99 macros for integer types. +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "platform.h" + + +typedef uint8_t byte; + +const int KBytes = 1024; +const int MBytes = 1024 * KBytes; + +#define VIXL_ABORT() printf("in %s, line %i", __FILE__, __LINE__); abort() +#ifdef VIXL_DEBUG + #define VIXL_ASSERT(condition) assert(condition) + #define VIXL_CHECK(condition) VIXL_ASSERT(condition) + #define VIXL_UNIMPLEMENTED() printf("UNIMPLEMENTED\t"); VIXL_ABORT() + #define VIXL_UNREACHABLE() printf("UNREACHABLE\t"); VIXL_ABORT() +#else + #define VIXL_ASSERT(condition) ((void) 0) + #define VIXL_CHECK(condition) assert(condition) + #define VIXL_UNIMPLEMENTED() ((void) 0) + #define VIXL_UNREACHABLE() ((void) 0) +#endif +// This is not as powerful as template based assertions, but it is simple. +// It assumes that the descriptions are unique. If this starts being a problem, +// we can switch to a different implemention. +#define VIXL_CONCAT(a, b) a##b +#define VIXL_STATIC_ASSERT_LINE(line, condition) \ + typedef char VIXL_CONCAT(STATIC_ASSERT_LINE_, line)[(condition) ? 1 : -1] \ + __attribute__((unused)) +#define VIXL_STATIC_ASSERT(condition) VIXL_STATIC_ASSERT_LINE(__LINE__, condition) //NOLINT + +template inline void USE(T) {} + +#define VIXL_ALIGNMENT_EXCEPTION() printf("ALIGNMENT EXCEPTION\t"); VIXL_ABORT() + +#endif // VIXL_GLOBALS_H diff --git a/disas/libvixl/platform.h b/disas/libvixl/platform.h new file mode 100644 index 0000000..de2b110 --- /dev/null +++ b/disas/libvixl/platform.h @@ -0,0 +1,37 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef PLATFORM_H +#define PLATFORM_H + +// Define platform specific functionalities. +#include + +namespace vixl { +inline void HostBreakpoint() { raise(SIGINT); } +} // namespace vixl + +#endif diff --git a/disas/libvixl/utils.cc b/disas/libvixl/utils.cc new file mode 100644 index 0000000..80b132a --- /dev/null +++ b/disas/libvixl/utils.cc @@ -0,0 +1,151 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils.h" +#include + +namespace vixl { + +uint32_t float_to_rawbits(float value) { + uint32_t bits = 0; + memcpy(&bits, &value, 4); + return bits; +} + + +uint64_t double_to_rawbits(double value) { + uint64_t bits = 0; + memcpy(&bits, &value, 8); + return bits; +} + + +float rawbits_to_float(uint32_t bits) { + float value = 0.0; + memcpy(&value, &bits, 4); + return value; +} + + +double rawbits_to_double(uint64_t bits) { + double value = 0.0; + memcpy(&value, &bits, 8); + return value; +} + + +int CountLeadingZeros(uint64_t value, int width) { + VIXL_ASSERT((width == 32) || (width == 64)); + int count = 0; + uint64_t bit_test = UINT64_C(1) << (width - 1); + while ((count < width) && ((bit_test & value) == 0)) { + count++; + bit_test >>= 1; + } + return count; +} + + +int CountLeadingSignBits(int64_t value, int width) { + VIXL_ASSERT((width == 32) || (width == 64)); + if (value >= 0) { + return CountLeadingZeros(value, width) - 1; + } else { + return CountLeadingZeros(~value, width) - 1; + } +} + + +int CountTrailingZeros(uint64_t value, int width) { + VIXL_ASSERT((width == 32) || (width == 64)); + int count = 0; + while ((count < width) && (((value >> count) & 1) == 0)) { + count++; + } + return count; +} + + +int CountSetBits(uint64_t value, int width) { + // TODO: Other widths could be added here, as the implementation already + // supports them. + VIXL_ASSERT((width == 32) || (width == 64)); + + // Mask out unused bits to ensure that they are not counted. + value &= (UINT64_C(0xffffffffffffffff) >> (64-width)); + + // Add up the set bits. + // The algorithm works by adding pairs of bit fields together iteratively, + // where the size of each bit field doubles each time. + // An example for an 8-bit value: + // Bits: h g f e d c b a + // \ | \ | \ | \ | + // value = h+g f+e d+c b+a + // \ | \ | + // value = h+g+f+e d+c+b+a + // \ | + // value = h+g+f+e+d+c+b+a + const uint64_t kMasks[] = { + UINT64_C(0x5555555555555555), + UINT64_C(0x3333333333333333), + UINT64_C(0x0f0f0f0f0f0f0f0f), + UINT64_C(0x00ff00ff00ff00ff), + UINT64_C(0x0000ffff0000ffff), + UINT64_C(0x00000000ffffffff), + }; + + for (unsigned i = 0; i < (sizeof(kMasks) / sizeof(kMasks[0])); i++) { + int shift = 1 << i; + value = ((value >> shift) & kMasks[i]) + (value & kMasks[i]); + } + + return value; +} + + +uint64_t LowestSetBit(uint64_t value) { + return value & -value; +} + + +bool IsPowerOf2(int64_t value) { + return (value != 0) && ((value & (value - 1)) == 0); +} + + +unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) { + VIXL_ASSERT((reg_size % 8) == 0); + int count = 0; + for (unsigned i = 0; i < (reg_size / 16); i++) { + if ((imm & 0xffff) == 0) { + count++; + } + imm >>= 16; + } + return count; +} + +} // namespace vixl diff --git a/disas/libvixl/utils.h b/disas/libvixl/utils.h new file mode 100644 index 0000000..b440626 --- /dev/null +++ b/disas/libvixl/utils.h @@ -0,0 +1,211 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_UTILS_H +#define VIXL_UTILS_H + +#include +#include +#include "globals.h" + +namespace vixl { + +// Macros for compile-time format checking. +#if defined(__GNUC__) +#define PRINTF_CHECK(format_index, varargs_index) \ + __attribute__((format(printf, format_index, varargs_index))) +#else +#define PRINTF_CHECK(format_index, varargs_index) +#endif + +// Check number width. +inline bool is_intn(unsigned n, int64_t x) { + VIXL_ASSERT((0 < n) && (n < 64)); + int64_t limit = INT64_C(1) << (n - 1); + return (-limit <= x) && (x < limit); +} + +inline bool is_uintn(unsigned n, int64_t x) { + VIXL_ASSERT((0 < n) && (n < 64)); + return !(x >> n); +} + +inline unsigned truncate_to_intn(unsigned n, int64_t x) { + VIXL_ASSERT((0 < n) && (n < 64)); + return (x & ((INT64_C(1) << n) - 1)); +} + +#define INT_1_TO_63_LIST(V) \ +V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \ +V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \ +V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \ +V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \ +V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \ +V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \ +V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \ +V(57) V(58) V(59) V(60) V(61) V(62) V(63) + +#define DECLARE_IS_INT_N(N) \ +inline bool is_int##N(int64_t x) { return is_intn(N, x); } +#define DECLARE_IS_UINT_N(N) \ +inline bool is_uint##N(int64_t x) { return is_uintn(N, x); } +#define DECLARE_TRUNCATE_TO_INT_N(N) \ +inline int truncate_to_int##N(int x) { return truncate_to_intn(N, x); } +INT_1_TO_63_LIST(DECLARE_IS_INT_N) +INT_1_TO_63_LIST(DECLARE_IS_UINT_N) +INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N) +#undef DECLARE_IS_INT_N +#undef DECLARE_IS_UINT_N +#undef DECLARE_TRUNCATE_TO_INT_N + +// Bit field extraction. +inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) { + return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1); +} + +inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) { + return (x >> lsb) & ((static_cast(1) << (1 + msb - lsb)) - 1); +} + +inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) { + return (x << (31 - msb)) >> (lsb + 31 - msb); +} + +inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) { + return (x << (63 - msb)) >> (lsb + 63 - msb); +} + +// Floating point representation. +uint32_t float_to_rawbits(float value); +uint64_t double_to_rawbits(double value); +float rawbits_to_float(uint32_t bits); +double rawbits_to_double(uint64_t bits); + + +// NaN tests. +inline bool IsSignallingNaN(double num) { + const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); + uint64_t raw = double_to_rawbits(num); + if (isnan(num) && ((raw & kFP64QuietNaNMask) == 0)) { + return true; + } + return false; +} + + +inline bool IsSignallingNaN(float num) { + const uint32_t kFP32QuietNaNMask = 0x00400000; + uint32_t raw = float_to_rawbits(num); + if (isnan(num) && ((raw & kFP32QuietNaNMask) == 0)) { + return true; + } + return false; +} + + +template +inline bool IsQuietNaN(T num) { + return isnan(num) && !IsSignallingNaN(num); +} + + +// Convert the NaN in 'num' to a quiet NaN. +inline double ToQuietNaN(double num) { + const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); + VIXL_ASSERT(isnan(num)); + return rawbits_to_double(double_to_rawbits(num) | kFP64QuietNaNMask); +} + + +inline float ToQuietNaN(float num) { + const uint32_t kFP32QuietNaNMask = 0x00400000; + VIXL_ASSERT(isnan(num)); + return rawbits_to_float(float_to_rawbits(num) | kFP32QuietNaNMask); +} + + +// Fused multiply-add. +inline double FusedMultiplyAdd(double op1, double op2, double a) { + return fma(op1, op2, a); +} + + +inline float FusedMultiplyAdd(float op1, float op2, float a) { + return fmaf(op1, op2, a); +} + + +// Bit counting. +int CountLeadingZeros(uint64_t value, int width); +int CountLeadingSignBits(int64_t value, int width); +int CountTrailingZeros(uint64_t value, int width); +int CountSetBits(uint64_t value, int width); +uint64_t LowestSetBit(uint64_t value); +bool IsPowerOf2(int64_t value); + +unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size); + +// Pointer alignment +// TODO: rename/refactor to make it specific to instructions. +template +bool IsWordAligned(T pointer) { + VIXL_ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof) + return ((intptr_t)(pointer) & 3) == 0; +} + +// Increment a pointer (up to 64 bits) until it has the specified alignment. +template +T AlignUp(T pointer, size_t alignment) { + // Use C-style casts to get static_cast behaviour for integral types (T), and + // reinterpret_cast behaviour for other types. + + uint64_t pointer_raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); + + size_t align_step = (alignment - pointer_raw) % alignment; + VIXL_ASSERT((pointer_raw + align_step) % alignment == 0); + + return (T)(pointer_raw + align_step); +} + +// Decrement a pointer (up to 64 bits) until it has the specified alignment. +template +T AlignDown(T pointer, size_t alignment) { + // Use C-style casts to get static_cast behaviour for integral types (T), and + // reinterpret_cast behaviour for other types. + + uint64_t pointer_raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); + + size_t align_step = pointer_raw % alignment; + VIXL_ASSERT((pointer_raw - align_step) % alignment == 0); + + return (T)(pointer_raw - align_step); +} + +} // namespace vixl + +#endif // VIXL_UTILS_H diff --git a/disas/lm32.c b/disas/lm32.c new file mode 100644 index 0000000..1718c86 --- /dev/null +++ b/disas/lm32.c @@ -0,0 +1,361 @@ +/* + * Simple LatticeMico32 disassembler. + * + * Copyright (c) 2012 Michael Walle + * + * 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 . + * + */ + +#include +#include "disas/bfd.h" + +typedef enum { + LM32_OP_SRUI = 0, LM32_OP_NORI, LM32_OP_MULI, LM32_OP_SH, LM32_OP_LB, + LM32_OP_SRI, LM32_OP_XORI, LM32_OP_LH, LM32_OP_ANDI, LM32_OP_XNORI, + LM32_OP_LW, LM32_OP_LHU, LM32_OP_SB, LM32_OP_ADDI, LM32_OP_ORI, + LM32_OP_SLI, LM32_OP_LBU, LM32_OP_BE, LM32_OP_BG, LM32_OP_BGE, + LM32_OP_BGEU, LM32_OP_BGU, LM32_OP_SW, LM32_OP_BNE, LM32_OP_ANDHI, + LM32_OP_CMPEI, LM32_OP_CMPGI, LM32_OP_CMPGEI, LM32_OP_CMPGEUI, + LM32_OP_CMPGUI, LM32_OP_ORHI, LM32_OP_CMPNEI, LM32_OP_SRU, LM32_OP_NOR, + LM32_OP_MUL, LM32_OP_DIVU, LM32_OP_RCSR, LM32_OP_SR, LM32_OP_XOR, + LM32_OP_ILL0, LM32_OP_AND, LM32_OP_XNOR, LM32_OP_ILL1, LM32_OP_SCALL, + LM32_OP_SEXTB, LM32_OP_ADD, LM32_OP_OR, LM32_OP_SL, LM32_OP_B, + LM32_OP_MODU, LM32_OP_SUB, LM32_OP_ILL2, LM32_OP_WCSR, LM32_OP_ILL3, + LM32_OP_CALL, LM32_OP_SEXTH, LM32_OP_BI, LM32_OP_CMPE, LM32_OP_CMPG, + LM32_OP_CMPGE, LM32_OP_CMPGEU, LM32_OP_CMPGU, LM32_OP_CALLI, LM32_OP_CMPNE, +} Lm32Opcode; + +typedef enum { + FMT_INVALID = 0, FMT_RRI5, FMT_RRI16, FMT_IMM26, FMT_LOAD, FMT_STORE, + FMT_RRR, FMT_R, FMT_RNR, FMT_CRN, FMT_CNR, FMT_BREAK, +} Lm32OpcodeFmt; + +typedef enum { + LM32_CSR_IE = 0, LM32_CSR_IM, LM32_CSR_IP, LM32_CSR_ICC, LM32_CSR_DCC, + LM32_CSR_CC, LM32_CSR_CFG, LM32_CSR_EBA, LM32_CSR_DC, LM32_CSR_DEBA, + LM32_CSR_CFG2, LM32_CSR_JTX = 0xe, LM32_CSR_JRX, LM32_CSR_BP0, + LM32_CSR_BP1, LM32_CSR_BP2, LM32_CSR_BP3, LM32_CSR_WP0 = 0x18, + LM32_CSR_WP1, LM32_CSR_WP2, LM32_CSR_WP3, +} Lm32CsrNum; + +typedef struct { + int csr; + const char *name; +} Lm32CsrInfo; + +static const Lm32CsrInfo lm32_csr_info[] = { + {LM32_CSR_IE, "ie", }, + {LM32_CSR_IM, "im", }, + {LM32_CSR_IP, "ip", }, + {LM32_CSR_ICC, "icc", }, + {LM32_CSR_DCC, "dcc", }, + {LM32_CSR_CC, "cc", }, + {LM32_CSR_CFG, "cfg", }, + {LM32_CSR_EBA, "eba", }, + {LM32_CSR_DC, "dc", }, + {LM32_CSR_DEBA, "deba", }, + {LM32_CSR_CFG2, "cfg2", }, + {LM32_CSR_JTX, "jtx", }, + {LM32_CSR_JRX, "jrx", }, + {LM32_CSR_BP0, "bp0", }, + {LM32_CSR_BP1, "bp1", }, + {LM32_CSR_BP2, "bp2", }, + {LM32_CSR_BP3, "bp3", }, + {LM32_CSR_WP0, "wp0", }, + {LM32_CSR_WP1, "wp1", }, + {LM32_CSR_WP2, "wp2", }, + {LM32_CSR_WP3, "wp3", }, +}; + +static const Lm32CsrInfo *find_csr_info(int csr) +{ + const Lm32CsrInfo *info; + int i; + + for (i = 0; i < ARRAY_SIZE(lm32_csr_info); i++) { + info = &lm32_csr_info[i]; + if (csr == info->csr) { + return info; + } + } + + return NULL; +} + +typedef struct { + int reg; + const char *name; +} Lm32RegInfo; + +typedef enum { + LM32_REG_R0 = 0, LM32_REG_R1, LM32_REG_R2, LM32_REG_R3, LM32_REG_R4, + LM32_REG_R5, LM32_REG_R6, LM32_REG_R7, LM32_REG_R8, LM32_REG_R9, + LM32_REG_R10, LM32_REG_R11, LM32_REG_R12, LM32_REG_R13, LM32_REG_R14, + LM32_REG_R15, LM32_REG_R16, LM32_REG_R17, LM32_REG_R18, LM32_REG_R19, + LM32_REG_R20, LM32_REG_R21, LM32_REG_R22, LM32_REG_R23, LM32_REG_R24, + LM32_REG_R25, LM32_REG_GP, LM32_REG_FP, LM32_REG_SP, LM32_REG_RA, + LM32_REG_EA, LM32_REG_BA, +} Lm32RegNum; + +static const Lm32RegInfo lm32_reg_info[] = { + {LM32_REG_R0, "r0", }, + {LM32_REG_R1, "r1", }, + {LM32_REG_R2, "r2", }, + {LM32_REG_R3, "r3", }, + {LM32_REG_R4, "r4", }, + {LM32_REG_R5, "r5", }, + {LM32_REG_R6, "r6", }, + {LM32_REG_R7, "r7", }, + {LM32_REG_R8, "r8", }, + {LM32_REG_R9, "r9", }, + {LM32_REG_R10, "r10", }, + {LM32_REG_R11, "r11", }, + {LM32_REG_R12, "r12", }, + {LM32_REG_R13, "r13", }, + {LM32_REG_R14, "r14", }, + {LM32_REG_R15, "r15", }, + {LM32_REG_R16, "r16", }, + {LM32_REG_R17, "r17", }, + {LM32_REG_R18, "r18", }, + {LM32_REG_R19, "r19", }, + {LM32_REG_R20, "r20", }, + {LM32_REG_R21, "r21", }, + {LM32_REG_R22, "r22", }, + {LM32_REG_R23, "r23", }, + {LM32_REG_R24, "r24", }, + {LM32_REG_R25, "r25", }, + {LM32_REG_GP, "gp", }, + {LM32_REG_FP, "fp", }, + {LM32_REG_SP, "sp", }, + {LM32_REG_RA, "ra", }, + {LM32_REG_EA, "ea", }, + {LM32_REG_BA, "ba", }, +}; + +static const Lm32RegInfo *find_reg_info(int reg) +{ + assert(ARRAY_SIZE(lm32_reg_info) == 32); + return &lm32_reg_info[reg & 0x1f]; +} + +typedef struct { + struct { + uint32_t code; + uint32_t mask; + } op; + const char *name; + const char *args_fmt; +} Lm32OpcodeInfo; + +static const Lm32OpcodeInfo lm32_opcode_info[] = { + /* pseudo instructions */ + {{0x34000000, 0xffffffff}, "nop", NULL}, + {{0xac000002, 0xffffffff}, "break", NULL}, + {{0xac000003, 0xffffffff}, "scall", NULL}, + {{0xc3e00000, 0xffffffff}, "bret", NULL}, + {{0xc3c00000, 0xffffffff}, "eret", NULL}, + {{0xc3a00000, 0xffffffff}, "ret", NULL}, + {{0xa4000000, 0xfc1f07ff}, "not", "%2, %0"}, + {{0xb8000000, 0xfc1f07ff}, "mv", "%2, %0"}, + {{0x71e00000, 0xffe00000}, "mvhi", "%1, %u"}, + {{0x34000000, 0xffe00000}, "mvi", "%1, %s"}, + +#define _O(op) {op << 26, 0x3f << 26} + /* regular opcodes */ + {_O(LM32_OP_ADD), "add", "%2, %0, %1" }, + {_O(LM32_OP_ADDI), "addi", "%1, %0, %s" }, + {_O(LM32_OP_AND), "and", "%2, %0, %1" }, + {_O(LM32_OP_ANDHI), "andhi", "%1, %0, %u" }, + {_O(LM32_OP_ANDI), "andi", "%1, %0, %u" }, + {_O(LM32_OP_B), "b", "%0", }, + {_O(LM32_OP_BE), "be", "%1, %0, %r" }, + {_O(LM32_OP_BG), "bg", "%1, %0, %r" }, + {_O(LM32_OP_BGE), "bge", "%1, %0, %r" }, + {_O(LM32_OP_BGEU), "bgeu", "%1, %0, %r" }, + {_O(LM32_OP_BGU), "bgu", "%1, %0, %r" }, + {_O(LM32_OP_BI), "bi", "%R", }, + {_O(LM32_OP_BNE), "bne", "%1, %0, %r" }, + {_O(LM32_OP_CALL), "call", "%0", }, + {_O(LM32_OP_CALLI), "calli", "%R", }, + {_O(LM32_OP_CMPE), "cmpe", "%2, %0, %1" }, + {_O(LM32_OP_CMPEI), "cmpei", "%1, %0, %s" }, + {_O(LM32_OP_CMPG), "cmpg", "%2, %0, %1" }, + {_O(LM32_OP_CMPGE), "cmpge", "%2, %0, %1" }, + {_O(LM32_OP_CMPGEI), "cmpgei", "%1, %0, %s" }, + {_O(LM32_OP_CMPGEU), "cmpgeu", "%2, %0, %1" }, + {_O(LM32_OP_CMPGEUI), "cmpgeui", "%1, %0, %s" }, + {_O(LM32_OP_CMPGI), "cmpgi", "%1, %0, %s" }, + {_O(LM32_OP_CMPGU), "cmpgu", "%2, %0, %1" }, + {_O(LM32_OP_CMPGUI), "cmpgui", "%1, %0, %s" }, + {_O(LM32_OP_CMPNE), "cmpne", "%2, %0, %1" }, + {_O(LM32_OP_CMPNEI), "cmpnei", "%1, %0, %s" }, + {_O(LM32_OP_DIVU), "divu", "%2, %0, %1" }, + {_O(LM32_OP_LB), "lb", "%1, (%0+%s)" }, + {_O(LM32_OP_LBU), "lbu", "%1, (%0+%s)" }, + {_O(LM32_OP_LH), "lh", "%1, (%0+%s)" }, + {_O(LM32_OP_LHU), "lhu", "%1, (%0+%s)" }, + {_O(LM32_OP_LW), "lw", "%1, (%0+%s)" }, + {_O(LM32_OP_MODU), "modu", "%2, %0, %1" }, + {_O(LM32_OP_MULI), "muli", "%1, %0, %s" }, + {_O(LM32_OP_MUL), "mul", "%2, %0, %1" }, + {_O(LM32_OP_NORI), "nori", "%1, %0, %u" }, + {_O(LM32_OP_NOR), "nor", "%2, %0, %1" }, + {_O(LM32_OP_ORHI), "orhi", "%1, %0, %u" }, + {_O(LM32_OP_ORI), "ori", "%1, %0, %u" }, + {_O(LM32_OP_OR), "or", "%2, %0, %1" }, + {_O(LM32_OP_RCSR), "rcsr", "%2, %c", }, + {_O(LM32_OP_SB), "sb", "(%0+%s), %1" }, + {_O(LM32_OP_SEXTB), "sextb", "%2, %0", }, + {_O(LM32_OP_SEXTH), "sexth", "%2, %0", }, + {_O(LM32_OP_SH), "sh", "(%0+%s), %1" }, + {_O(LM32_OP_SLI), "sli", "%1, %0, %h" }, + {_O(LM32_OP_SL), "sl", "%2, %0, %1" }, + {_O(LM32_OP_SRI), "sri", "%1, %0, %h" }, + {_O(LM32_OP_SR), "sr", "%2, %0, %1" }, + {_O(LM32_OP_SRUI), "srui", "%1, %0, %d" }, + {_O(LM32_OP_SRU), "sru", "%2, %0, %s" }, + {_O(LM32_OP_SUB), "sub", "%2, %0, %s" }, + {_O(LM32_OP_SW), "sw", "(%0+%s), %1" }, + {_O(LM32_OP_WCSR), "wcsr", "%c, %1", }, + {_O(LM32_OP_XNORI), "xnori", "%1, %0, %u" }, + {_O(LM32_OP_XNOR), "xnor", "%2, %0, %1" }, + {_O(LM32_OP_XORI), "xori", "%1, %0, %u" }, + {_O(LM32_OP_XOR), "xor", "%2, %0, %1" }, +#undef _O +}; + +static const Lm32OpcodeInfo *find_opcode_info(uint32_t opcode) +{ + const Lm32OpcodeInfo *info; + int i; + for (i = 0; i < ARRAY_SIZE(lm32_opcode_info); i++) { + info = &lm32_opcode_info[i]; + if ((opcode & info->op.mask) == info->op.code) { + return info; + } + } + + return NULL; +} + +int print_insn_lm32(bfd_vma memaddr, struct disassemble_info *info) +{ + fprintf_function fprintf_fn = info->fprintf_func; + void *stream = info->stream; + int rc; + uint8_t insn[4]; + const Lm32OpcodeInfo *opc_info; + uint32_t op; + const char *args_fmt; + + rc = info->read_memory_func(memaddr, insn, 4, info); + if (rc != 0) { + info->memory_error_func(rc, memaddr, info); + return -1; + } + + fprintf_fn(stream, "%02x %02x %02x %02x ", + insn[0], insn[1], insn[2], insn[3]); + + op = bfd_getb32(insn); + opc_info = find_opcode_info(op); + if (opc_info) { + fprintf_fn(stream, "%-8s ", opc_info->name); + args_fmt = opc_info->args_fmt; + while (args_fmt && *args_fmt) { + if (*args_fmt == '%') { + switch (*(++args_fmt)) { + case '0': { + uint8_t r0; + const char *r0_name; + r0 = (op >> 21) & 0x1f; + r0_name = find_reg_info(r0)->name; + fprintf_fn(stream, "%s", r0_name); + break; + } + case '1': { + uint8_t r1; + const char *r1_name; + r1 = (op >> 16) & 0x1f; + r1_name = find_reg_info(r1)->name; + fprintf_fn(stream, "%s", r1_name); + break; + } + case '2': { + uint8_t r2; + const char *r2_name; + r2 = (op >> 11) & 0x1f; + r2_name = find_reg_info(r2)->name; + fprintf_fn(stream, "%s", r2_name); + break; + } + case 'c': { + uint8_t csr; + const Lm32CsrInfo *info; + csr = (op >> 21) & 0x1f; + info = find_csr_info(csr); + if (info) { + fprintf_fn(stream, "%s", info->name); + } else { + fprintf_fn(stream, "0x%x", csr); + } + break; + } + case 'u': { + uint16_t u16; + u16 = op & 0xffff; + fprintf_fn(stream, "0x%x", u16); + break; + } + case 's': { + int16_t s16; + s16 = (int16_t)(op & 0xffff); + fprintf_fn(stream, "%d", s16); + break; + } + case 'r': { + uint32_t rela; + rela = memaddr + (((int16_t)(op & 0xffff)) << 2); + fprintf_fn(stream, "%x", rela); + break; + } + case 'R': { + uint32_t rela; + int32_t imm26; + imm26 = (int32_t)((op & 0x3ffffff) << 6) >> 4; + rela = memaddr + imm26; + fprintf_fn(stream, "%x", rela); + break; + } + case 'h': { + uint8_t u5; + u5 = (op & 0x1f); + fprintf_fn(stream, "%d", u5); + break; + } + default: + break; + } + } else { + fprintf_fn(stream, "%c", *args_fmt); + } + args_fmt++; + } + } else { + fprintf_fn(stream, ".word 0x%x", op); + } + + return 4; +} diff --git a/disas/m68k.c b/disas/m68k.c new file mode 100644 index 0000000..cc0db96 --- /dev/null +++ b/disas/m68k.c @@ -0,0 +1,5052 @@ +/* This file is composed of several different files from the upstream + sourceware.org CVS. Original file boundaries marked with **** */ + +#include +#include +#include + +#include "disas/bfd.h" + +/* **** floatformat.h from sourceware.org CVS 2005-08-14. */ +/* IEEE floating point support declarations, for GDB, the GNU Debugger. + Copyright 1991, 1994, 1995, 1997, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . */ + +#if !defined (FLOATFORMAT_H) +#define FLOATFORMAT_H 1 + +/*#include "ansidecl.h" */ + +/* A floatformat consists of a sign bit, an exponent and a mantissa. Once the + bytes are concatenated according to the byteorder flag, then each of those + fields is contiguous. We number the bits with 0 being the most significant + (i.e. BITS_BIG_ENDIAN type numbering), and specify which bits each field + contains with the *_start and *_len fields. */ + +/* What is the order of the bytes. */ + +enum floatformat_byteorders { + + /* Standard little endian byte order. + EX: 1.2345678e10 => 00 00 80 c5 e0 fe 06 42 */ + + floatformat_little, + + /* Standard big endian byte order. + EX: 1.2345678e10 => 42 06 fe e0 c5 80 00 00 */ + + floatformat_big, + + /* Little endian byte order but big endian word order. + EX: 1.2345678e10 => e0 fe 06 42 00 00 80 c5 */ + + floatformat_littlebyte_bigword + +}; + +enum floatformat_intbit { floatformat_intbit_yes, floatformat_intbit_no }; + +struct floatformat +{ + enum floatformat_byteorders byteorder; + unsigned int totalsize; /* Total size of number in bits */ + + /* Sign bit is always one bit long. 1 means negative, 0 means positive. */ + unsigned int sign_start; + + unsigned int exp_start; + unsigned int exp_len; + /* Bias added to a "true" exponent to form the biased exponent. It + is intentionally signed as, otherwize, -exp_bias can turn into a + very large number (e.g., given the exp_bias of 0x3fff and a 64 + bit long, the equation (long)(1 - exp_bias) evaluates to + 4294950914) instead of -16382). */ + int exp_bias; + /* Exponent value which indicates NaN. This is the actual value stored in + the float, not adjusted by the exp_bias. This usually consists of all + one bits. */ + unsigned int exp_nan; + + unsigned int man_start; + unsigned int man_len; + + /* Is the integer bit explicit or implicit? */ + enum floatformat_intbit intbit; + + /* Internal name for debugging. */ + const char *name; + + /* Validator method. */ + int (*is_valid) (const struct floatformat *fmt, const char *from); +}; + +/* floatformats for IEEE single and double, big and little endian. */ + +extern const struct floatformat floatformat_ieee_single_big; +extern const struct floatformat floatformat_ieee_single_little; +extern const struct floatformat floatformat_ieee_double_big; +extern const struct floatformat floatformat_ieee_double_little; + +/* floatformat for ARM IEEE double, little endian bytes and big endian words */ + +extern const struct floatformat floatformat_ieee_double_littlebyte_bigword; + +/* floatformats for various extendeds. */ + +extern const struct floatformat floatformat_i387_ext; +extern const struct floatformat floatformat_m68881_ext; +extern const struct floatformat floatformat_i960_ext; +extern const struct floatformat floatformat_m88110_ext; +extern const struct floatformat floatformat_m88110_harris_ext; +extern const struct floatformat floatformat_arm_ext_big; +extern const struct floatformat floatformat_arm_ext_littlebyte_bigword; +/* IA-64 Floating Point register spilt into memory. */ +extern const struct floatformat floatformat_ia64_spill_big; +extern const struct floatformat floatformat_ia64_spill_little; +extern const struct floatformat floatformat_ia64_quad_big; +extern const struct floatformat floatformat_ia64_quad_little; + +/* Convert from FMT to a double. + FROM is the address of the extended float. + Store the double in *TO. */ + +extern void +floatformat_to_double (const struct floatformat *, const char *, double *); + +/* The converse: convert the double *FROM to FMT + and store where TO points. */ + +extern void +floatformat_from_double (const struct floatformat *, const double *, char *); + +/* Return non-zero iff the data at FROM is a valid number in format FMT. */ + +extern int +floatformat_is_valid (const struct floatformat *fmt, const char *from); + +#endif /* defined (FLOATFORMAT_H) */ +/* **** End of floatformat.h */ +/* **** m68k-dis.h from sourceware.org CVS 2005-08-14. */ +/* Opcode table header for m680[01234]0/m6888[12]/m68851. + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2001, + 2003, 2004 Free Software Foundation, Inc. + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 1, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, + see . */ + +/* These are used as bit flags for the arch field in the m68k_opcode + structure. */ +#define _m68k_undef 0 +#define m68000 0x001 +#define m68008 m68000 /* Synonym for -m68000. otherwise unused. */ +#define m68010 0x002 +#define m68020 0x004 +#define m68030 0x008 +#define m68ec030 m68030 /* Similar enough to -m68030 to ignore differences; + gas will deal with the few differences. */ +#define m68040 0x010 +/* There is no 68050. */ +#define m68060 0x020 +#define m68881 0x040 +#define m68882 m68881 /* Synonym for -m68881. otherwise unused. */ +#define m68851 0x080 +#define cpu32 0x100 /* e.g., 68332 */ + +#define mcfmac 0x200 /* ColdFire MAC. */ +#define mcfemac 0x400 /* ColdFire EMAC. */ +#define cfloat 0x800 /* ColdFire FPU. */ +#define mcfhwdiv 0x1000 /* ColdFire hardware divide. */ + +#define mcfisa_a 0x2000 /* ColdFire ISA_A. */ +#define mcfisa_aa 0x4000 /* ColdFire ISA_A+. */ +#define mcfisa_b 0x8000 /* ColdFire ISA_B. */ +#define mcfusp 0x10000 /* ColdFire USP instructions. */ + +#define mcf5200 0x20000 +#define mcf5206e 0x40000 +#define mcf521x 0x80000 +#define mcf5249 0x100000 +#define mcf528x 0x200000 +#define mcf5307 0x400000 +#define mcf5407 0x800000 +#define mcf5470 0x1000000 +#define mcf5480 0x2000000 + + /* Handy aliases. */ +#define m68040up (m68040 | m68060) +#define m68030up (m68030 | m68040up) +#define m68020up (m68020 | m68030up) +#define m68010up (m68010 | cpu32 | m68020up) +#define m68000up (m68000 | m68010up) + +#define mfloat (m68881 | m68882 | m68040 | m68060) +#define mmmu (m68851 | m68030 | m68040 | m68060) + +/* The structure used to hold information for an opcode. */ + +struct m68k_opcode +{ + /* The opcode name. */ + const char *name; + /* The pseudo-size of the instruction(in bytes). Used to determine + number of bytes necessary to disassemble the instruction. */ + unsigned int size; + /* The opcode itself. */ + unsigned long opcode; + /* The mask used by the disassembler. */ + unsigned long match; + /* The arguments. */ + const char *args; + /* The architectures which support this opcode. */ + unsigned int arch; +}; + +/* The structure used to hold information for an opcode alias. */ + +struct m68k_opcode_alias +{ + /* The alias name. */ + const char *alias; + /* The instruction for which this is an alias. */ + const char *primary; +}; + +/* We store four bytes of opcode for all opcodes because that is the + most any of them need. The actual length of an instruction is + always at least 2 bytes, and is as much longer as necessary to hold + the operands it has. + + The match field is a mask saying which bits must match particular + opcode in order for an instruction to be an instance of that + opcode. + + The args field is a string containing two characters for each + operand of the instruction. The first specifies the kind of + operand; the second, the place it is stored. */ + +/* Kinds of operands: + Characters used: AaBbCcDdEeFfGgHIiJkLlMmnOopQqRrSsTtU VvWwXxYyZz01234|*~%;@!&$?/<>#^+- + + D data register only. Stored as 3 bits. + A address register only. Stored as 3 bits. + a address register indirect only. Stored as 3 bits. + R either kind of register. Stored as 4 bits. + r either kind of register indirect only. Stored as 4 bits. + At the moment, used only for cas2 instruction. + F floating point coprocessor register only. Stored as 3 bits. + O an offset (or width): immediate data 0-31 or data register. + Stored as 6 bits in special format for BF... insns. + + autoincrement only. Stored as 3 bits (number of the address register). + - autodecrement only. Stored as 3 bits (number of the address register). + Q quick immediate data. Stored as 3 bits. + This matches an immediate operand only when value is in range 1 .. 8. + M moveq immediate data. Stored as 8 bits. + This matches an immediate operand only when value is in range -128..127 + T trap vector immediate data. Stored as 4 bits. + + k K-factor for fmove.p instruction. Stored as a 7-bit constant or + a three bit register offset, depending on the field type. + + # immediate data. Stored in special places (b, w or l) + which say how many bits to store. + ^ immediate data for floating point instructions. Special places + are offset by 2 bytes from '#'... + B pc-relative address, converted to an offset + that is treated as immediate data. + d displacement and register. Stores the register as 3 bits + and stores the displacement in the entire second word. + + C the CCR. No need to store it; this is just for filtering validity. + S the SR. No need to store, just as with CCR. + U the USP. No need to store, just as with CCR. + E the MAC ACC. No need to store, just as with CCR. + e the EMAC ACC[0123]. + G the MAC/EMAC MACSR. No need to store, just as with CCR. + g the EMAC ACCEXT{01,23}. + H the MASK. No need to store, just as with CCR. + i the MAC/EMAC scale factor. + + I Coprocessor ID. Not printed if 1. The Coprocessor ID is always + extracted from the 'd' field of word one, which means that an extended + coprocessor opcode can be skipped using the 'i' place, if needed. + + s System Control register for the floating point coprocessor. + + J Misc register for movec instruction, stored in 'j' format. + Possible values: + 0x000 SFC Source Function Code reg [60, 40, 30, 20, 10] + 0x001 DFC Data Function Code reg [60, 40, 30, 20, 10] + 0x002 CACR Cache Control Register [60, 40, 30, 20, mcf] + 0x003 TC MMU Translation Control [60, 40] + 0x004 ITT0 Instruction Transparent + Translation reg 0 [60, 40] + 0x005 ITT1 Instruction Transparent + Translation reg 1 [60, 40] + 0x006 DTT0 Data Transparent + Translation reg 0 [60, 40] + 0x007 DTT1 Data Transparent + Translation reg 1 [60, 40] + 0x008 BUSCR Bus Control Register [60] + 0x800 USP User Stack Pointer [60, 40, 30, 20, 10] + 0x801 VBR Vector Base reg [60, 40, 30, 20, 10, mcf] + 0x802 CAAR Cache Address Register [ 30, 20] + 0x803 MSP Master Stack Pointer [ 40, 30, 20] + 0x804 ISP Interrupt Stack Pointer [ 40, 30, 20] + 0x805 MMUSR MMU Status reg [ 40] + 0x806 URP User Root Pointer [60, 40] + 0x807 SRP Supervisor Root Pointer [60, 40] + 0x808 PCR Processor Configuration reg [60] + 0xC00 ROMBAR ROM Base Address Register [520X] + 0xC04 RAMBAR0 RAM Base Address Register 0 [520X] + 0xC05 RAMBAR1 RAM Base Address Register 0 [520X] + 0xC0F MBAR0 RAM Base Address Register 0 [520X] + 0xC04 FLASHBAR FLASH Base Address Register [mcf528x] + 0xC05 RAMBAR Static RAM Base Address Register [mcf528x] + + L Register list of the type d0-d7/a0-a7 etc. + (New! Improved! Can also hold fp0-fp7, as well!) + The assembler tries to see if the registers match the insn by + looking at where the insn wants them stored. + + l Register list like L, but with all the bits reversed. + Used for going the other way. . . + + c cache identifier which may be "nc" for no cache, "ic" + for instruction cache, "dc" for data cache, or "bc" + for both caches. Used in cinv and cpush. Always + stored in position "d". + + u Any register, with ``upper'' or ``lower'' specification. Used + in the mac instructions with size word. + + The remainder are all stored as 6 bits using an address mode and a + register number; they differ in which addressing modes they match. + + * all (modes 0-6,7.0-4) + ~ alterable memory (modes 2-6,7.0,7.1) + (not 0,1,7.2-4) + % alterable (modes 0-6,7.0,7.1) + (not 7.2-4) + ; data (modes 0,2-6,7.0-4) + (not 1) + @ data, but not immediate (modes 0,2-6,7.0-3) + (not 1,7.4) + ! control (modes 2,5,6,7.0-3) + (not 0,1,3,4,7.4) + & alterable control (modes 2,5,6,7.0,7.1) + (not 0,1,3,4,7.2-4) + $ alterable data (modes 0,2-6,7.0,7.1) + (not 1,7.2-4) + ? alterable control, or data register (modes 0,2,5,6,7.0,7.1) + (not 1,3,4,7.2-4) + / control, or data register (modes 0,2,5,6,7.0-3) + (not 1,3,4,7.4) + > *save operands (modes 2,4,5,6,7.0,7.1) + (not 0,1,3,7.2-4) + < *restore operands (modes 2,3,5,6,7.0-3) + (not 0,1,4,7.4) + + coldfire move operands: + m (modes 0-4) + n (modes 5,7.2) + o (modes 6,7.0,7.1,7.3,7.4) + p (modes 0-5) + + coldfire bset/bclr/btst/mulsl/mulul operands: + q (modes 0,2-5) + v (modes 0,2-5,7.0,7.1) + b (modes 0,2-5,7.2) + w (modes 2-5,7.2) + y (modes 2,5) + z (modes 2,5,7.2) + x mov3q immediate operand. + 4 (modes 2,3,4,5) + */ + +/* For the 68851: */ +/* I didn't use much imagination in choosing the + following codes, so many of them aren't very + mnemonic. -rab + + 0 32 bit pmmu register + Possible values: + 000 TC Translation Control Register (68030, 68851) + + 1 16 bit pmmu register + 111 AC Access Control (68851) + + 2 8 bit pmmu register + 100 CAL Current Access Level (68851) + 101 VAL Validate Access Level (68851) + 110 SCC Stack Change Control (68851) + + 3 68030-only pmmu registers (32 bit) + 010 TT0 Transparent Translation reg 0 + (aka Access Control reg 0 -- AC0 -- on 68ec030) + 011 TT1 Transparent Translation reg 1 + (aka Access Control reg 1 -- AC1 -- on 68ec030) + + W wide pmmu registers + Possible values: + 001 DRP Dma Root Pointer (68851) + 010 SRP Supervisor Root Pointer (68030, 68851) + 011 CRP Cpu Root Pointer (68030, 68851) + + f function code register (68030, 68851) + 0 SFC + 1 DFC + + V VAL register only (68851) + + X BADx, BACx (16 bit) + 100 BAD Breakpoint Acknowledge Data (68851) + 101 BAC Breakpoint Acknowledge Control (68851) + + Y PSR (68851) (MMUSR on 68030) (ACUSR on 68ec030) + Z PCSR (68851) + + | memory (modes 2-6, 7.*) + + t address test level (68030 only) + Stored as 3 bits, range 0-7. + Also used for breakpoint instruction now. + +*/ + +/* Places to put an operand, for non-general operands: + Characters used: BbCcDdFfGgHhIijkLlMmNnostWw123456789/ + + s source, low bits of first word. + d dest, shifted 9 in first word + 1 second word, shifted 12 + 2 second word, shifted 6 + 3 second word, shifted 0 + 4 third word, shifted 12 + 5 third word, shifted 6 + 6 third word, shifted 0 + 7 second word, shifted 7 + 8 second word, shifted 10 + 9 second word, shifted 5 + D store in both place 1 and place 3; for divul and divsl. + B first word, low byte, for branch displacements + W second word (entire), for branch displacements + L second and third words (entire), for branch displacements + (also overloaded for move16) + b second word, low byte + w second word (entire) [variable word/long branch offset for dbra] + W second word (entire) (must be signed 16 bit value) + l second and third word (entire) + g variable branch offset for bra and similar instructions. + The place to store depends on the magnitude of offset. + t store in both place 7 and place 8; for floating point operations + c branch offset for cpBcc operations. + The place to store is word two if bit six of word one is zero, + and words two and three if bit six of word one is one. + i Increment by two, to skip over coprocessor extended operands. Only + works with the 'I' format. + k Dynamic K-factor field. Bits 6-4 of word 2, used as a register number. + Also used for dynamic fmovem instruction. + C floating point coprocessor constant - 7 bits. Also used for static + K-factors... + j Movec register #, stored in 12 low bits of second word. + m For M[S]ACx; 4 bits split with MSB shifted 6 bits in first word + and remaining 3 bits of register shifted 9 bits in first word. + Indicate upper/lower in 1 bit shifted 7 bits in second word. + Use with `R' or `u' format. + n `m' withouth upper/lower indication. (For M[S]ACx; 4 bits split + with MSB shifted 6 bits in first word and remaining 3 bits of + register shifted 9 bits in first word. No upper/lower + indication is done.) Use with `R' or `u' format. + o For M[S]ACw; 4 bits shifted 12 in second word (like `1'). + Indicate upper/lower in 1 bit shifted 7 bits in second word. + Use with `R' or `u' format. + M For M[S]ACw; 4 bits in low bits of first word. Indicate + upper/lower in 1 bit shifted 6 bits in second word. Use with + `R' or `u' format. + N For M[S]ACw; 4 bits in low bits of second word. Indicate + upper/lower in 1 bit shifted 6 bits in second word. Use with + `R' or `u' format. + h shift indicator (scale factor), 1 bit shifted 10 in second word + + Places to put operand, for general operands: + d destination, shifted 6 bits in first word + b source, at low bit of first word, and immediate uses one byte + w source, at low bit of first word, and immediate uses two bytes + l source, at low bit of first word, and immediate uses four bytes + s source, at low bit of first word. + Used sometimes in contexts where immediate is not allowed anyway. + f single precision float, low bit of 1st word, immediate uses 4 bytes + F double precision float, low bit of 1st word, immediate uses 8 bytes + x extended precision float, low bit of 1st word, immediate uses 12 bytes + p packed float, low bit of 1st word, immediate uses 12 bytes + G EMAC accumulator, load (bit 4 2nd word, !bit8 first word) + H EMAC accumulator, non load (bit 4 2nd word, bit 8 first word) + F EMAC ACCx + f EMAC ACCy + I MAC/EMAC scale factor + / Like 's', but set 2nd word, bit 5 if trailing_ampersand set + ] first word, bit 10 +*/ + +extern const struct m68k_opcode m68k_opcodes[]; +extern const struct m68k_opcode_alias m68k_opcode_aliases[]; + +extern const int m68k_numopcodes, m68k_numaliases; + +/* **** End of m68k-opcode.h */ +/* **** m68k-dis.c from sourceware.org CVS 2005-08-14. */ +/* Print Motorola 68k instructions. + Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +/* Local function prototypes. */ + +static const char * const fpcr_names[] = +{ + "", "%fpiar", "%fpsr", "%fpiar/%fpsr", "%fpcr", + "%fpiar/%fpcr", "%fpsr/%fpcr", "%fpiar/%fpsr/%fpcr" +}; + +static const char *const reg_names[] = +{ + "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", + "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%fp", "%sp", + "%ps", "%pc" +}; + +/* Name of register halves for MAC/EMAC. + Separate from reg_names since 'spu', 'fpl' look weird. */ +static const char *const reg_half_names[] = +{ + "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", + "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7", + "%ps", "%pc" +}; + +/* Sign-extend an (unsigned char). */ +#if __STDC__ == 1 +#define COERCE_SIGNED_CHAR(ch) ((signed char) (ch)) +#else +#define COERCE_SIGNED_CHAR(ch) ((int) (((ch) ^ 0x80) & 0xFF) - 128) +#endif + +/* Get a 1 byte signed integer. */ +#define NEXTBYTE(p) (p += 2, fetch_data(info, p), COERCE_SIGNED_CHAR(p[-1])) + +/* Get a 2 byte signed integer. */ +#define COERCE16(x) ((int) (((x) ^ 0x8000) - 0x8000)) +#define NEXTWORD(p) \ + (p += 2, fetch_data(info, p), \ + COERCE16 ((p[-2] << 8) + p[-1])) + +/* Get a 4 byte signed integer. */ +#define COERCE32(x) ((bfd_signed_vma) ((x) ^ 0x80000000) - 0x80000000) +#define NEXTLONG(p) \ + (p += 4, fetch_data(info, p), \ + (COERCE32 ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1]))) + +/* Get a 4 byte unsigned integer. */ +#define NEXTULONG(p) \ + (p += 4, fetch_data(info, p), \ + (unsigned int) ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1])) + +/* Get a single precision float. */ +#define NEXTSINGLE(val, p) \ + (p += 4, fetch_data(info, p), \ + floatformat_to_double (&floatformat_ieee_single_big, (char *) p - 4, &val)) + +/* Get a double precision float. */ +#define NEXTDOUBLE(val, p) \ + (p += 8, fetch_data(info, p), \ + floatformat_to_double (&floatformat_ieee_double_big, (char *) p - 8, &val)) + +/* Get an extended precision float. */ +#define NEXTEXTEND(val, p) \ + (p += 12, fetch_data(info, p), \ + floatformat_to_double (&floatformat_m68881_ext, (char *) p - 12, &val)) + +/* Need a function to convert from packed to double + precision. Actually, it's easier to print a + packed number than a double anyway, so maybe + there should be a special case to handle this... */ +#define NEXTPACKED(p) \ + (p += 12, fetch_data(info, p), 0.0) + +/* Maximum length of an instruction. */ +#define MAXLEN 22 + +#include + +struct private +{ + /* Points to first byte not fetched. */ + bfd_byte *max_fetched; + bfd_byte the_buffer[MAXLEN]; + bfd_vma insn_start; + sigjmp_buf bailout; +}; + +/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) + to ADDR (exclusive) are valid. Returns 1 for success, longjmps + on error. */ +static int +fetch_data2(struct disassemble_info *info, bfd_byte *addr) +{ + int status; + struct private *priv = (struct private *)info->private_data; + bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); + + status = (*info->read_memory_func) (start, + priv->max_fetched, + addr - priv->max_fetched, + info); + if (status != 0) + { + (*info->memory_error_func) (status, start, info); + siglongjmp(priv->bailout, 1); + } + else + priv->max_fetched = addr; + return 1; +} + +static int +fetch_data(struct disassemble_info *info, bfd_byte *addr) +{ + if (addr <= ((struct private *) (info->private_data))->max_fetched) { + return 1; + } else { + return fetch_data2(info, addr); + } +} + +/* This function is used to print to the bit-bucket. */ +static int +dummy_printer (FILE *file ATTRIBUTE_UNUSED, + const char *format ATTRIBUTE_UNUSED, + ...) +{ + return 0; +} + +static void +dummy_print_address (bfd_vma vma ATTRIBUTE_UNUSED, + struct disassemble_info *info ATTRIBUTE_UNUSED) +{ +} + +/* Fetch BITS bits from a position in the instruction specified by CODE. + CODE is a "place to put an argument", or 'x' for a destination + that is a general address (mode and register). + BUFFER contains the instruction. */ + +static int +fetch_arg (unsigned char *buffer, + int code, + int bits, + disassemble_info *info) +{ + int val = 0; + + switch (code) + { + case '/': /* MAC/EMAC mask bit. */ + val = buffer[3] >> 5; + break; + + case 'G': /* EMAC ACC load. */ + val = ((buffer[3] >> 3) & 0x2) | ((~buffer[1] >> 7) & 0x1); + break; + + case 'H': /* EMAC ACC !load. */ + val = ((buffer[3] >> 3) & 0x2) | ((buffer[1] >> 7) & 0x1); + break; + + case ']': /* EMAC ACCEXT bit. */ + val = buffer[0] >> 2; + break; + + case 'I': /* MAC/EMAC scale factor. */ + val = buffer[2] >> 1; + break; + + case 'F': /* EMAC ACCx. */ + val = buffer[0] >> 1; + break; + + case 'f': + val = buffer[1]; + break; + + case 's': + val = buffer[1]; + break; + + case 'd': /* Destination, for register or quick. */ + val = (buffer[0] << 8) + buffer[1]; + val >>= 9; + break; + + case 'x': /* Destination, for general arg. */ + val = (buffer[0] << 8) + buffer[1]; + val >>= 6; + break; + + case 'k': + fetch_data(info, buffer + 3); + val = (buffer[3] >> 4); + break; + + case 'C': + fetch_data(info, buffer + 3); + val = buffer[3]; + break; + + case '1': + fetch_data(info, buffer + 3); + val = (buffer[2] << 8) + buffer[3]; + val >>= 12; + break; + + case '2': + fetch_data(info, buffer + 3); + val = (buffer[2] << 8) + buffer[3]; + val >>= 6; + break; + + case '3': + case 'j': + fetch_data(info, buffer + 3); + val = (buffer[2] << 8) + buffer[3]; + break; + + case '4': + fetch_data(info, buffer + 5); + val = (buffer[4] << 8) + buffer[5]; + val >>= 12; + break; + + case '5': + fetch_data(info, buffer + 5); + val = (buffer[4] << 8) + buffer[5]; + val >>= 6; + break; + + case '6': + fetch_data(info, buffer + 5); + val = (buffer[4] << 8) + buffer[5]; + break; + + case '7': + fetch_data(info, buffer + 3); + val = (buffer[2] << 8) + buffer[3]; + val >>= 7; + break; + + case '8': + fetch_data(info, buffer + 3); + val = (buffer[2] << 8) + buffer[3]; + val >>= 10; + break; + + case '9': + fetch_data(info, buffer + 3); + val = (buffer[2] << 8) + buffer[3]; + val >>= 5; + break; + + case 'e': + val = (buffer[1] >> 6); + break; + + case 'm': + val = (buffer[1] & 0x40 ? 0x8 : 0) + | ((buffer[0] >> 1) & 0x7) + | (buffer[3] & 0x80 ? 0x10 : 0); + break; + + case 'n': + val = (buffer[1] & 0x40 ? 0x8 : 0) | ((buffer[0] >> 1) & 0x7); + break; + + case 'o': + val = (buffer[2] >> 4) | (buffer[3] & 0x80 ? 0x10 : 0); + break; + + case 'M': + val = (buffer[1] & 0xf) | (buffer[3] & 0x40 ? 0x10 : 0); + break; + + case 'N': + val = (buffer[3] & 0xf) | (buffer[3] & 0x40 ? 0x10 : 0); + break; + + case 'h': + val = buffer[2] >> 2; + break; + + default: + abort (); + } + + switch (bits) + { + case 1: + return val & 1; + case 2: + return val & 3; + case 3: + return val & 7; + case 4: + return val & 017; + case 5: + return val & 037; + case 6: + return val & 077; + case 7: + return val & 0177; + case 8: + return val & 0377; + case 12: + return val & 07777; + default: + abort (); + } +} + +/* Check if an EA is valid for a particular code. This is required + for the EMAC instructions since the type of source address determines + if it is a EMAC-load instruciton if the EA is mode 2-5, otherwise it + is a non-load EMAC instruction and the bits mean register Ry. + A similar case exists for the movem instructions where the register + mask is interpreted differently for different EAs. */ + +static bfd_boolean +m68k_valid_ea (char code, int val) +{ + int mode, mask; +#define M(n0,n1,n2,n3,n4,n5,n6,n70,n71,n72,n73,n74) \ + (n0 | n1 << 1 | n2 << 2 | n3 << 3 | n4 << 4 | n5 << 5 | n6 << 6 \ + | n70 << 7 | n71 << 8 | n72 << 9 | n73 << 10 | n74 << 11) + + switch (code) + { + case '*': + mask = M (1,1,1,1,1,1,1,1,1,1,1,1); + break; + case '~': + mask = M (0,0,1,1,1,1,1,1,1,0,0,0); + break; + case '%': + mask = M (1,1,1,1,1,1,1,1,1,0,0,0); + break; + case ';': + mask = M (1,0,1,1,1,1,1,1,1,1,1,1); + break; + case '@': + mask = M (1,0,1,1,1,1,1,1,1,1,1,0); + break; + case '!': + mask = M (0,0,1,0,0,1,1,1,1,1,1,0); + break; + case '&': + mask = M (0,0,1,0,0,1,1,1,1,0,0,0); + break; + case '$': + mask = M (1,0,1,1,1,1,1,1,1,0,0,0); + break; + case '?': + mask = M (1,0,1,0,0,1,1,1,1,0,0,0); + break; + case '/': + mask = M (1,0,1,0,0,1,1,1,1,1,1,0); + break; + case '|': + mask = M (0,0,1,0,0,1,1,1,1,1,1,0); + break; + case '>': + mask = M (0,0,1,0,1,1,1,1,1,0,0,0); + break; + case '<': + mask = M (0,0,1,1,0,1,1,1,1,1,1,0); + break; + case 'm': + mask = M (1,1,1,1,1,0,0,0,0,0,0,0); + break; + case 'n': + mask = M (0,0,0,0,0,1,0,0,0,1,0,0); + break; + case 'o': + mask = M (0,0,0,0,0,0,1,1,1,0,1,1); + break; + case 'p': + mask = M (1,1,1,1,1,1,0,0,0,0,0,0); + break; + case 'q': + mask = M (1,0,1,1,1,1,0,0,0,0,0,0); + break; + case 'v': + mask = M (1,0,1,1,1,1,0,1,1,0,0,0); + break; + case 'b': + mask = M (1,0,1,1,1,1,0,0,0,1,0,0); + break; + case 'w': + mask = M (0,0,1,1,1,1,0,0,0,1,0,0); + break; + case 'y': + mask = M (0,0,1,0,0,1,0,0,0,0,0,0); + break; + case 'z': + mask = M (0,0,1,0,0,1,0,0,0,1,0,0); + break; + case '4': + mask = M (0,0,1,1,1,1,0,0,0,0,0,0); + break; + default: + abort (); + } +#undef M + + mode = (val >> 3) & 7; + if (mode == 7) + mode += val & 7; + return (mask & (1 << mode)) != 0; +} + +/* Print a base register REGNO and displacement DISP, on INFO->STREAM. + REGNO = -1 for pc, -2 for none (suppressed). */ + +static void +print_base (int regno, bfd_vma disp, disassemble_info *info) +{ + if (regno == -1) + { + (*info->fprintf_func) (info->stream, "%%pc@("); + (*info->print_address_func) (disp, info); + } + else + { + char buf[50]; + + if (regno == -2) + (*info->fprintf_func) (info->stream, "@("); + else if (regno == -3) + (*info->fprintf_func) (info->stream, "%%zpc@("); + else + (*info->fprintf_func) (info->stream, "%s@(", reg_names[regno]); + + sprintf_vma (buf, disp); + (*info->fprintf_func) (info->stream, "%s", buf); + } +} + +/* Print an indexed argument. The base register is BASEREG (-1 for pc). + P points to extension word, in buffer. + ADDR is the nominal core address of that extension word. */ + +static unsigned char * +print_indexed (int basereg, + unsigned char *p, + bfd_vma addr, + disassemble_info *info) +{ + int word; + static const char *const scales[] = { "", ":2", ":4", ":8" }; + bfd_vma base_disp; + bfd_vma outer_disp; + char buf[40]; + char vmabuf[50]; + + word = NEXTWORD (p); + + /* Generate the text for the index register. + Where this will be output is not yet determined. */ + sprintf (buf, "%s:%c%s", + reg_names[(word >> 12) & 0xf], + (word & 0x800) ? 'l' : 'w', + scales[(word >> 9) & 3]); + + /* Handle the 68000 style of indexing. */ + + if ((word & 0x100) == 0) + { + base_disp = word & 0xff; + if ((base_disp & 0x80) != 0) + base_disp -= 0x100; + if (basereg == -1) + base_disp += addr; + print_base (basereg, base_disp, info); + (*info->fprintf_func) (info->stream, ",%s)", buf); + return p; + } + + /* Handle the generalized kind. */ + /* First, compute the displacement to add to the base register. */ + if (word & 0200) + { + if (basereg == -1) + basereg = -3; + else + basereg = -2; + } + if (word & 0100) + buf[0] = '\0'; + base_disp = 0; + switch ((word >> 4) & 3) + { + case 2: + base_disp = NEXTWORD (p); + break; + case 3: + base_disp = NEXTLONG (p); + } + if (basereg == -1) + base_disp += addr; + + /* Handle single-level case (not indirect). */ + if ((word & 7) == 0) + { + print_base (basereg, base_disp, info); + if (buf[0] != '\0') + (*info->fprintf_func) (info->stream, ",%s", buf); + (*info->fprintf_func) (info->stream, ")"); + return p; + } + + /* Two level. Compute displacement to add after indirection. */ + outer_disp = 0; + switch (word & 3) + { + case 2: + outer_disp = NEXTWORD (p); + break; + case 3: + outer_disp = NEXTLONG (p); + } + + print_base (basereg, base_disp, info); + if ((word & 4) == 0 && buf[0] != '\0') + { + (*info->fprintf_func) (info->stream, ",%s", buf); + buf[0] = '\0'; + } + sprintf_vma (vmabuf, outer_disp); + (*info->fprintf_func) (info->stream, ")@(%s", vmabuf); + if (buf[0] != '\0') + (*info->fprintf_func) (info->stream, ",%s", buf); + (*info->fprintf_func) (info->stream, ")"); + + return p; +} + +/* Returns number of bytes "eaten" by the operand, or + return -1 if an invalid operand was found, or -2 if + an opcode tabe error was found. + ADDR is the pc for this arg to be relative to. */ + +static int +print_insn_arg (const char *d, + unsigned char *buffer, + unsigned char *p0, + bfd_vma addr, + disassemble_info *info) +{ + int val = 0; + int place = d[1]; + unsigned char *p = p0; + int regno; + const char *regname; + unsigned char *p1; + double flval; + int flt_p; + bfd_signed_vma disp; + unsigned int uval; + + switch (*d) + { + case 'c': /* Cache identifier. */ + { + static const char *const cacheFieldName[] = { "nc", "dc", "ic", "bc" }; + val = fetch_arg (buffer, place, 2, info); + (*info->fprintf_func) (info->stream, "%s", cacheFieldName[val]); + break; + } + + case 'a': /* Address register indirect only. Cf. case '+'. */ + { + (*info->fprintf_func) + (info->stream, + "%s@", + reg_names[fetch_arg (buffer, place, 3, info) + 8]); + break; + } + + case '_': /* 32-bit absolute address for move16. */ + { + uval = NEXTULONG (p); + (*info->print_address_func) (uval, info); + break; + } + + case 'C': + (*info->fprintf_func) (info->stream, "%%ccr"); + break; + + case 'S': + (*info->fprintf_func) (info->stream, "%%sr"); + break; + + case 'U': + (*info->fprintf_func) (info->stream, "%%usp"); + break; + + case 'E': + (*info->fprintf_func) (info->stream, "%%acc"); + break; + + case 'G': + (*info->fprintf_func) (info->stream, "%%macsr"); + break; + + case 'H': + (*info->fprintf_func) (info->stream, "%%mask"); + break; + + case 'J': + { + /* FIXME: There's a problem here, different m68k processors call the + same address different names. This table can't get it right + because it doesn't know which processor it's disassembling for. */ + static const struct { const char *name; int value; } names[] + = {{"%sfc", 0x000}, {"%dfc", 0x001}, {"%cacr", 0x002}, + {"%tc", 0x003}, {"%itt0",0x004}, {"%itt1", 0x005}, + {"%dtt0",0x006}, {"%dtt1",0x007}, {"%buscr",0x008}, + {"%usp", 0x800}, {"%vbr", 0x801}, {"%caar", 0x802}, + {"%msp", 0x803}, {"%isp", 0x804}, + {"%flashbar", 0xc04}, {"%rambar", 0xc05}, /* mcf528x added these. */ + + /* Should we be calling this psr like we do in case 'Y'? */ + {"%mmusr",0x805}, + + {"%urp", 0x806}, {"%srp", 0x807}, {"%pcr", 0x808}}; + + val = fetch_arg (buffer, place, 12, info); + for (regno = sizeof names / sizeof names[0] - 1; regno >= 0; regno--) + if (names[regno].value == val) + { + (*info->fprintf_func) (info->stream, "%s", names[regno].name); + break; + } + if (regno < 0) + (*info->fprintf_func) (info->stream, "%d", val); + } + break; + + case 'Q': + val = fetch_arg (buffer, place, 3, info); + /* 0 means 8, except for the bkpt instruction... */ + if (val == 0 && d[1] != 's') + val = 8; + (*info->fprintf_func) (info->stream, "#%d", val); + break; + + case 'x': + val = fetch_arg (buffer, place, 3, info); + /* 0 means -1. */ + if (val == 0) + val = -1; + (*info->fprintf_func) (info->stream, "#%d", val); + break; + + case 'M': + if (place == 'h') + { + static const char *const scalefactor_name[] = { "<<", ">>" }; + val = fetch_arg (buffer, place, 1, info); + (*info->fprintf_func) (info->stream, "%s", scalefactor_name[val]); + } + else + { + val = fetch_arg (buffer, place, 8, info); + if (val & 0x80) + val = val - 0x100; + (*info->fprintf_func) (info->stream, "#%d", val); + } + break; + + case 'T': + val = fetch_arg (buffer, place, 4, info); + (*info->fprintf_func) (info->stream, "#%d", val); + break; + + case 'D': + (*info->fprintf_func) (info->stream, "%s", + reg_names[fetch_arg (buffer, place, 3, info)]); + break; + + case 'A': + (*info->fprintf_func) + (info->stream, "%s", + reg_names[fetch_arg (buffer, place, 3, info) + 010]); + break; + + case 'R': + (*info->fprintf_func) + (info->stream, "%s", + reg_names[fetch_arg (buffer, place, 4, info)]); + break; + + case 'r': + regno = fetch_arg (buffer, place, 4, info); + if (regno > 7) + (*info->fprintf_func) (info->stream, "%s@", reg_names[regno]); + else + (*info->fprintf_func) (info->stream, "@(%s)", reg_names[regno]); + break; + + case 'F': + (*info->fprintf_func) + (info->stream, "%%fp%d", + fetch_arg (buffer, place, 3, info)); + break; + + case 'O': + val = fetch_arg (buffer, place, 6, info); + if (val & 0x20) + (*info->fprintf_func) (info->stream, "%s", reg_names[val & 7]); + else + (*info->fprintf_func) (info->stream, "%d", val); + break; + + case '+': + (*info->fprintf_func) + (info->stream, "%s@+", + reg_names[fetch_arg (buffer, place, 3, info) + 8]); + break; + + case '-': + (*info->fprintf_func) + (info->stream, "%s@-", + reg_names[fetch_arg (buffer, place, 3, info) + 8]); + break; + + case 'k': + if (place == 'k') + (*info->fprintf_func) + (info->stream, "{%s}", + reg_names[fetch_arg (buffer, place, 3, info)]); + else if (place == 'C') + { + val = fetch_arg (buffer, place, 7, info); + if (val > 63) /* This is a signed constant. */ + val -= 128; + (*info->fprintf_func) (info->stream, "{#%d}", val); + } + else + return -2; + break; + + case '#': + case '^': + p1 = buffer + (*d == '#' ? 2 : 4); + if (place == 's') + val = fetch_arg (buffer, place, 4, info); + else if (place == 'C') + val = fetch_arg (buffer, place, 7, info); + else if (place == '8') + val = fetch_arg (buffer, place, 3, info); + else if (place == '3') + val = fetch_arg (buffer, place, 8, info); + else if (place == 'b') + val = NEXTBYTE (p1); + else if (place == 'w' || place == 'W') + val = NEXTWORD (p1); + else if (place == 'l') + val = NEXTLONG (p1); + else + return -2; + (*info->fprintf_func) (info->stream, "#%d", val); + break; + + case 'B': + if (place == 'b') + disp = NEXTBYTE (p); + else if (place == 'B') + disp = COERCE_SIGNED_CHAR (buffer[1]); + else if (place == 'w' || place == 'W') + disp = NEXTWORD (p); + else if (place == 'l' || place == 'L' || place == 'C') + disp = NEXTLONG (p); + else if (place == 'g') + { + disp = NEXTBYTE (buffer); + if (disp == 0) + disp = NEXTWORD (p); + else if (disp == -1) + disp = NEXTLONG (p); + } + else if (place == 'c') + { + if (buffer[1] & 0x40) /* If bit six is one, long offset. */ + disp = NEXTLONG (p); + else + disp = NEXTWORD (p); + } + else + return -2; + + (*info->print_address_func) (addr + disp, info); + break; + + case 'd': + val = NEXTWORD (p); + (*info->fprintf_func) + (info->stream, "%s@(%d)", + reg_names[fetch_arg (buffer, place, 3, info) + 8], val); + break; + + case 's': + (*info->fprintf_func) (info->stream, "%s", + fpcr_names[fetch_arg (buffer, place, 3, info)]); + break; + + case 'e': + val = fetch_arg(buffer, place, 2, info); + (*info->fprintf_func) (info->stream, "%%acc%d", val); + break; + + case 'g': + val = fetch_arg(buffer, place, 1, info); + (*info->fprintf_func) (info->stream, "%%accext%s", val==0 ? "01" : "23"); + break; + + case 'i': + val = fetch_arg(buffer, place, 2, info); + if (val == 1) + (*info->fprintf_func) (info->stream, "<<"); + else if (val == 3) + (*info->fprintf_func) (info->stream, ">>"); + else + return -1; + break; + + case 'I': + /* Get coprocessor ID... */ + val = fetch_arg (buffer, 'd', 3, info); + + if (val != 1) /* Unusual coprocessor ID? */ + (*info->fprintf_func) (info->stream, "(cpid=%d) ", val); + break; + + case '4': + case '*': + case '~': + case '%': + case ';': + case '@': + case '!': + case '$': + case '?': + case '/': + case '&': + case '|': + case '<': + case '>': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'v': + case 'b': + case 'w': + case 'y': + case 'z': + if (place == 'd') + { + val = fetch_arg (buffer, 'x', 6, info); + val = ((val & 7) << 3) + ((val >> 3) & 7); + } + else + val = fetch_arg (buffer, 's', 6, info); + + /* If the is invalid for *d, then reject this match. */ + if (!m68k_valid_ea (*d, val)) + return -1; + + /* Get register number assuming address register. */ + regno = (val & 7) + 8; + regname = reg_names[regno]; + switch (val >> 3) + { + case 0: + (*info->fprintf_func) (info->stream, "%s", reg_names[val]); + break; + + case 1: + (*info->fprintf_func) (info->stream, "%s", regname); + break; + + case 2: + (*info->fprintf_func) (info->stream, "%s@", regname); + break; + + case 3: + (*info->fprintf_func) (info->stream, "%s@+", regname); + break; + + case 4: + (*info->fprintf_func) (info->stream, "%s@-", regname); + break; + + case 5: + val = NEXTWORD (p); + (*info->fprintf_func) (info->stream, "%s@(%d)", regname, val); + break; + + case 6: + p = print_indexed (regno, p, addr, info); + break; + + case 7: + switch (val & 7) + { + case 0: + val = NEXTWORD (p); + (*info->print_address_func) (val, info); + break; + + case 1: + uval = NEXTULONG (p); + (*info->print_address_func) (uval, info); + break; + + case 2: + val = NEXTWORD (p); + (*info->fprintf_func) (info->stream, "%%pc@("); + (*info->print_address_func) (addr + val, info); + (*info->fprintf_func) (info->stream, ")"); + break; + + case 3: + p = print_indexed (-1, p, addr, info); + break; + + case 4: + flt_p = 1; /* Assume it's a float... */ + switch (place) + { + case 'b': + val = NEXTBYTE (p); + flt_p = 0; + break; + + case 'w': + val = NEXTWORD (p); + flt_p = 0; + break; + + case 'l': + val = NEXTLONG (p); + flt_p = 0; + break; + + case 'f': + NEXTSINGLE (flval, p); + break; + + case 'F': + NEXTDOUBLE (flval, p); + break; + + case 'x': + NEXTEXTEND (flval, p); + break; + + case 'p': + flval = NEXTPACKED (p); + break; + + default: + return -1; + } + if (flt_p) /* Print a float? */ + (*info->fprintf_func) (info->stream, "#%g", flval); + else + (*info->fprintf_func) (info->stream, "#%d", val); + break; + + default: + return -1; + } + } + + /* If place is '/', then this is the case of the mask bit for + mac/emac loads. Now that the arg has been printed, grab the + mask bit and if set, add a '&' to the arg. */ + if (place == '/') + { + val = fetch_arg (buffer, place, 1, info); + if (val) + info->fprintf_func (info->stream, "&"); + } + break; + + case 'L': + case 'l': + if (place == 'w') + { + char doneany; + p1 = buffer + 2; + val = NEXTWORD (p1); + /* Move the pointer ahead if this point is farther ahead + than the last. */ + p = p1 > p ? p1 : p; + if (val == 0) + { + (*info->fprintf_func) (info->stream, "#0"); + break; + } + if (*d == 'l') + { + int newval = 0; + + for (regno = 0; regno < 16; ++regno) + if (val & (0x8000 >> regno)) + newval |= 1 << regno; + val = newval; + } + val &= 0xffff; + doneany = 0; + for (regno = 0; regno < 16; ++regno) + if (val & (1 << regno)) + { + int first_regno; + + if (doneany) + (*info->fprintf_func) (info->stream, "/"); + doneany = 1; + (*info->fprintf_func) (info->stream, "%s", reg_names[regno]); + first_regno = regno; + while (val & (1 << (regno + 1))) + ++regno; + if (regno > first_regno) + (*info->fprintf_func) (info->stream, "-%s", + reg_names[regno]); + } + } + else if (place == '3') + { + /* `fmovem' insn. */ + char doneany; + val = fetch_arg (buffer, place, 8, info); + if (val == 0) + { + (*info->fprintf_func) (info->stream, "#0"); + break; + } + if (*d == 'l') + { + int newval = 0; + + for (regno = 0; regno < 8; ++regno) + if (val & (0x80 >> regno)) + newval |= 1 << regno; + val = newval; + } + val &= 0xff; + doneany = 0; + for (regno = 0; regno < 8; ++regno) + if (val & (1 << regno)) + { + int first_regno; + if (doneany) + (*info->fprintf_func) (info->stream, "/"); + doneany = 1; + (*info->fprintf_func) (info->stream, "%%fp%d", regno); + first_regno = regno; + while (val & (1 << (regno + 1))) + ++regno; + if (regno > first_regno) + (*info->fprintf_func) (info->stream, "-%%fp%d", regno); + } + } + else if (place == '8') + { + /* fmoveml for FP status registers. */ + (*info->fprintf_func) (info->stream, "%s", + fpcr_names[fetch_arg (buffer, place, 3, + info)]); + } + else + return -2; + break; + + case 'X': + place = '8'; + case 'Y': + case 'Z': + case 'W': + case '0': + case '1': + case '2': + case '3': + { + int val = fetch_arg (buffer, place, 5, info); + const char *name = 0; + + switch (val) + { + case 2: name = "%tt0"; break; + case 3: name = "%tt1"; break; + case 0x10: name = "%tc"; break; + case 0x11: name = "%drp"; break; + case 0x12: name = "%srp"; break; + case 0x13: name = "%crp"; break; + case 0x14: name = "%cal"; break; + case 0x15: name = "%val"; break; + case 0x16: name = "%scc"; break; + case 0x17: name = "%ac"; break; + case 0x18: name = "%psr"; break; + case 0x19: name = "%pcsr"; break; + case 0x1c: + case 0x1d: + { + int break_reg = ((buffer[3] >> 2) & 7); + + (*info->fprintf_func) + (info->stream, val == 0x1c ? "%%bad%d" : "%%bac%d", + break_reg); + } + break; + default: + (*info->fprintf_func) (info->stream, "", val); + } + if (name) + (*info->fprintf_func) (info->stream, "%s", name); + } + break; + + case 'f': + { + int fc = fetch_arg (buffer, place, 5, info); + + if (fc == 1) + (*info->fprintf_func) (info->stream, "%%dfc"); + else if (fc == 0) + (*info->fprintf_func) (info->stream, "%%sfc"); + else + /* xgettext:c-format */ + (*info->fprintf_func) (info->stream, _(""), fc); + } + break; + + case 'V': + (*info->fprintf_func) (info->stream, "%%val"); + break; + + case 't': + { + int level = fetch_arg (buffer, place, 3, info); + + (*info->fprintf_func) (info->stream, "%d", level); + } + break; + + case 'u': + { + short is_upper = 0; + int reg = fetch_arg (buffer, place, 5, info); + + if (reg & 0x10) + { + is_upper = 1; + reg &= 0xf; + } + (*info->fprintf_func) (info->stream, "%s%s", + reg_half_names[reg], + is_upper ? "u" : "l"); + } + break; + + default: + return -2; + } + + return p - p0; +} + +/* Try to match the current instruction to best and if so, return the + number of bytes consumed from the instruction stream, else zero. */ + +static int +match_insn_m68k (bfd_vma memaddr, + disassemble_info * info, + const struct m68k_opcode * best, + struct private * priv) +{ + unsigned char *save_p; + unsigned char *p; + const char *d; + + bfd_byte *buffer = priv->the_buffer; + fprintf_function save_printer = info->fprintf_func; + void (* save_print_address) (bfd_vma, struct disassemble_info *) + = info->print_address_func; + + /* Point at first word of argument data, + and at descriptor for first argument. */ + p = buffer + 2; + + /* Figure out how long the fixed-size portion of the instruction is. + The only place this is stored in the opcode table is + in the arguments--look for arguments which specify fields in the 2nd + or 3rd words of the instruction. */ + for (d = best->args; *d; d += 2) + { + /* I don't think it is necessary to be checking d[0] here; + I suspect all this could be moved to the case statement below. */ + if (d[0] == '#') + { + if (d[1] == 'l' && p - buffer < 6) + p = buffer + 6; + else if (p - buffer < 4 && d[1] != 'C' && d[1] != '8') + p = buffer + 4; + } + + if ((d[0] == 'L' || d[0] == 'l') && d[1] == 'w' && p - buffer < 4) + p = buffer + 4; + + switch (d[1]) + { + case '1': + case '2': + case '3': + case '7': + case '8': + case '9': + case 'i': + if (p - buffer < 4) + p = buffer + 4; + break; + case '4': + case '5': + case '6': + if (p - buffer < 6) + p = buffer + 6; + break; + default: + break; + } + } + + /* pflusha is an exceptions. It takes no arguments but is two words + long. Recognize it by looking at the lower 16 bits of the mask. */ + if (p - buffer < 4 && (best->match & 0xFFFF) != 0) + p = buffer + 4; + + /* lpstop is another exception. It takes a one word argument but is + three words long. */ + if (p - buffer < 6 + && (best->match & 0xffff) == 0xffff + && best->args[0] == '#' + && best->args[1] == 'w') + { + /* Copy the one word argument into the usual location for a one + word argument, to simplify printing it. We can get away with + this because we know exactly what the second word is, and we + aren't going to print anything based on it. */ + p = buffer + 6; + fetch_data(info, p); + buffer[2] = buffer[4]; + buffer[3] = buffer[5]; + } + + fetch_data(info, p); + + d = best->args; + + save_p = p; + info->print_address_func = dummy_print_address; + info->fprintf_func = dummy_printer; + + /* We scan the operands twice. The first time we don't print anything, + but look for errors. */ + for (; *d; d += 2) + { + int eaten = print_insn_arg (d, buffer, p, memaddr + (p - buffer), info); + + if (eaten >= 0) + p += eaten; + else if (eaten == -1) + { + info->fprintf_func = save_printer; + info->print_address_func = save_print_address; + return 0; + } + else + { + info->fprintf_func (info->stream, + /* xgettext:c-format */ + _("\n"), + best->name, best->args); + info->fprintf_func = save_printer; + info->print_address_func = save_print_address; + return 2; + } + } + + p = save_p; + info->fprintf_func = save_printer; + info->print_address_func = save_print_address; + + d = best->args; + + info->fprintf_func (info->stream, "%s", best->name); + + if (*d) + info->fprintf_func (info->stream, " "); + + while (*d) + { + p += print_insn_arg (d, buffer, p, memaddr + (p - buffer), info); + d += 2; + + if (*d && *(d - 2) != 'I' && *d != 'k') + info->fprintf_func (info->stream, ","); + } + + return p - buffer; +} + +/* Print the m68k instruction at address MEMADDR in debugged memory, + on INFO->STREAM. Returns length of the instruction, in bytes. */ + +int +print_insn_m68k (bfd_vma memaddr, disassemble_info *info) +{ + int i; + const char *d; + unsigned int arch_mask; + struct private priv; + bfd_byte *buffer = priv.the_buffer; + int major_opcode; + static int numopcodes[16]; + static const struct m68k_opcode **opcodes[16]; + int val; + + if (!opcodes[0]) + { + /* Speed up the matching by sorting the opcode + table on the upper four bits of the opcode. */ + const struct m68k_opcode **opc_pointer[16]; + + /* First count how many opcodes are in each of the sixteen buckets. */ + for (i = 0; i < m68k_numopcodes; i++) + numopcodes[(m68k_opcodes[i].opcode >> 28) & 15]++; + + /* Then create a sorted table of pointers + that point into the unsorted table. */ + opc_pointer[0] = malloc (sizeof (struct m68k_opcode *) + * m68k_numopcodes); + opcodes[0] = opc_pointer[0]; + + for (i = 1; i < 16; i++) + { + opc_pointer[i] = opc_pointer[i - 1] + numopcodes[i - 1]; + opcodes[i] = opc_pointer[i]; + } + + for (i = 0; i < m68k_numopcodes; i++) + *opc_pointer[(m68k_opcodes[i].opcode >> 28) & 15]++ = &m68k_opcodes[i]; + } + + info->private_data = (PTR) &priv; + /* Tell objdump to use two bytes per chunk + and six bytes per line for displaying raw data. */ + info->bytes_per_chunk = 2; + info->bytes_per_line = 6; + info->display_endian = BFD_ENDIAN_BIG; + priv.max_fetched = priv.the_buffer; + priv.insn_start = memaddr; + + if (sigsetjmp(priv.bailout, 0) != 0) { + /* Error return. */ + return -1; + } + + switch (info->mach) + { + default: + case 0: + arch_mask = (unsigned int) -1; + break; + case bfd_mach_m68000: + arch_mask = m68000|m68881|m68851; + break; + case bfd_mach_m68008: + arch_mask = m68008|m68881|m68851; + break; + case bfd_mach_m68010: + arch_mask = m68010|m68881|m68851; + break; + case bfd_mach_m68020: + arch_mask = m68020|m68881|m68851; + break; + case bfd_mach_m68030: + arch_mask = m68030|m68881|m68851; + break; + case bfd_mach_m68040: + arch_mask = m68040|m68881|m68851; + break; + case bfd_mach_m68060: + arch_mask = m68060|m68881|m68851; + break; + case bfd_mach_mcf5200: + arch_mask = mcfisa_a; + break; + case bfd_mach_mcf521x: + case bfd_mach_mcf528x: + arch_mask = mcfisa_a|mcfhwdiv|mcfisa_aa|mcfusp|mcfemac; + break; + case bfd_mach_mcf5206e: + arch_mask = mcfisa_a|mcfhwdiv|mcfmac; + break; + case bfd_mach_mcf5249: + arch_mask = mcfisa_a|mcfhwdiv|mcfemac; + break; + case bfd_mach_mcf5307: + arch_mask = mcfisa_a|mcfhwdiv|mcfmac; + break; + case bfd_mach_mcf5407: + arch_mask = mcfisa_a|mcfhwdiv|mcfisa_b|mcfmac; + break; + case bfd_mach_mcf547x: + case bfd_mach_mcf548x: + case bfd_mach_mcfv4e: + arch_mask = mcfisa_a|mcfhwdiv|mcfisa_b|mcfusp|cfloat|mcfemac; + break; + } + + fetch_data(info, buffer + 2); + major_opcode = (buffer[0] >> 4) & 15; + + for (i = 0; i < numopcodes[major_opcode]; i++) + { + const struct m68k_opcode *opc = opcodes[major_opcode][i]; + unsigned long opcode = opc->opcode; + unsigned long match = opc->match; + + if (((0xff & buffer[0] & (match >> 24)) == (0xff & (opcode >> 24))) + && ((0xff & buffer[1] & (match >> 16)) == (0xff & (opcode >> 16))) + /* Only fetch the next two bytes if we need to. */ + && (((0xffff & match) == 0) + || + (fetch_data(info, buffer + 4) + && ((0xff & buffer[2] & (match >> 8)) == (0xff & (opcode >> 8))) + && ((0xff & buffer[3] & match) == (0xff & opcode))) + ) + && (opc->arch & arch_mask) != 0) + { + /* Don't use for printout the variants of divul and divsl + that have the same register number in two places. + The more general variants will match instead. */ + for (d = opc->args; *d; d += 2) + if (d[1] == 'D') + break; + + /* Don't use for printout the variants of most floating + point coprocessor instructions which use the same + register number in two places, as above. */ + if (*d == '\0') + for (d = opc->args; *d; d += 2) + if (d[1] == 't') + break; + + /* Don't match fmovel with more than one register; + wait for fmoveml. */ + if (*d == '\0') + { + for (d = opc->args; *d; d += 2) + { + if (d[0] == 's' && d[1] == '8') + { + val = fetch_arg (buffer, d[1], 3, info); + if ((val & (val - 1)) != 0) + break; + } + } + } + + if (*d == '\0') + if ((val = match_insn_m68k (memaddr, info, opc, & priv))) + return val; + } + } + + /* Handle undefined instructions. */ + info->fprintf_func (info->stream, "0%o", (buffer[0] << 8) + buffer[1]); + return 2; +} +/* **** End of m68k-dis.c */ +/* **** m68k-opc.h from sourceware.org CVS 2005-08-14. */ +/* Opcode table for m680[012346]0/m6888[12]/m68851/mcf5200. + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2003, 2004, 2005 + Free Software Foundation, Inc. + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 1, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, + see . */ + +#define one(x) ((unsigned int) (x) << 16) +#define two(x, y) (((unsigned int) (x) << 16) + (y)) + +/* The assembler requires that all instances of the same mnemonic must + be consecutive. If they aren't, the assembler will bomb at + runtime. */ + +const struct m68k_opcode m68k_opcodes[] = +{ +{"abcd", 2, one(0140400), one(0170770), "DsDd", m68000up }, +{"abcd", 2, one(0140410), one(0170770), "-s-d", m68000up }, + +{"addaw", 2, one(0150300), one(0170700), "*wAd", m68000up }, +{"addal", 2, one(0150700), one(0170700), "*lAd", m68000up | mcfisa_a }, + +{"addib", 4, one(0003000), one(0177700), "#b$s", m68000up }, +{"addiw", 4, one(0003100), one(0177700), "#w$s", m68000up }, +{"addil", 6, one(0003200), one(0177700), "#l$s", m68000up }, +{"addil", 6, one(0003200), one(0177700), "#lDs", mcfisa_a }, + +{"addqb", 2, one(0050000), one(0170700), "Qd$b", m68000up }, +{"addqw", 2, one(0050100), one(0170700), "Qd%w", m68000up }, +{"addql", 2, one(0050200), one(0170700), "Qd%l", m68000up | mcfisa_a }, + +/* The add opcode can generate the adda, addi, and addq instructions. */ +{"addb", 2, one(0050000), one(0170700), "Qd$b", m68000up }, +{"addb", 4, one(0003000), one(0177700), "#b$s", m68000up }, +{"addb", 2, one(0150000), one(0170700), ";bDd", m68000up }, +{"addb", 2, one(0150400), one(0170700), "Dd~b", m68000up }, +{"addw", 2, one(0050100), one(0170700), "Qd%w", m68000up }, +{"addw", 2, one(0150300), one(0170700), "*wAd", m68000up }, +{"addw", 4, one(0003100), one(0177700), "#w$s", m68000up }, +{"addw", 2, one(0150100), one(0170700), "*wDd", m68000up }, +{"addw", 2, one(0150500), one(0170700), "Dd~w", m68000up }, +{"addl", 2, one(0050200), one(0170700), "Qd%l", m68000up | mcfisa_a }, +{"addl", 6, one(0003200), one(0177700), "#l$s", m68000up }, +{"addl", 6, one(0003200), one(0177700), "#lDs", mcfisa_a }, +{"addl", 2, one(0150700), one(0170700), "*lAd", m68000up | mcfisa_a }, +{"addl", 2, one(0150200), one(0170700), "*lDd", m68000up | mcfisa_a }, +{"addl", 2, one(0150600), one(0170700), "Dd~l", m68000up | mcfisa_a }, + +{"addxb", 2, one(0150400), one(0170770), "DsDd", m68000up }, +{"addxb", 2, one(0150410), one(0170770), "-s-d", m68000up }, +{"addxw", 2, one(0150500), one(0170770), "DsDd", m68000up }, +{"addxw", 2, one(0150510), one(0170770), "-s-d", m68000up }, +{"addxl", 2, one(0150600), one(0170770), "DsDd", m68000up | mcfisa_a }, +{"addxl", 2, one(0150610), one(0170770), "-s-d", m68000up }, + +{"andib", 4, one(0001000), one(0177700), "#b$s", m68000up }, +{"andib", 4, one(0001074), one(0177777), "#bCs", m68000up }, +{"andiw", 4, one(0001100), one(0177700), "#w$s", m68000up }, +{"andiw", 4, one(0001174), one(0177777), "#wSs", m68000up }, +{"andil", 6, one(0001200), one(0177700), "#l$s", m68000up }, +{"andil", 6, one(0001200), one(0177700), "#lDs", mcfisa_a }, +{"andi", 4, one(0001100), one(0177700), "#w$s", m68000up }, +{"andi", 4, one(0001074), one(0177777), "#bCs", m68000up }, +{"andi", 4, one(0001174), one(0177777), "#wSs", m68000up }, + +/* The and opcode can generate the andi instruction. */ +{"andb", 4, one(0001000), one(0177700), "#b$s", m68000up }, +{"andb", 4, one(0001074), one(0177777), "#bCs", m68000up }, +{"andb", 2, one(0140000), one(0170700), ";bDd", m68000up }, +{"andb", 2, one(0140400), one(0170700), "Dd~b", m68000up }, +{"andw", 4, one(0001100), one(0177700), "#w$s", m68000up }, +{"andw", 4, one(0001174), one(0177777), "#wSs", m68000up }, +{"andw", 2, one(0140100), one(0170700), ";wDd", m68000up }, +{"andw", 2, one(0140500), one(0170700), "Dd~w", m68000up }, +{"andl", 6, one(0001200), one(0177700), "#l$s", m68000up }, +{"andl", 6, one(0001200), one(0177700), "#lDs", mcfisa_a }, +{"andl", 2, one(0140200), one(0170700), ";lDd", m68000up | mcfisa_a }, +{"andl", 2, one(0140600), one(0170700), "Dd~l", m68000up | mcfisa_a }, +{"and", 4, one(0001100), one(0177700), "#w$w", m68000up }, +{"and", 4, one(0001074), one(0177777), "#bCs", m68000up }, +{"and", 4, one(0001174), one(0177777), "#wSs", m68000up }, +{"and", 2, one(0140100), one(0170700), ";wDd", m68000up }, +{"and", 2, one(0140500), one(0170700), "Dd~w", m68000up }, + +{"aslb", 2, one(0160400), one(0170770), "QdDs", m68000up }, +{"aslb", 2, one(0160440), one(0170770), "DdDs", m68000up }, +{"aslw", 2, one(0160500), one(0170770), "QdDs", m68000up }, +{"aslw", 2, one(0160540), one(0170770), "DdDs", m68000up }, +{"aslw", 2, one(0160700), one(0177700), "~s", m68000up }, +{"asll", 2, one(0160600), one(0170770), "QdDs", m68000up | mcfisa_a }, +{"asll", 2, one(0160640), one(0170770), "DdDs", m68000up | mcfisa_a }, + +{"asrb", 2, one(0160000), one(0170770), "QdDs", m68000up }, +{"asrb", 2, one(0160040), one(0170770), "DdDs", m68000up }, +{"asrw", 2, one(0160100), one(0170770), "QdDs", m68000up }, +{"asrw", 2, one(0160140), one(0170770), "DdDs", m68000up }, +{"asrw", 2, one(0160300), one(0177700), "~s", m68000up }, +{"asrl", 2, one(0160200), one(0170770), "QdDs", m68000up | mcfisa_a }, +{"asrl", 2, one(0160240), one(0170770), "DdDs", m68000up | mcfisa_a }, + +{"bhiw", 2, one(0061000), one(0177777), "BW", m68000up | mcfisa_a }, +{"blsw", 2, one(0061400), one(0177777), "BW", m68000up | mcfisa_a }, +{"bccw", 2, one(0062000), one(0177777), "BW", m68000up | mcfisa_a }, +{"bcsw", 2, one(0062400), one(0177777), "BW", m68000up | mcfisa_a }, +{"bnew", 2, one(0063000), one(0177777), "BW", m68000up | mcfisa_a }, +{"beqw", 2, one(0063400), one(0177777), "BW", m68000up | mcfisa_a }, +{"bvcw", 2, one(0064000), one(0177777), "BW", m68000up | mcfisa_a }, +{"bvsw", 2, one(0064400), one(0177777), "BW", m68000up | mcfisa_a }, +{"bplw", 2, one(0065000), one(0177777), "BW", m68000up | mcfisa_a }, +{"bmiw", 2, one(0065400), one(0177777), "BW", m68000up | mcfisa_a }, +{"bgew", 2, one(0066000), one(0177777), "BW", m68000up | mcfisa_a }, +{"bltw", 2, one(0066400), one(0177777), "BW", m68000up | mcfisa_a }, +{"bgtw", 2, one(0067000), one(0177777), "BW", m68000up | mcfisa_a }, +{"blew", 2, one(0067400), one(0177777), "BW", m68000up | mcfisa_a }, + +{"bhil", 2, one(0061377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"blsl", 2, one(0061777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bccl", 2, one(0062377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bcsl", 2, one(0062777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bnel", 2, one(0063377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"beql", 2, one(0063777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bvcl", 2, one(0064377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bvsl", 2, one(0064777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bpll", 2, one(0065377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bmil", 2, one(0065777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bgel", 2, one(0066377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bltl", 2, one(0066777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bgtl", 2, one(0067377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"blel", 2, one(0067777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, + +{"bhis", 2, one(0061000), one(0177400), "BB", m68000up | mcfisa_a }, +{"blss", 2, one(0061400), one(0177400), "BB", m68000up | mcfisa_a }, +{"bccs", 2, one(0062000), one(0177400), "BB", m68000up | mcfisa_a }, +{"bcss", 2, one(0062400), one(0177400), "BB", m68000up | mcfisa_a }, +{"bnes", 2, one(0063000), one(0177400), "BB", m68000up | mcfisa_a }, +{"beqs", 2, one(0063400), one(0177400), "BB", m68000up | mcfisa_a }, +{"bvcs", 2, one(0064000), one(0177400), "BB", m68000up | mcfisa_a }, +{"bvss", 2, one(0064400), one(0177400), "BB", m68000up | mcfisa_a }, +{"bpls", 2, one(0065000), one(0177400), "BB", m68000up | mcfisa_a }, +{"bmis", 2, one(0065400), one(0177400), "BB", m68000up | mcfisa_a }, +{"bges", 2, one(0066000), one(0177400), "BB", m68000up | mcfisa_a }, +{"blts", 2, one(0066400), one(0177400), "BB", m68000up | mcfisa_a }, +{"bgts", 2, one(0067000), one(0177400), "BB", m68000up | mcfisa_a }, +{"bles", 2, one(0067400), one(0177400), "BB", m68000up | mcfisa_a }, + +{"jhi", 2, one(0061000), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jls", 2, one(0061400), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jcc", 2, one(0062000), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jcs", 2, one(0062400), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jne", 2, one(0063000), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jeq", 2, one(0063400), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jvc", 2, one(0064000), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jvs", 2, one(0064400), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jpl", 2, one(0065000), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jmi", 2, one(0065400), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jge", 2, one(0066000), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jlt", 2, one(0066400), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jgt", 2, one(0067000), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jle", 2, one(0067400), one(0177400), "Bg", m68000up | mcfisa_a }, + +{"bchg", 2, one(0000500), one(0170700), "Dd$s", m68000up | mcfisa_a }, +{"bchg", 4, one(0004100), one(0177700), "#b$s", m68000up }, +{"bchg", 4, one(0004100), one(0177700), "#bqs", mcfisa_a }, + +{"bclr", 2, one(0000600), one(0170700), "Dd$s", m68000up | mcfisa_a }, +{"bclr", 4, one(0004200), one(0177700), "#b$s", m68000up }, +{"bclr", 4, one(0004200), one(0177700), "#bqs", mcfisa_a }, + +{"bfchg", 4, two(0165300, 0), two(0177700, 0170000), "?sO2O3", m68020up }, +{"bfclr", 4, two(0166300, 0), two(0177700, 0170000), "?sO2O3", m68020up }, +{"bfexts", 4, two(0165700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up }, +{"bfextu", 4, two(0164700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up }, +{"bfffo", 4, two(0166700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up }, +{"bfins", 4, two(0167700, 0), two(0177700, 0100000), "D1?sO2O3", m68020up }, +{"bfset", 4, two(0167300, 0), two(0177700, 0170000), "?sO2O3", m68020up }, +{"bftst", 4, two(0164300, 0), two(0177700, 0170000), "/sO2O3", m68020up }, + +{"bgnd", 2, one(0045372), one(0177777), "", cpu32 }, + +{"bitrev", 2, one(0000300), one(0177770), "Ds", mcfisa_aa}, + +{"bkpt", 2, one(0044110), one(0177770), "ts", m68010up }, + +{"braw", 2, one(0060000), one(0177777), "BW", m68000up | mcfisa_a }, +{"bral", 2, one(0060377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bras", 2, one(0060000), one(0177400), "BB", m68000up | mcfisa_a }, + +{"bset", 2, one(0000700), one(0170700), "Dd$s", m68000up | mcfisa_a }, +{"bset", 2, one(0000700), one(0170700), "Ddvs", mcfisa_a }, +{"bset", 4, one(0004300), one(0177700), "#b$s", m68000up }, +{"bset", 4, one(0004300), one(0177700), "#bqs", mcfisa_a }, + +{"bsrw", 2, one(0060400), one(0177777), "BW", m68000up | mcfisa_a }, +{"bsrl", 2, one(0060777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b}, +{"bsrs", 2, one(0060400), one(0177400), "BB", m68000up | mcfisa_a }, + +{"btst", 2, one(0000400), one(0170700), "Dd;b", m68000up | mcfisa_a }, +{"btst", 4, one(0004000), one(0177700), "#b@s", m68000up }, +{"btst", 4, one(0004000), one(0177700), "#bqs", mcfisa_a }, + +{"byterev", 2, one(0001300), one(0177770), "Ds", mcfisa_aa}, + +{"callm", 4, one(0003300), one(0177700), "#b!s", m68020 }, + +{"cas2w", 6, two(0006374,0), two(0177777,0007070), "D3D6D2D5r1r4", m68020up }, +{"cas2w", 6, two(0006374,0), two(0177777,0007070), "D3D6D2D5R1R4", m68020up }, +{"cas2l", 6, two(0007374,0), two(0177777,0007070), "D3D6D2D5r1r4", m68020up }, +{"cas2l", 6, two(0007374,0), two(0177777,0007070), "D3D6D2D5R1R4", m68020up }, + +{"casb", 4, two(0005300, 0), two(0177700, 0177070), "D3D2~s", m68020up }, +{"casw", 4, two(0006300, 0), two(0177700, 0177070), "D3D2~s", m68020up }, +{"casl", 4, two(0007300, 0), two(0177700, 0177070), "D3D2~s", m68020up }, + +{"chk2b", 4, two(0000300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 }, +{"chk2w", 4, two(0001300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 }, +{"chk2l", 4, two(0002300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 }, + +{"chkl", 2, one(0040400), one(0170700), ";lDd", m68000up }, +{"chkw", 2, one(0040600), one(0170700), ";wDd", m68000up }, + +#define SCOPE_LINE (0x1 << 3) +#define SCOPE_PAGE (0x2 << 3) +#define SCOPE_ALL (0x3 << 3) + +{"cinva", 2, one(0xf400|SCOPE_ALL), one(0xff38), "ce", m68040up }, +{"cinvl", 2, one(0xf400|SCOPE_LINE), one(0xff38), "ceas", m68040up }, +{"cinvp", 2, one(0xf400|SCOPE_PAGE), one(0xff38), "ceas", m68040up }, + +{"cpusha", 2, one(0xf420|SCOPE_ALL), one(0xff38), "ce", m68040up }, +{"cpushl", 2, one(0xf420|SCOPE_LINE), one(0xff38), "ceas", m68040up | mcfisa_a }, +{"cpushp", 2, one(0xf420|SCOPE_PAGE), one(0xff38), "ceas", m68040up }, + +#undef SCOPE_LINE +#undef SCOPE_PAGE +#undef SCOPE_ALL + +{"clrb", 2, one(0041000), one(0177700), "$s", m68000up | mcfisa_a }, +{"clrw", 2, one(0041100), one(0177700), "$s", m68000up | mcfisa_a }, +{"clrl", 2, one(0041200), one(0177700), "$s", m68000up | mcfisa_a }, + +{"cmp2b", 4, two(0000300,0), two(0177700,07777), "!sR1", m68020up | cpu32 }, +{"cmp2w", 4, two(0001300,0), two(0177700,07777), "!sR1", m68020up | cpu32 }, +{"cmp2l", 4, two(0002300,0), two(0177700,07777), "!sR1", m68020up | cpu32 }, + +{"cmpaw", 2, one(0130300), one(0170700), "*wAd", m68000up }, +{"cmpal", 2, one(0130700), one(0170700), "*lAd", m68000up | mcfisa_a }, + +{"cmpib", 4, one(0006000), one(0177700), "#b@s", m68000up }, +{"cmpib", 4, one(0006000), one(0177700), "#bDs", mcfisa_b }, +{"cmpiw", 4, one(0006100), one(0177700), "#w@s", m68000up }, +{"cmpiw", 4, one(0006100), one(0177700), "#wDs", mcfisa_b }, +{"cmpil", 6, one(0006200), one(0177700), "#l@s", m68000up }, +{"cmpil", 6, one(0006200), one(0177700), "#lDs", mcfisa_a }, + +{"cmpmb", 2, one(0130410), one(0170770), "+s+d", m68000up }, +{"cmpmw", 2, one(0130510), one(0170770), "+s+d", m68000up }, +{"cmpml", 2, one(0130610), one(0170770), "+s+d", m68000up }, + +/* The cmp opcode can generate the cmpa, cmpm, and cmpi instructions. */ +{"cmpb", 4, one(0006000), one(0177700), "#b@s", m68000up }, +{"cmpb", 4, one(0006000), one(0177700), "#bDs", mcfisa_b }, +{"cmpb", 2, one(0130410), one(0170770), "+s+d", m68000up }, +{"cmpb", 2, one(0130000), one(0170700), ";bDd", m68000up }, +{"cmpb", 2, one(0130000), one(0170700), "*bDd", mcfisa_b }, +{"cmpw", 2, one(0130300), one(0170700), "*wAd", m68000up }, +{"cmpw", 4, one(0006100), one(0177700), "#w@s", m68000up }, +{"cmpw", 4, one(0006100), one(0177700), "#wDs", mcfisa_b }, +{"cmpw", 2, one(0130510), one(0170770), "+s+d", m68000up }, +{"cmpw", 2, one(0130100), one(0170700), "*wDd", m68000up | mcfisa_b }, +{"cmpl", 2, one(0130700), one(0170700), "*lAd", m68000up | mcfisa_a }, +{"cmpl", 6, one(0006200), one(0177700), "#l@s", m68000up }, +{"cmpl", 6, one(0006200), one(0177700), "#lDs", mcfisa_a }, +{"cmpl", 2, one(0130610), one(0170770), "+s+d", m68000up }, +{"cmpl", 2, one(0130200), one(0170700), "*lDd", m68000up | mcfisa_a }, + +{"dbcc", 2, one(0052310), one(0177770), "DsBw", m68000up }, +{"dbcs", 2, one(0052710), one(0177770), "DsBw", m68000up }, +{"dbeq", 2, one(0053710), one(0177770), "DsBw", m68000up }, +{"dbf", 2, one(0050710), one(0177770), "DsBw", m68000up }, +{"dbge", 2, one(0056310), one(0177770), "DsBw", m68000up }, +{"dbgt", 2, one(0057310), one(0177770), "DsBw", m68000up }, +{"dbhi", 2, one(0051310), one(0177770), "DsBw", m68000up }, +{"dble", 2, one(0057710), one(0177770), "DsBw", m68000up }, +{"dbls", 2, one(0051710), one(0177770), "DsBw", m68000up }, +{"dblt", 2, one(0056710), one(0177770), "DsBw", m68000up }, +{"dbmi", 2, one(0055710), one(0177770), "DsBw", m68000up }, +{"dbne", 2, one(0053310), one(0177770), "DsBw", m68000up }, +{"dbpl", 2, one(0055310), one(0177770), "DsBw", m68000up }, +{"dbt", 2, one(0050310), one(0177770), "DsBw", m68000up }, +{"dbvc", 2, one(0054310), one(0177770), "DsBw", m68000up }, +{"dbvs", 2, one(0054710), one(0177770), "DsBw", m68000up }, + +{"divsw", 2, one(0100700), one(0170700), ";wDd", m68000up | mcfhwdiv }, + +{"divsl", 4, two(0046100,0006000),two(0177700,0107770),";lD3D1", m68020up|cpu32 }, +{"divsl", 4, two(0046100,0004000),two(0177700,0107770),";lDD", m68020up|cpu32 }, +{"divsl", 4, two(0046100,0004000),two(0177700,0107770),"qsDD", mcfhwdiv }, + +{"divsll", 4, two(0046100,0004000),two(0177700,0107770),";lD3D1",m68020up|cpu32 }, +{"divsll", 4, two(0046100,0004000),two(0177700,0107770),";lDD", m68020up|cpu32 }, + +{"divuw", 2, one(0100300), one(0170700), ";wDd", m68000up | mcfhwdiv }, + +{"divul", 4, two(0046100,0002000),two(0177700,0107770),";lD3D1", m68020up|cpu32 }, +{"divul", 4, two(0046100,0000000),two(0177700,0107770),";lDD", m68020up|cpu32 }, +{"divul", 4, two(0046100,0000000),two(0177700,0107770),"qsDD", mcfhwdiv }, + +{"divull", 4, two(0046100,0000000),two(0177700,0107770),";lD3D1",m68020up|cpu32 }, +{"divull", 4, two(0046100,0000000),two(0177700,0107770),";lDD", m68020up|cpu32 }, + +{"eorib", 4, one(0005000), one(0177700), "#b$s", m68000up }, +{"eorib", 4, one(0005074), one(0177777), "#bCs", m68000up }, +{"eoriw", 4, one(0005100), one(0177700), "#w$s", m68000up }, +{"eoriw", 4, one(0005174), one(0177777), "#wSs", m68000up }, +{"eoril", 6, one(0005200), one(0177700), "#l$s", m68000up }, +{"eoril", 6, one(0005200), one(0177700), "#lDs", mcfisa_a }, +{"eori", 4, one(0005074), one(0177777), "#bCs", m68000up }, +{"eori", 4, one(0005174), one(0177777), "#wSs", m68000up }, +{"eori", 4, one(0005100), one(0177700), "#w$s", m68000up }, + +/* The eor opcode can generate the eori instruction. */ +{"eorb", 4, one(0005000), one(0177700), "#b$s", m68000up }, +{"eorb", 4, one(0005074), one(0177777), "#bCs", m68000up }, +{"eorb", 2, one(0130400), one(0170700), "Dd$s", m68000up }, +{"eorw", 4, one(0005100), one(0177700), "#w$s", m68000up }, +{"eorw", 4, one(0005174), one(0177777), "#wSs", m68000up }, +{"eorw", 2, one(0130500), one(0170700), "Dd$s", m68000up }, +{"eorl", 6, one(0005200), one(0177700), "#l$s", m68000up }, +{"eorl", 6, one(0005200), one(0177700), "#lDs", mcfisa_a }, +{"eorl", 2, one(0130600), one(0170700), "Dd$s", m68000up | mcfisa_a }, +{"eor", 4, one(0005074), one(0177777), "#bCs", m68000up }, +{"eor", 4, one(0005174), one(0177777), "#wSs", m68000up }, +{"eor", 4, one(0005100), one(0177700), "#w$s", m68000up }, +{"eor", 2, one(0130500), one(0170700), "Dd$s", m68000up }, + +{"exg", 2, one(0140500), one(0170770), "DdDs", m68000up }, +{"exg", 2, one(0140510), one(0170770), "AdAs", m68000up }, +{"exg", 2, one(0140610), one(0170770), "DdAs", m68000up }, +{"exg", 2, one(0140610), one(0170770), "AsDd", m68000up }, + +{"extw", 2, one(0044200), one(0177770), "Ds", m68000up|mcfisa_a }, +{"extl", 2, one(0044300), one(0177770), "Ds", m68000up|mcfisa_a }, +{"extbl", 2, one(0044700), one(0177770), "Ds", m68020up|cpu32|mcfisa_a }, + +{"ff1", 2, one(0002300), one(0177770), "Ds", mcfisa_aa}, + +/* float stuff starts here */ + +{"fabsb", 4, two(0xF000, 0x5818), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fabsb", 4, two(0xF000, 0x5818), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fabsd", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fabsd", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fabsd", 4, two(0xF000, 0x5418), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fabsd", 4, two(0xF000, 0x5418), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fabsl", 4, two(0xF000, 0x4018), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fabsl", 4, two(0xF000, 0x4018), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fabsp", 4, two(0xF000, 0x4C18), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fabss", 4, two(0xF000, 0x4418), two(0xF1C0, 0xFC7F), "Ii;fF7", cfloat }, +{"fabss", 4, two(0xF000, 0x4418), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fabsw", 4, two(0xF000, 0x5018), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fabsw", 4, two(0xF000, 0x5018), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fabsx", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fabsx", 4, two(0xF000, 0x4818), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fabsx", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fsabsb", 4, two(0xF000, 0x5858), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fsabsb", 4, two(0xF000, 0x5858), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsabsd", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fsabsd", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fsabsd", 4, two(0xF000, 0x5458), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fsabsd", 4, two(0xF000, 0x5458), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fsabsl", 4, two(0xF000, 0x4058), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fsabsl", 4, two(0xF000, 0x4058), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsabsp", 4, two(0xF000, 0x4C58), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fsabss", 4, two(0xF000, 0x4258), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsabss", 4, two(0xF000, 0x4458), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fsabsw", 4, two(0xF000, 0x5058), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fsabsw", 4, two(0xF000, 0x5058), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsabsx", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fsabsx", 4, two(0xF000, 0x4858), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fsabsx", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiFt", m68040up }, + +{"fdabsb", 4, two(0xF000, 0x585C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdabsb", 4, two(0xF000, 0x585c), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up}, +{"fdabsd", 4, two(0xF000, 0x005C), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fdabsd", 4, two(0xF000, 0x005C), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fdabsd", 4, two(0xF000, 0x545C), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fdabsd", 4, two(0xF000, 0x545c), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up}, +{"fdabsl", 4, two(0xF000, 0x405C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdabsl", 4, two(0xF000, 0x405c), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up}, +{"fdabsp", 4, two(0xF000, 0x4C5c), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up}, +{"fdabss", 4, two(0xF000, 0x425C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdabss", 4, two(0xF000, 0x445c), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up}, +{"fdabsw", 4, two(0xF000, 0x505C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdabsw", 4, two(0xF000, 0x505c), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up}, +{"fdabsx", 4, two(0xF000, 0x005c), two(0xF1C0, 0xE07F), "IiF8F7", m68040up}, +{"fdabsx", 4, two(0xF000, 0x485c), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up}, +{"fdabsx", 4, two(0xF000, 0x005c), two(0xF1C0, 0xE07F), "IiFt", m68040up}, + +{"facosb", 4, two(0xF000, 0x581C), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"facosd", 4, two(0xF000, 0x541C), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"facosl", 4, two(0xF000, 0x401C), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"facosp", 4, two(0xF000, 0x4C1C), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"facoss", 4, two(0xF000, 0x441C), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"facosw", 4, two(0xF000, 0x501C), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"facosx", 4, two(0xF000, 0x001C), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"facosx", 4, two(0xF000, 0x481C), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"facosx", 4, two(0xF000, 0x001C), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"faddb", 4, two(0xF000, 0x5822), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"faddb", 4, two(0xF000, 0x5822), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"faddd", 4, two(0xF000, 0x0022), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"faddl", 4, two(0xF000, 0x4022), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"faddl", 4, two(0xF000, 0x4022), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"faddp", 4, two(0xF000, 0x4C22), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fadds", 4, two(0xF000, 0x4422), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fadds", 4, two(0xF000, 0x4422), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"faddw", 4, two(0xF000, 0x5022), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"faddw", 4, two(0xF000, 0x5022), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"faddx", 4, two(0xF000, 0x0022), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"faddx", 4, two(0xF000, 0x4822), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, + +{"fsaddb", 4, two(0xF000, 0x5862), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fsaddb", 4, two(0xF000, 0x5862), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsaddd", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fsaddd", 4, two(0xF000, 0x5462), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fsaddd", 4, two(0xF000, 0x5462), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fsaddl", 4, two(0xF000, 0x4062), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fsaddl", 4, two(0xF000, 0x4062), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsaddp", 4, two(0xF000, 0x4C62), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fsadds", 4, two(0xF000, 0x4462), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fsadds", 4, two(0xF000, 0x4862), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsaddw", 4, two(0xF000, 0x5062), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fsaddw", 4, two(0xF000, 0x5062), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsaddx", 4, two(0xF000, 0x0062), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fsaddx", 4, two(0xF000, 0x4862), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, + +{"fdaddb", 4, two(0xF000, 0x5826), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdaddb", 4, two(0xF000, 0x5866), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fdaddd", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fdaddd", 4, two(0xF000, 0x5426), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdaddd", 4, two(0xF000, 0x5466), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fdaddl", 4, two(0xF000, 0x4026), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fdaddl", 4, two(0xF000, 0x4066), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fdaddp", 4, two(0xF000, 0x4C66), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fdadds", 4, two(0xF000, 0x4466), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fdadds", 4, two(0xF000, 0x4826), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdaddw", 4, two(0xF000, 0x5026), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdaddw", 4, two(0xF000, 0x5066), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fdaddx", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fdaddx", 4, two(0xF000, 0x4866), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, + +{"fasinb", 4, two(0xF000, 0x580C), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fasind", 4, two(0xF000, 0x540C), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fasinl", 4, two(0xF000, 0x400C), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fasinp", 4, two(0xF000, 0x4C0C), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fasins", 4, two(0xF000, 0x440C), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fasinw", 4, two(0xF000, 0x500C), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fasinx", 4, two(0xF000, 0x000C), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fasinx", 4, two(0xF000, 0x480C), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fasinx", 4, two(0xF000, 0x000C), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fatanb", 4, two(0xF000, 0x580A), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fatand", 4, two(0xF000, 0x540A), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fatanl", 4, two(0xF000, 0x400A), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fatanp", 4, two(0xF000, 0x4C0A), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fatans", 4, two(0xF000, 0x440A), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fatanw", 4, two(0xF000, 0x500A), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fatanx", 4, two(0xF000, 0x000A), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fatanx", 4, two(0xF000, 0x480A), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fatanx", 4, two(0xF000, 0x000A), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fatanhb", 4, two(0xF000, 0x580D), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fatanhd", 4, two(0xF000, 0x540D), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fatanhl", 4, two(0xF000, 0x400D), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fatanhp", 4, two(0xF000, 0x4C0D), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fatanhs", 4, two(0xF000, 0x440D), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fatanhw", 4, two(0xF000, 0x500D), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fatanhx", 4, two(0xF000, 0x000D), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fatanhx", 4, two(0xF000, 0x480D), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fatanhx", 4, two(0xF000, 0x000D), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fbeq", 2, one(0xF081), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbf", 2, one(0xF080), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbge", 2, one(0xF093), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbgl", 2, one(0xF096), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbgle", 2, one(0xF097), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbgt", 2, one(0xF092), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fble", 2, one(0xF095), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fblt", 2, one(0xF094), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbne", 2, one(0xF08E), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbnge", 2, one(0xF09C), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbngl", 2, one(0xF099), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbngle", 2, one(0xF098), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbngt", 2, one(0xF09D), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbnle", 2, one(0xF09A), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbnlt", 2, one(0xF09B), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fboge", 2, one(0xF083), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbogl", 2, one(0xF086), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbogt", 2, one(0xF082), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbole", 2, one(0xF085), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbolt", 2, one(0xF084), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbor", 2, one(0xF087), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbseq", 2, one(0xF091), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbsf", 2, one(0xF090), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbsne", 2, one(0xF09E), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbst", 2, one(0xF09F), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbt", 2, one(0xF08F), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbueq", 2, one(0xF089), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbuge", 2, one(0xF08B), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbugt", 2, one(0xF08A), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbule", 2, one(0xF08D), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbult", 2, one(0xF08C), one(0xF1FF), "IdBW", mfloat | cfloat }, +{"fbun", 2, one(0xF088), one(0xF1FF), "IdBW", mfloat | cfloat }, + +{"fbeql", 2, one(0xF0C1), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbfl", 2, one(0xF0C0), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbgel", 2, one(0xF0D3), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbgll", 2, one(0xF0D6), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbglel", 2, one(0xF0D7), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbgtl", 2, one(0xF0D2), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fblel", 2, one(0xF0D5), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbltl", 2, one(0xF0D4), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbnel", 2, one(0xF0CE), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbngel", 2, one(0xF0DC), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbngll", 2, one(0xF0D9), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbnglel", 2, one(0xF0D8), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbngtl", 2, one(0xF0DD), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbnlel", 2, one(0xF0DA), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbnltl", 2, one(0xF0DB), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbogel", 2, one(0xF0C3), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbogll", 2, one(0xF0C6), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbogtl", 2, one(0xF0C2), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbolel", 2, one(0xF0C5), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fboltl", 2, one(0xF0C4), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fborl", 2, one(0xF0C7), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbseql", 2, one(0xF0D1), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbsfl", 2, one(0xF0D0), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbsnel", 2, one(0xF0DE), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbstl", 2, one(0xF0DF), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbtl", 2, one(0xF0CF), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbueql", 2, one(0xF0C9), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbugel", 2, one(0xF0CB), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbugtl", 2, one(0xF0CA), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbulel", 2, one(0xF0CD), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbultl", 2, one(0xF0CC), one(0xF1FF), "IdBC", mfloat | cfloat }, +{"fbunl", 2, one(0xF0C8), one(0xF1FF), "IdBC", mfloat | cfloat }, + +{"fjeq", 2, one(0xF081), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjf", 2, one(0xF080), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjge", 2, one(0xF093), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjgl", 2, one(0xF096), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjgle", 2, one(0xF097), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjgt", 2, one(0xF092), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjle", 2, one(0xF095), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjlt", 2, one(0xF094), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjne", 2, one(0xF08E), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjnge", 2, one(0xF09C), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjngl", 2, one(0xF099), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjngle", 2, one(0xF098), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjngt", 2, one(0xF09D), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjnle", 2, one(0xF09A), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjnlt", 2, one(0xF09B), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjoge", 2, one(0xF083), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjogl", 2, one(0xF086), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjogt", 2, one(0xF082), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjole", 2, one(0xF085), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjolt", 2, one(0xF084), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjor", 2, one(0xF087), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjseq", 2, one(0xF091), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjsf", 2, one(0xF090), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjsne", 2, one(0xF09E), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjst", 2, one(0xF09F), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjt", 2, one(0xF08F), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjueq", 2, one(0xF089), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjuge", 2, one(0xF08B), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjugt", 2, one(0xF08A), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjule", 2, one(0xF08D), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjult", 2, one(0xF08C), one(0xF1BF), "IdBc", mfloat | cfloat }, +{"fjun", 2, one(0xF088), one(0xF1BF), "IdBc", mfloat | cfloat }, + +{"fcmpb", 4, two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fcmpb", 4, two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fcmpd", 4, two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fcmpd", 4, two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fcmpd", 4, two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fcmpl", 4, two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fcmpl", 4, two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fcmpp", 4, two(0xF000, 0x4C38), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fcmps", 4, two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fcmps", 4, two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fcmpw", 4, two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fcmpw", 4, two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fcmpx", 4, two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fcmpx", 4, two(0xF000, 0x4838), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, + +{"fcosb", 4, two(0xF000, 0x581D), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fcosd", 4, two(0xF000, 0x541D), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fcosl", 4, two(0xF000, 0x401D), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fcosp", 4, two(0xF000, 0x4C1D), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fcoss", 4, two(0xF000, 0x441D), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fcosw", 4, two(0xF000, 0x501D), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fcosx", 4, two(0xF000, 0x001D), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fcosx", 4, two(0xF000, 0x481D), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fcosx", 4, two(0xF000, 0x001D), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fcoshb", 4, two(0xF000, 0x5819), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fcoshd", 4, two(0xF000, 0x5419), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fcoshl", 4, two(0xF000, 0x4019), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fcoshp", 4, two(0xF000, 0x4C19), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fcoshs", 4, two(0xF000, 0x4419), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fcoshw", 4, two(0xF000, 0x5019), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fcoshx", 4, two(0xF000, 0x0019), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fcoshx", 4, two(0xF000, 0x4819), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fcoshx", 4, two(0xF000, 0x0019), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fdbeq", 4, two(0xF048, 0x0001), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbf", 4, two(0xF048, 0x0000), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbge", 4, two(0xF048, 0x0013), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbgl", 4, two(0xF048, 0x0016), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbgle", 4, two(0xF048, 0x0017), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbgt", 4, two(0xF048, 0x0012), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdble", 4, two(0xF048, 0x0015), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdblt", 4, two(0xF048, 0x0014), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbne", 4, two(0xF048, 0x000E), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbnge", 4, two(0xF048, 0x001C), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbngl", 4, two(0xF048, 0x0019), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbngle", 4, two(0xF048, 0x0018), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbngt", 4, two(0xF048, 0x001D), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbnle", 4, two(0xF048, 0x001A), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbnlt", 4, two(0xF048, 0x001B), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdboge", 4, two(0xF048, 0x0003), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbogl", 4, two(0xF048, 0x0006), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbogt", 4, two(0xF048, 0x0002), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbole", 4, two(0xF048, 0x0005), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbolt", 4, two(0xF048, 0x0004), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbor", 4, two(0xF048, 0x0007), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbseq", 4, two(0xF048, 0x0011), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbsf", 4, two(0xF048, 0x0010), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbsne", 4, two(0xF048, 0x001E), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbst", 4, two(0xF048, 0x001F), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbt", 4, two(0xF048, 0x000F), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbueq", 4, two(0xF048, 0x0009), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbuge", 4, two(0xF048, 0x000B), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbugt", 4, two(0xF048, 0x000A), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbule", 4, two(0xF048, 0x000D), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbult", 4, two(0xF048, 0x000C), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, +{"fdbun", 4, two(0xF048, 0x0008), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat }, + +{"fdivb", 4, two(0xF000, 0x5820), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fdivb", 4, two(0xF000, 0x5820), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdivd", 4, two(0xF000, 0x0020), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fdivd", 4, two(0xF000, 0x5420), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fdivd", 4, two(0xF000, 0x5420), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fdivl", 4, two(0xF000, 0x4020), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fdivl", 4, two(0xF000, 0x4020), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdivp", 4, two(0xF000, 0x4C20), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fdivs", 4, two(0xF000, 0x4420), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fdivs", 4, two(0xF000, 0x4420), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdivw", 4, two(0xF000, 0x5020), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fdivw", 4, two(0xF000, 0x5020), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdivx", 4, two(0xF000, 0x0020), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fdivx", 4, two(0xF000, 0x4820), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, + +{"fsdivb", 4, two(0xF000, 0x5860), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fsdivb", 4, two(0xF000, 0x5860), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsdivd", 4, two(0xF000, 0x0060), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fsdivd", 4, two(0xF000, 0x5460), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fsdivd", 4, two(0xF000, 0x5460), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fsdivl", 4, two(0xF000, 0x4060), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fsdivl", 4, two(0xF000, 0x4060), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsdivp", 4, two(0xF000, 0x4C60), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fsdivs", 4, two(0xF000, 0x4460), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fsdivs", 4, two(0xF000, 0x4460), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsdivw", 4, two(0xF000, 0x5060), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fsdivw", 4, two(0xF000, 0x5060), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsdivx", 4, two(0xF000, 0x0060), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fsdivx", 4, two(0xF000, 0x4860), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, + +{"fddivb", 4, two(0xF000, 0x5864), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fddivb", 4, two(0xF000, 0x5864), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fddivd", 4, two(0xF000, 0x0064), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fddivd", 4, two(0xF000, 0x5464), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fddivd", 4, two(0xF000, 0x5464), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fddivl", 4, two(0xF000, 0x4064), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fddivl", 4, two(0xF000, 0x4064), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fddivp", 4, two(0xF000, 0x4C64), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fddivs", 4, two(0xF000, 0x4464), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fddivs", 4, two(0xF000, 0x4464), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fddivw", 4, two(0xF000, 0x5064), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fddivw", 4, two(0xF000, 0x5064), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fddivx", 4, two(0xF000, 0x0064), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fddivx", 4, two(0xF000, 0x4864), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, + +{"fetoxb", 4, two(0xF000, 0x5810), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fetoxd", 4, two(0xF000, 0x5410), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fetoxl", 4, two(0xF000, 0x4010), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fetoxp", 4, two(0xF000, 0x4C10), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fetoxs", 4, two(0xF000, 0x4410), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fetoxw", 4, two(0xF000, 0x5010), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fetoxx", 4, two(0xF000, 0x0010), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fetoxx", 4, two(0xF000, 0x4810), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fetoxx", 4, two(0xF000, 0x0010), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fetoxm1b", 4, two(0xF000, 0x5808), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fetoxm1d", 4, two(0xF000, 0x5408), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fetoxm1l", 4, two(0xF000, 0x4008), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fetoxm1p", 4, two(0xF000, 0x4C08), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fetoxm1s", 4, two(0xF000, 0x4408), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fetoxm1w", 4, two(0xF000, 0x5008), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fetoxm1x", 4, two(0xF000, 0x0008), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fetoxm1x", 4, two(0xF000, 0x4808), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fetoxm1x", 4, two(0xF000, 0x0008), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fgetexpb", 4, two(0xF000, 0x581E), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fgetexpd", 4, two(0xF000, 0x541E), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fgetexpl", 4, two(0xF000, 0x401E), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fgetexpp", 4, two(0xF000, 0x4C1E), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fgetexps", 4, two(0xF000, 0x441E), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fgetexpw", 4, two(0xF000, 0x501E), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fgetexpx", 4, two(0xF000, 0x001E), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fgetexpx", 4, two(0xF000, 0x481E), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fgetexpx", 4, two(0xF000, 0x001E), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fgetmanb", 4, two(0xF000, 0x581F), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fgetmand", 4, two(0xF000, 0x541F), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fgetmanl", 4, two(0xF000, 0x401F), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fgetmanp", 4, two(0xF000, 0x4C1F), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fgetmans", 4, two(0xF000, 0x441F), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fgetmanw", 4, two(0xF000, 0x501F), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fgetmanx", 4, two(0xF000, 0x001F), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fgetmanx", 4, two(0xF000, 0x481F), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fgetmanx", 4, two(0xF000, 0x001F), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fintb", 4, two(0xF000, 0x5801), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fintb", 4, two(0xF000, 0x5801), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fintd", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fintd", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fintd", 4, two(0xF000, 0x5401), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fintd", 4, two(0xF000, 0x5401), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fintl", 4, two(0xF000, 0x4001), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fintl", 4, two(0xF000, 0x4001), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fintp", 4, two(0xF000, 0x4C01), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fints", 4, two(0xF000, 0x4401), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fints", 4, two(0xF000, 0x4401), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fintw", 4, two(0xF000, 0x5001), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fintw", 4, two(0xF000, 0x5001), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fintx", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fintx", 4, two(0xF000, 0x4801), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fintx", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fintrzb", 4, two(0xF000, 0x5803), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fintrzb", 4, two(0xF000, 0x5803), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fintrzd", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fintrzd", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fintrzd", 4, two(0xF000, 0x5403), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fintrzd", 4, two(0xF000, 0x5403), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fintrzl", 4, two(0xF000, 0x4003), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fintrzl", 4, two(0xF000, 0x4003), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fintrzp", 4, two(0xF000, 0x4C03), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fintrzs", 4, two(0xF000, 0x4403), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fintrzs", 4, two(0xF000, 0x4403), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fintrzw", 4, two(0xF000, 0x5003), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fintrzw", 4, two(0xF000, 0x5003), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fintrzx", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fintrzx", 4, two(0xF000, 0x4803), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fintrzx", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"flog10b", 4, two(0xF000, 0x5815), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"flog10d", 4, two(0xF000, 0x5415), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"flog10l", 4, two(0xF000, 0x4015), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"flog10p", 4, two(0xF000, 0x4C15), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"flog10s", 4, two(0xF000, 0x4415), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"flog10w", 4, two(0xF000, 0x5015), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"flog10x", 4, two(0xF000, 0x0015), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"flog10x", 4, two(0xF000, 0x4815), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"flog10x", 4, two(0xF000, 0x0015), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"flog2b", 4, two(0xF000, 0x5816), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"flog2d", 4, two(0xF000, 0x5416), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"flog2l", 4, two(0xF000, 0x4016), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"flog2p", 4, two(0xF000, 0x4C16), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"flog2s", 4, two(0xF000, 0x4416), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"flog2w", 4, two(0xF000, 0x5016), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"flog2x", 4, two(0xF000, 0x0016), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"flog2x", 4, two(0xF000, 0x4816), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"flog2x", 4, two(0xF000, 0x0016), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"flognb", 4, two(0xF000, 0x5814), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"flognd", 4, two(0xF000, 0x5414), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"flognl", 4, two(0xF000, 0x4014), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"flognp", 4, two(0xF000, 0x4C14), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"flogns", 4, two(0xF000, 0x4414), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"flognw", 4, two(0xF000, 0x5014), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"flognx", 4, two(0xF000, 0x0014), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"flognx", 4, two(0xF000, 0x4814), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"flognx", 4, two(0xF000, 0x0014), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"flognp1b", 4, two(0xF000, 0x5806), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"flognp1d", 4, two(0xF000, 0x5406), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"flognp1l", 4, two(0xF000, 0x4006), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"flognp1p", 4, two(0xF000, 0x4C06), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"flognp1s", 4, two(0xF000, 0x4406), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"flognp1w", 4, two(0xF000, 0x5006), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"flognp1x", 4, two(0xF000, 0x0006), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"flognp1x", 4, two(0xF000, 0x4806), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"flognp1x", 4, two(0xF000, 0x0006), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fmodb", 4, two(0xF000, 0x5821), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fmodd", 4, two(0xF000, 0x5421), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fmodl", 4, two(0xF000, 0x4021), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fmodp", 4, two(0xF000, 0x4C21), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fmods", 4, two(0xF000, 0x4421), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fmodw", 4, two(0xF000, 0x5021), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fmodx", 4, two(0xF000, 0x0021), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fmodx", 4, two(0xF000, 0x4821), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, + +{"fmoveb", 4, two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fmoveb", 4, two(0xF000, 0x7800), two(0xF1C0, 0xFC7F), "IiF7bs", cfloat }, +{"fmoveb", 4, two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fmoveb", 4, two(0xF000, 0x7800), two(0xF1C0, 0xFC7F), "IiF7$b", mfloat }, +{"fmoved", 4, two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fmoved", 4, two(0xF000, 0x7400), two(0xF1C0, 0xFC7F), "IiF7~F", mfloat }, +{"fmoved", 4, two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fmoved", 4, two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fmoved", 4, two(0xF000, 0x7400), two(0xF1C0, 0xFC7F), "IiF7ws", cfloat }, +{"fmovel", 4, two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fmovel", 4, two(0xF000, 0x6000), two(0xF1C0, 0xFC7F), "IiF7$l", mfloat }, +/* FIXME: the next two variants should not permit moving an address + register to anything but the floating point instruction register. */ +{"fmovel", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat }, +{"fmovel", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ls8", mfloat }, +{"fmovel", 4, two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fmovel", 4, two(0xF000, 0x6000), two(0xF1C0, 0xFC7F), "IiF7bs", cfloat }, + /* Move the FP control registers. */ +{"fmovel", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8ps", cfloat }, +{"fmovel", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Iibss8", cfloat }, +{"fmovep", 4, two(0xF000, 0x4C00), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fmovep", 4, two(0xF000, 0x6C00), two(0xF1C0, 0xFC00), "IiF7~pkC", mfloat }, +{"fmovep", 4, two(0xF000, 0x7C00), two(0xF1C0, 0xFC0F), "IiF7~pDk", mfloat }, +{"fmoves", 4, two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fmoves", 4, two(0xF000, 0x6400), two(0xF1C0, 0xFC7F), "IiF7$f", mfloat }, +{"fmoves", 4, two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fmoves", 4, two(0xF000, 0x6400), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fmovew", 4, two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fmovew", 4, two(0xF000, 0x7000), two(0xF1C0, 0xFC7F), "IiF7$w", mfloat }, +{"fmovew", 4, two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fmovew", 4, two(0xF000, 0x7000), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fmovex", 4, two(0xF000, 0x0000), two(0xF1FF, 0xE07F), "IiF8F7", mfloat }, +{"fmovex", 4, two(0xF000, 0x4800), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fmovex", 4, two(0xF000, 0x6800), two(0xF1C0, 0xFC7F), "IiF7~x", mfloat }, + +{"fsmoveb", 4, two(0xF000, 0x5840), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fsmoveb", 4, two(0xF000, 0x5840), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsmoveb", 4, two(0xF000, 0x7840), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fsmoved", 4, two(0xF000, 0x0040), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fsmoved", 4, two(0xF000, 0x5440), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fsmoved", 4, two(0xF000, 0x5440), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fsmoved", 4, two(0xF000, 0x7440), two(0xF1C0, 0xFC7F), "IiF7ws", cfloat }, +{"fsmovel", 4, two(0xF000, 0x4040), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fsmovel", 4, two(0xF000, 0x4040), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsmovel", 4, two(0xF000, 0x6040), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fsmoves", 4, two(0xF000, 0x4440), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fsmoves", 4, two(0xF000, 0x4440), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsmoves", 4, two(0xF000, 0x6440), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fsmovew", 4, two(0xF000, 0x5040), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fsmovew", 4, two(0xF000, 0x5040), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsmovew", 4, two(0xF000, 0x7040), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fsmovex", 4, two(0xF000, 0x0040), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fsmovex", 4, two(0xF000, 0x4840), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fsmovep", 4, two(0xF000, 0x4C40), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, + +{"fdmoveb", 4, two(0xF000, 0x5844), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fdmoveb", 4, two(0xF000, 0x5844), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdmoveb", 4, two(0xF000, 0x7844), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fdmoved", 4, two(0xF000, 0x0044), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fdmoved", 4, two(0xF000, 0x5444), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fdmoved", 4, two(0xF000, 0x5444), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fdmoved", 4, two(0xF000, 0x7444), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fdmovel", 4, two(0xF000, 0x4044), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fdmovel", 4, two(0xF000, 0x4044), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdmovel", 4, two(0xF000, 0x6044), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fdmoves", 4, two(0xF000, 0x4444), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fdmoves", 4, two(0xF000, 0x4444), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdmoves", 4, two(0xF000, 0x6444), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fdmovew", 4, two(0xF000, 0x5044), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fdmovew", 4, two(0xF000, 0x5044), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdmovew", 4, two(0xF000, 0x7044), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat }, +{"fdmovex", 4, two(0xF000, 0x0044), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fdmovex", 4, two(0xF000, 0x4844), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fdmovep", 4, two(0xF000, 0x4C44), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, + +{"fmovecrx", 4, two(0xF000, 0x5C00), two(0xF1FF, 0xFC00), "Ii#CF7", mfloat }, + +{"fmovemd", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizsl3", cfloat }, +{"fmovemd", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizs#3", cfloat }, +{"fmovemd", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Ii#3ys", cfloat }, +{"fmovemd", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Iil3ys", cfloat }, + +{"fmovemx", 4, two(0xF000, 0xF800), two(0xF1C0, 0xFF8F), "IiDk&s", mfloat }, +{"fmovemx", 4, two(0xF020, 0xE800), two(0xF1F8, 0xFF8F), "IiDk-s", mfloat }, +{"fmovemx", 4, two(0xF000, 0xD800), two(0xF1C0, 0xFF8F), "Ii&sDk", mfloat }, +{"fmovemx", 4, two(0xF018, 0xD800), two(0xF1F8, 0xFF8F), "Ii+sDk", mfloat }, +{"fmovemx", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Idl3&s", mfloat }, +{"fmovemx", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Id#3&s", mfloat }, +{"fmovemx", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&sl3", mfloat }, +{"fmovemx", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&s#3", mfloat }, +{"fmovemx", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "IdL3-s", mfloat }, +{"fmovemx", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "Id#3-s", mfloat }, +{"fmovemx", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+sl3", mfloat }, +{"fmovemx", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+s#3", mfloat }, + +{"fmoveml", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat }, +{"fmoveml", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "IiL8~s", mfloat }, +/* FIXME: In the next instruction, we should only permit %dn if the + target is a single register. We should only permit %an if the + target is a single %fpiar. */ +{"fmoveml", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*lL8", mfloat }, + +{"fmovem", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "IizsL3", cfloat }, +{"fmovem", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizs#3", cfloat }, +{"fmovem", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Ii#3ys", cfloat }, +{"fmovem", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "IiL3ys", cfloat }, + +{"fmovem", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "IdL3-s", mfloat }, +{"fmovem", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Idl3&s", mfloat }, +{"fmovem", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+sl3", mfloat }, +{"fmovem", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&sl3", mfloat }, +{"fmovem", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "Id#3-s", mfloat }, +{"fmovem", 4, two(0xF020, 0xE800), two(0xF1F8, 0xFF8F), "IiDk-s", mfloat }, +{"fmovem", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Id#3&s", mfloat }, +{"fmovem", 4, two(0xF000, 0xF800), two(0xF1C0, 0xFF8F), "IiDk&s", mfloat }, +{"fmovem", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+s#3", mfloat }, +{"fmovem", 4, two(0xF018, 0xD800), two(0xF1F8, 0xFF8F), "Ii+sDk", mfloat }, +{"fmovem", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&s#3", mfloat }, +{"fmovem", 4, two(0xF000, 0xD800), two(0xF1C0, 0xFF8F), "Ii&sDk", mfloat }, +{"fmovem", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat }, +{"fmovem", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ss8", mfloat }, +{"fmovem", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "IiL8~s", mfloat }, +{"fmovem", 4, two(0xF000, 0x8000), two(0xF2C0, 0xE3FF), "Ii*sL8", mfloat }, + +{"fmulb", 4, two(0xF000, 0x5823), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fmulb", 4, two(0xF000, 0x5823), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fmuld", 4, two(0xF000, 0x0023), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fmuld", 4, two(0xF000, 0x5423), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fmuld", 4, two(0xF000, 0x5423), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fmull", 4, two(0xF000, 0x4023), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fmull", 4, two(0xF000, 0x4023), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fmulp", 4, two(0xF000, 0x4C23), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fmuls", 4, two(0xF000, 0x4423), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fmuls", 4, two(0xF000, 0x4423), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fmulw", 4, two(0xF000, 0x5023), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fmulw", 4, two(0xF000, 0x5023), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fmulx", 4, two(0xF000, 0x0023), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fmulx", 4, two(0xF000, 0x4823), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, + +{"fsmulb", 4, two(0xF000, 0x5863), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fsmulb", 4, two(0xF000, 0x5863), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsmuld", 4, two(0xF000, 0x0063), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fsmuld", 4, two(0xF000, 0x5463), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fsmuld", 4, two(0xF000, 0x5463), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fsmull", 4, two(0xF000, 0x4063), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fsmull", 4, two(0xF000, 0x4063), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsmulp", 4, two(0xF000, 0x4C63), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fsmuls", 4, two(0xF000, 0x4463), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fsmuls", 4, two(0xF000, 0x4463), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsmulw", 4, two(0xF000, 0x5063), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fsmulw", 4, two(0xF000, 0x5063), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsmulx", 4, two(0xF000, 0x0063), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fsmulx", 4, two(0xF000, 0x4863), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, + +{"fdmulb", 4, two(0xF000, 0x5867), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fdmulb", 4, two(0xF000, 0x5867), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdmuld", 4, two(0xF000, 0x0067), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fdmuld", 4, two(0xF000, 0x5467), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fdmuld", 4, two(0xF000, 0x5467), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fdmull", 4, two(0xF000, 0x4067), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fdmull", 4, two(0xF000, 0x4067), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdmulp", 4, two(0xF000, 0x4C67), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fdmuls", 4, two(0xF000, 0x4467), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fdmuls", 4, two(0xF000, 0x4467), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdmulw", 4, two(0xF000, 0x5067), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fdmulw", 4, two(0xF000, 0x5067), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdmulx", 4, two(0xF000, 0x0067), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fdmulx", 4, two(0xF000, 0x4867), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, + +{"fnegb", 4, two(0xF000, 0x581A), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fnegb", 4, two(0xF000, 0x581A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fnegd", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fnegd", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fnegd", 4, two(0xF000, 0x541A), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fnegd", 4, two(0xF000, 0x541A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fnegl", 4, two(0xF000, 0x401A), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fnegl", 4, two(0xF000, 0x401A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fnegp", 4, two(0xF000, 0x4C1A), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fnegs", 4, two(0xF000, 0x441A), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fnegs", 4, two(0xF000, 0x441A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fnegw", 4, two(0xF000, 0x501A), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fnegw", 4, two(0xF000, 0x501A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fnegx", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fnegx", 4, two(0xF000, 0x481A), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fnegx", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fsnegb", 4, two(0xF000, 0x585A), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fsnegb", 4, two(0xF000, 0x585A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsnegd", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fsnegd", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fsnegd", 4, two(0xF000, 0x545A), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fsnegd", 4, two(0xF000, 0x545A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fsnegl", 4, two(0xF000, 0x405A), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fsnegl", 4, two(0xF000, 0x405A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsnegp", 4, two(0xF000, 0x4C5A), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fsnegs", 4, two(0xF000, 0x445A), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fsnegs", 4, two(0xF000, 0x445A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsnegw", 4, two(0xF000, 0x505A), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fsnegw", 4, two(0xF000, 0x505A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsnegx", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fsnegx", 4, two(0xF000, 0x485A), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fsnegx", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiFt", m68040up }, + +{"fdnegb", 4, two(0xF000, 0x585E), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fdnegb", 4, two(0xF000, 0x585E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdnegd", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fdnegd", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fdnegd", 4, two(0xF000, 0x545E), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fdnegd", 4, two(0xF000, 0x545E), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fdnegl", 4, two(0xF000, 0x405E), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fdnegl", 4, two(0xF000, 0x405E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdnegp", 4, two(0xF000, 0x4C5E), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fdnegs", 4, two(0xF000, 0x445E), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fdnegs", 4, two(0xF000, 0x445E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdnegw", 4, two(0xF000, 0x505E), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fdnegw", 4, two(0xF000, 0x505E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdnegx", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fdnegx", 4, two(0xF000, 0x485E), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fdnegx", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiFt", m68040up }, + +{"fnop", 4, two(0xF280, 0x0000), two(0xFFFF, 0xFFFF), "Ii", mfloat | cfloat }, + +{"fremb", 4, two(0xF000, 0x5825), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fremd", 4, two(0xF000, 0x5425), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"freml", 4, two(0xF000, 0x4025), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fremp", 4, two(0xF000, 0x4C25), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"frems", 4, two(0xF000, 0x4425), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fremw", 4, two(0xF000, 0x5025), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fremx", 4, two(0xF000, 0x0025), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fremx", 4, two(0xF000, 0x4825), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, + +{"frestore", 2, one(0xF140), one(0xF1C0), "Ids", mfloat }, +{"fsave", 2, one(0xF100), one(0xF1C0), "Idzs", cfloat }, + +{"fscaleb", 4, two(0xF000, 0x5826), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fscaled", 4, two(0xF000, 0x5426), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fscalel", 4, two(0xF000, 0x4026), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fscalep", 4, two(0xF000, 0x4C26), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fscales", 4, two(0xF000, 0x4426), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fscalew", 4, two(0xF000, 0x5026), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fscalex", 4, two(0xF000, 0x0026), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fscalex", 4, two(0xF000, 0x4826), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, + +/* $ is necessary to prevent the assembler from using PC-relative. + If @ were used, "label: fseq label" could produce "ftrapeq", 2, + because "label" became "pc@label". */ +{"fseq", 4, two(0xF040, 0x0001), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsf", 4, two(0xF040, 0x0000), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsge", 4, two(0xF040, 0x0013), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsgl", 4, two(0xF040, 0x0016), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsgle", 4, two(0xF040, 0x0017), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsgt", 4, two(0xF040, 0x0012), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsle", 4, two(0xF040, 0x0015), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fslt", 4, two(0xF040, 0x0014), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsne", 4, two(0xF040, 0x000E), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsnge", 4, two(0xF040, 0x001C), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsngl", 4, two(0xF040, 0x0019), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsngle", 4, two(0xF040, 0x0018), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsngt", 4, two(0xF040, 0x001D), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsnle", 4, two(0xF040, 0x001A), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsnlt", 4, two(0xF040, 0x001B), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsoge", 4, two(0xF040, 0x0003), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsogl", 4, two(0xF040, 0x0006), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsogt", 4, two(0xF040, 0x0002), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsole", 4, two(0xF040, 0x0005), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsolt", 4, two(0xF040, 0x0004), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsor", 4, two(0xF040, 0x0007), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsseq", 4, two(0xF040, 0x0011), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fssf", 4, two(0xF040, 0x0010), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fssne", 4, two(0xF040, 0x001E), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsst", 4, two(0xF040, 0x001F), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fst", 4, two(0xF040, 0x000F), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsueq", 4, two(0xF040, 0x0009), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsuge", 4, two(0xF040, 0x000B), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsugt", 4, two(0xF040, 0x000A), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsule", 4, two(0xF040, 0x000D), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsult", 4, two(0xF040, 0x000C), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, +{"fsun", 4, two(0xF040, 0x0008), two(0xF1C0, 0xFFFF), "Ii$s", mfloat }, + +{"fsgldivb", 4, two(0xF000, 0x5824), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fsgldivd", 4, two(0xF000, 0x5424), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fsgldivl", 4, two(0xF000, 0x4024), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fsgldivp", 4, two(0xF000, 0x4C24), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fsgldivs", 4, two(0xF000, 0x4424), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fsgldivw", 4, two(0xF000, 0x5024), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fsgldivx", 4, two(0xF000, 0x0024), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fsgldivx", 4, two(0xF000, 0x4824), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fsgldivx", 4, two(0xF000, 0x0024), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fsglmulb", 4, two(0xF000, 0x5827), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fsglmuld", 4, two(0xF000, 0x5427), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fsglmull", 4, two(0xF000, 0x4027), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fsglmulp", 4, two(0xF000, 0x4C27), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fsglmuls", 4, two(0xF000, 0x4427), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fsglmulw", 4, two(0xF000, 0x5027), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fsglmulx", 4, two(0xF000, 0x0027), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fsglmulx", 4, two(0xF000, 0x4827), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fsglmulx", 4, two(0xF000, 0x0027), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fsinb", 4, two(0xF000, 0x580E), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fsind", 4, two(0xF000, 0x540E), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fsinl", 4, two(0xF000, 0x400E), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fsinp", 4, two(0xF000, 0x4C0E), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fsins", 4, two(0xF000, 0x440E), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fsinw", 4, two(0xF000, 0x500E), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fsinx", 4, two(0xF000, 0x000E), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fsinx", 4, two(0xF000, 0x480E), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fsinx", 4, two(0xF000, 0x000E), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fsincosb", 4, two(0xF000, 0x5830), two(0xF1C0, 0xFC78), "Ii;bF3F7", mfloat }, +{"fsincosd", 4, two(0xF000, 0x5430), two(0xF1C0, 0xFC78), "Ii;FF3F7", mfloat }, +{"fsincosl", 4, two(0xF000, 0x4030), two(0xF1C0, 0xFC78), "Ii;lF3F7", mfloat }, +{"fsincosp", 4, two(0xF000, 0x4C30), two(0xF1C0, 0xFC78), "Ii;pF3F7", mfloat }, +{"fsincoss", 4, two(0xF000, 0x4430), two(0xF1C0, 0xFC78), "Ii;fF3F7", mfloat }, +{"fsincosw", 4, two(0xF000, 0x5030), two(0xF1C0, 0xFC78), "Ii;wF3F7", mfloat }, +{"fsincosx", 4, two(0xF000, 0x0030), two(0xF1C0, 0xE078), "IiF8F3F7", mfloat }, +{"fsincosx", 4, two(0xF000, 0x4830), two(0xF1C0, 0xFC78), "Ii;xF3F7", mfloat }, + +{"fsinhb", 4, two(0xF000, 0x5802), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fsinhd", 4, two(0xF000, 0x5402), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fsinhl", 4, two(0xF000, 0x4002), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fsinhp", 4, two(0xF000, 0x4C02), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fsinhs", 4, two(0xF000, 0x4402), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fsinhw", 4, two(0xF000, 0x5002), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fsinhx", 4, two(0xF000, 0x0002), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fsinhx", 4, two(0xF000, 0x4802), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fsinhx", 4, two(0xF000, 0x0002), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fsqrtb", 4, two(0xF000, 0x5804), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fsqrtb", 4, two(0xF000, 0x5804), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsqrtd", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fsqrtd", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fsqrtd", 4, two(0xF000, 0x5404), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fsqrtd", 4, two(0xF000, 0x5404), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fsqrtl", 4, two(0xF000, 0x4004), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fsqrtl", 4, two(0xF000, 0x4004), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsqrtp", 4, two(0xF000, 0x4C04), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fsqrts", 4, two(0xF000, 0x4404), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fsqrts", 4, two(0xF000, 0x4404), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsqrtw", 4, two(0xF000, 0x5004), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fsqrtw", 4, two(0xF000, 0x5004), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsqrtx", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fsqrtx", 4, two(0xF000, 0x4804), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fsqrtx", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fssqrtb", 4, two(0xF000, 0x5841), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fssqrtb", 4, two(0xF000, 0x5841), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fssqrtd", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fssqrtd", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fssqrtd", 4, two(0xF000, 0x5441), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fssqrtd", 4, two(0xF000, 0x5441), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fssqrtl", 4, two(0xF000, 0x4041), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fssqrtl", 4, two(0xF000, 0x4041), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fssqrtp", 4, two(0xF000, 0x4C41), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fssqrts", 4, two(0xF000, 0x4441), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fssqrts", 4, two(0xF000, 0x4441), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fssqrtw", 4, two(0xF000, 0x5041), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fssqrtw", 4, two(0xF000, 0x5041), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fssqrtx", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fssqrtx", 4, two(0xF000, 0x4841), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fssqrtx", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiFt", m68040up }, + +{"fdsqrtb", 4, two(0xF000, 0x5845), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fdsqrtb", 4, two(0xF000, 0x5845), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdsqrtd", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fdsqrtd", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiFt", cfloat }, +{"fdsqrtd", 4, two(0xF000, 0x5445), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fdsqrtl", 4, two(0xF000, 0x4045), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fdsqrtl", 4, two(0xF000, 0x4045), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdsqrtp", 4, two(0xF000, 0x4C45), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fdsqrts", 4, two(0xF000, 0x4445), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fdsqrts", 4, two(0xF000, 0x4445), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdsqrtw", 4, two(0xF000, 0x5045), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fdsqrtw", 4, two(0xF000, 0x5045), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdsqrtx", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fdsqrtx", 4, two(0xF000, 0x4845), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fdsqrtx", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiFt", m68040up }, + +{"fsubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"fsubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsubd", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fsubd", 4, two(0xF000, 0x5428), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"fsubd", 4, two(0xF000, 0x5428), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fsubl", 4, two(0xF000, 0x4028), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"fsubl", 4, two(0xF000, 0x4028), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsubp", 4, two(0xF000, 0x4C28), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"fsubs", 4, two(0xF000, 0x4428), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"fsubs", 4, two(0xF000, 0x4428), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsubw", 4, two(0xF000, 0x5028), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"fsubw", 4, two(0xF000, 0x5028), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fsubx", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"fsubx", 4, two(0xF000, 0x4828), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"fsubx", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"fssubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fssubb", 4, two(0xF000, 0x5868), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fssubd", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fssubd", 4, two(0xF000, 0x5468), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fssubd", 4, two(0xF000, 0x5468), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fssubl", 4, two(0xF000, 0x4068), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fssubl", 4, two(0xF000, 0x4068), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fssubp", 4, two(0xF000, 0x4C68), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fssubs", 4, two(0xF000, 0x4468), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fssubs", 4, two(0xF000, 0x4468), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fssubw", 4, two(0xF000, 0x5068), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fssubw", 4, two(0xF000, 0x5068), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fssubx", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fssubx", 4, two(0xF000, 0x4868), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fssubx", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiFt", m68040up }, + +{"fdsubb", 4, two(0xF000, 0x586A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdsubb", 4, two(0xF000, 0x586c), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up }, +{"fdsubd", 4, two(0xF000, 0x006A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat }, +{"fdsubd", 4, two(0xF000, 0x546A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat }, +{"fdsubd", 4, two(0xF000, 0x546c), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up }, +{"fdsubl", 4, two(0xF000, 0x406A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdsubl", 4, two(0xF000, 0x406c), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up }, +{"fdsubp", 4, two(0xF000, 0x4C6c), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up }, +{"fdsubs", 4, two(0xF000, 0x446A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdsubs", 4, two(0xF000, 0x446c), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up }, +{"fdsubw", 4, two(0xF000, 0x506A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat }, +{"fdsubw", 4, two(0xF000, 0x506c), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up }, +{"fdsubx", 4, two(0xF000, 0x006c), two(0xF1C0, 0xE07F), "IiF8F7", m68040up }, +{"fdsubx", 4, two(0xF000, 0x486c), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up }, +{"fdsubx", 4, two(0xF000, 0x006c), two(0xF1C0, 0xE07F), "IiFt", m68040up }, + +{"ftanb", 4, two(0xF000, 0x580F), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"ftand", 4, two(0xF000, 0x540F), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"ftanl", 4, two(0xF000, 0x400F), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"ftanp", 4, two(0xF000, 0x4C0F), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"ftans", 4, two(0xF000, 0x440F), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"ftanw", 4, two(0xF000, 0x500F), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"ftanx", 4, two(0xF000, 0x000F), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"ftanx", 4, two(0xF000, 0x480F), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"ftanx", 4, two(0xF000, 0x000F), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"ftanhb", 4, two(0xF000, 0x5809), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"ftanhd", 4, two(0xF000, 0x5409), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"ftanhl", 4, two(0xF000, 0x4009), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"ftanhp", 4, two(0xF000, 0x4C09), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"ftanhs", 4, two(0xF000, 0x4409), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"ftanhw", 4, two(0xF000, 0x5009), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"ftanhx", 4, two(0xF000, 0x0009), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"ftanhx", 4, two(0xF000, 0x4809), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"ftanhx", 4, two(0xF000, 0x0009), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"ftentoxb", 4, two(0xF000, 0x5812), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"ftentoxd", 4, two(0xF000, 0x5412), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"ftentoxl", 4, two(0xF000, 0x4012), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"ftentoxp", 4, two(0xF000, 0x4C12), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"ftentoxs", 4, two(0xF000, 0x4412), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"ftentoxw", 4, two(0xF000, 0x5012), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"ftentoxx", 4, two(0xF000, 0x0012), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"ftentoxx", 4, two(0xF000, 0x4812), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"ftentoxx", 4, two(0xF000, 0x0012), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"ftrapeq", 4, two(0xF07C, 0x0001), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapf", 4, two(0xF07C, 0x0000), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapge", 4, two(0xF07C, 0x0013), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapgl", 4, two(0xF07C, 0x0016), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapgle", 4, two(0xF07C, 0x0017), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapgt", 4, two(0xF07C, 0x0012), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftraple", 4, two(0xF07C, 0x0015), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftraplt", 4, two(0xF07C, 0x0014), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapne", 4, two(0xF07C, 0x000E), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapnge", 4, two(0xF07C, 0x001C), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapngl", 4, two(0xF07C, 0x0019), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapngle", 4,two(0xF07C, 0x0018), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapngt", 4, two(0xF07C, 0x001D), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapnle", 4, two(0xF07C, 0x001A), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapnlt", 4, two(0xF07C, 0x001B), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapoge", 4, two(0xF07C, 0x0003), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapogl", 4, two(0xF07C, 0x0006), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapogt", 4, two(0xF07C, 0x0002), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapole", 4, two(0xF07C, 0x0005), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapolt", 4, two(0xF07C, 0x0004), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapor", 4, two(0xF07C, 0x0007), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapseq", 4, two(0xF07C, 0x0011), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapsf", 4, two(0xF07C, 0x0010), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapsne", 4, two(0xF07C, 0x001E), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapst", 4, two(0xF07C, 0x001F), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapt", 4, two(0xF07C, 0x000F), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapueq", 4, two(0xF07C, 0x0009), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapuge", 4, two(0xF07C, 0x000B), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapugt", 4, two(0xF07C, 0x000A), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapule", 4, two(0xF07C, 0x000D), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapult", 4, two(0xF07C, 0x000C), two(0xF1FF, 0xFFFF), "Ii", mfloat }, +{"ftrapun", 4, two(0xF07C, 0x0008), two(0xF1FF, 0xFFFF), "Ii", mfloat }, + +{"ftrapeqw", 4, two(0xF07A, 0x0001), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapfw", 4, two(0xF07A, 0x0000), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapgew", 4, two(0xF07A, 0x0013), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapglw", 4, two(0xF07A, 0x0016), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapglew", 4,two(0xF07A, 0x0017), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapgtw", 4, two(0xF07A, 0x0012), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftraplew", 4, two(0xF07A, 0x0015), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapltw", 4, two(0xF07A, 0x0014), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapnew", 4, two(0xF07A, 0x000E), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapngew", 4,two(0xF07A, 0x001C), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapnglw", 4,two(0xF07A, 0x0019), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapnglew", 4,two(0xF07A, 0x0018), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapngtw", 4,two(0xF07A, 0x001D), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapnlew", 4,two(0xF07A, 0x001A), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapnltw", 4,two(0xF07A, 0x001B), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapogew", 4,two(0xF07A, 0x0003), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapoglw", 4,two(0xF07A, 0x0006), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapogtw", 4,two(0xF07A, 0x0002), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapolew", 4,two(0xF07A, 0x0005), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapoltw", 4,two(0xF07A, 0x0004), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftraporw", 4, two(0xF07A, 0x0007), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapseqw", 4,two(0xF07A, 0x0011), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapsfw", 4, two(0xF07A, 0x0010), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapsnew", 4,two(0xF07A, 0x001E), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapstw", 4, two(0xF07A, 0x001F), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftraptw", 4, two(0xF07A, 0x000F), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapueqw", 4,two(0xF07A, 0x0009), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapugew", 4,two(0xF07A, 0x000B), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapugtw", 4,two(0xF07A, 0x000A), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapulew", 4,two(0xF07A, 0x000D), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapultw", 4,two(0xF07A, 0x000C), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, +{"ftrapunw", 4, two(0xF07A, 0x0008), two(0xF1FF, 0xFFFF), "Ii^w", mfloat }, + +{"ftrapeql", 4, two(0xF07B, 0x0001), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapfl", 4, two(0xF07B, 0x0000), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapgel", 4, two(0xF07B, 0x0013), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapgll", 4, two(0xF07B, 0x0016), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapglel", 4,two(0xF07B, 0x0017), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapgtl", 4, two(0xF07B, 0x0012), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftraplel", 4, two(0xF07B, 0x0015), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapltl", 4, two(0xF07B, 0x0014), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapnel", 4, two(0xF07B, 0x000E), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapngel", 4,two(0xF07B, 0x001C), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapngll", 4,two(0xF07B, 0x0019), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapnglel", 4,two(0xF07B, 0x0018), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapngtl", 4,two(0xF07B, 0x001D), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapnlel", 4,two(0xF07B, 0x001A), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapnltl", 4,two(0xF07B, 0x001B), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapogel", 4,two(0xF07B, 0x0003), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapogll", 4,two(0xF07B, 0x0006), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapogtl", 4,two(0xF07B, 0x0002), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapolel", 4,two(0xF07B, 0x0005), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapoltl", 4,two(0xF07B, 0x0004), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftraporl", 4, two(0xF07B, 0x0007), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapseql", 4,two(0xF07B, 0x0011), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapsfl", 4, two(0xF07B, 0x0010), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapsnel", 4,two(0xF07B, 0x001E), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapstl", 4, two(0xF07B, 0x001F), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftraptl", 4, two(0xF07B, 0x000F), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapueql", 4,two(0xF07B, 0x0009), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapugel", 4,two(0xF07B, 0x000B), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapugtl", 4,two(0xF07B, 0x000A), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapulel", 4,two(0xF07B, 0x000D), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapultl", 4,two(0xF07B, 0x000C), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, +{"ftrapunl", 4, two(0xF07B, 0x0008), two(0xF1FF, 0xFFFF), "Ii^l", mfloat }, + +{"ftstb", 4, two(0xF000, 0x583A), two(0xF1C0, 0xFC7F), "Ii;b", mfloat }, +{"ftstb", 4, two(0xF000, 0x583A), two(0xF1C0, 0xFC7F), "Iibs", cfloat }, +{"ftstd", 4, two(0xF000, 0x003A), two(0xF1C0, 0xE07F), "IiF8", cfloat }, +{"ftstd", 4, two(0xF000, 0x543A), two(0xF1C0, 0xFC7F), "Ii;F", mfloat }, +{"ftstd", 4, two(0xF000, 0x543A), two(0xF1C0, 0xFC7F), "Iibs", cfloat }, +{"ftstl", 4, two(0xF000, 0x403A), two(0xF1C0, 0xFC7F), "Ii;l", mfloat }, +{"ftstl", 4, two(0xF000, 0x403A), two(0xF1C0, 0xFC7F), "Iibs", cfloat }, +{"ftstp", 4, two(0xF000, 0x4C3A), two(0xF1C0, 0xFC7F), "Ii;p", mfloat }, +{"ftsts", 4, two(0xF000, 0x443A), two(0xF1C0, 0xFC7F), "Ii;f", mfloat }, +{"ftsts", 4, two(0xF000, 0x443A), two(0xF1C0, 0xFC7F), "Iibs", cfloat }, +{"ftstw", 4, two(0xF000, 0x503A), two(0xF1C0, 0xFC7F), "Ii;w", mfloat }, +{"ftstw", 4, two(0xF000, 0x503A), two(0xF1C0, 0xFC7F), "Iibs", cfloat }, +{"ftstx", 4, two(0xF000, 0x003A), two(0xF1C0, 0xE07F), "IiF8", mfloat }, +{"ftstx", 4, two(0xF000, 0x483A), two(0xF1C0, 0xFC7F), "Ii;x", mfloat }, + +{"ftwotoxb", 4, two(0xF000, 0x5811), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, +{"ftwotoxd", 4, two(0xF000, 0x5411), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, +{"ftwotoxl", 4, two(0xF000, 0x4011), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, +{"ftwotoxp", 4, two(0xF000, 0x4C11), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, +{"ftwotoxs", 4, two(0xF000, 0x4411), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, +{"ftwotoxw", 4, two(0xF000, 0x5011), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, +{"ftwotoxx", 4, two(0xF000, 0x0011), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, +{"ftwotoxx", 4, two(0xF000, 0x4811), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, +{"ftwotoxx", 4, two(0xF000, 0x0011), two(0xF1C0, 0xE07F), "IiFt", mfloat }, + +{"halt", 2, one(0045310), one(0177777), "", m68060 | mcfisa_a }, + +{"illegal", 2, one(0045374), one(0177777), "", m68000up | mcfisa_a }, +{"intouch", 2, one(0xf428), one(0xfff8), "As", mcfisa_b }, + +{"jmp", 2, one(0047300), one(0177700), "!s", m68000up | mcfisa_a }, + +{"jra", 2, one(0060000), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jra", 2, one(0047300), one(0177700), "!s", m68000up | mcfisa_a }, + +{"jsr", 2, one(0047200), one(0177700), "!s", m68000up | mcfisa_a }, + +{"jbsr", 2, one(0060400), one(0177400), "Bg", m68000up | mcfisa_a }, +{"jbsr", 2, one(0047200), one(0177700), "!s", m68000up | mcfisa_a }, + +{"lea", 2, one(0040700), one(0170700), "!sAd", m68000up | mcfisa_a }, + +{"lpstop", 6, two(0174000,0000700),two(0177777,0177777),"#w", cpu32|m68060 }, + +{"linkw", 4, one(0047120), one(0177770), "As#w", m68000up | mcfisa_a }, +{"linkl", 6, one(0044010), one(0177770), "As#l", m68020up | cpu32 }, +{"link", 4, one(0047120), one(0177770), "As#W", m68000up | mcfisa_a }, +{"link", 6, one(0044010), one(0177770), "As#l", m68020up | cpu32 }, + +{"lslb", 2, one(0160410), one(0170770), "QdDs", m68000up }, +{"lslb", 2, one(0160450), one(0170770), "DdDs", m68000up }, +{"lslw", 2, one(0160510), one(0170770), "QdDs", m68000up }, +{"lslw", 2, one(0160550), one(0170770), "DdDs", m68000up }, +{"lslw", 2, one(0161700), one(0177700), "~s", m68000up }, +{"lsll", 2, one(0160610), one(0170770), "QdDs", m68000up | mcfisa_a }, +{"lsll", 2, one(0160650), one(0170770), "DdDs", m68000up | mcfisa_a }, + +{"lsrb", 2, one(0160010), one(0170770), "QdDs", m68000up }, +{"lsrb", 2, one(0160050), one(0170770), "DdDs", m68000up }, +{"lsrw", 2, one(0160110), one(0170770), "QdDs", m68000up }, +{"lsrw", 2, one(0160150), one(0170770), "DdDs", m68000up }, +{"lsrw", 2, one(0161300), one(0177700), "~s", m68000up }, +{"lsrl", 2, one(0160210), one(0170770), "QdDs", m68000up | mcfisa_a }, +{"lsrl", 2, one(0160250), one(0170770), "DdDs", m68000up | mcfisa_a }, + +{"macw", 4, two(0xa080, 0x0000), two(0xf180, 0x0910), "uNuoiI4/Rn", mcfmac }, +{"macw", 4, two(0xa080, 0x0200), two(0xf180, 0x0910), "uNuoMh4/Rn", mcfmac }, +{"macw", 4, two(0xa080, 0x0000), two(0xf180, 0x0f10), "uNuo4/Rn", mcfmac }, +{"macw", 4, two(0xa000, 0x0000), two(0xf1b0, 0x0900), "uMumiI", mcfmac }, +{"macw", 4, two(0xa000, 0x0200), two(0xf1b0, 0x0900), "uMumMh", mcfmac }, +{"macw", 4, two(0xa000, 0x0000), two(0xf1b0, 0x0f00), "uMum", mcfmac }, + +{"macw", 4, two(0xa000, 0x0000), two(0xf100, 0x0900), "uNuoiI4/RneG", mcfemac },/* Ry,Rx,SF,,accX. */ +{"macw", 4, two(0xa000, 0x0200), two(0xf100, 0x0900), "uNuoMh4/RneG", mcfemac },/* Ry,Rx,+1/-1,,accX. */ +{"macw", 4, two(0xa000, 0x0000), two(0xf100, 0x0f00), "uNuo4/RneG", mcfemac },/* Ry,Rx,,accX. */ +{"macw", 4, two(0xa000, 0x0000), two(0xf130, 0x0900), "uMumiIeH", mcfemac },/* Ry,Rx,SF,accX. */ +{"macw", 4, two(0xa000, 0x0200), two(0xf130, 0x0900), "uMumMheH", mcfemac },/* Ry,Rx,+1/-1,accX. */ +{"macw", 4, two(0xa000, 0x0000), two(0xf130, 0x0f00), "uMumeH", mcfemac }, /* Ry,Rx,accX. */ + +{"macl", 4, two(0xa080, 0x0800), two(0xf180, 0x0910), "RNRoiI4/Rn", mcfmac }, +{"macl", 4, two(0xa080, 0x0a00), two(0xf180, 0x0910), "RNRoMh4/Rn", mcfmac }, +{"macl", 4, two(0xa080, 0x0800), two(0xf180, 0x0f10), "RNRo4/Rn", mcfmac }, +{"macl", 4, two(0xa000, 0x0800), two(0xf1b0, 0x0b00), "RMRmiI", mcfmac }, +{"macl", 4, two(0xa000, 0x0a00), two(0xf1b0, 0x0b00), "RMRmMh", mcfmac }, +{"macl", 4, two(0xa000, 0x0800), two(0xf1b0, 0x0800), "RMRm", mcfmac }, + +{"macl", 4, two(0xa000, 0x0800), two(0xf100, 0x0900), "R3R1iI4/RneG", mcfemac }, +{"macl", 4, two(0xa000, 0x0a00), two(0xf100, 0x0900), "R3R1Mh4/RneG", mcfemac }, +{"macl", 4, two(0xa000, 0x0800), two(0xf100, 0x0f00), "R3R14/RneG", mcfemac }, +{"macl", 4, two(0xa000, 0x0800), two(0xf130, 0x0900), "RMRmiIeH", mcfemac }, +{"macl", 4, two(0xa000, 0x0a00), two(0xf130, 0x0900), "RMRmMheH", mcfemac }, +{"macl", 4, two(0xa000, 0x0800), two(0xf130, 0x0f00), "RMRmeH", mcfemac }, + +/* NOTE: The mcf5200 family programmer's reference manual does not + indicate the byte form of the movea instruction is invalid (as it + is on 68000 family cpus). However, experiments on the 5202 yield + unexpected results. The value is copied, but it is not sign extended + (as is done with movea.w) and the top three bytes in the address + register are not disturbed. I don't know if this is the intended + behavior --- it could be a hole in instruction decoding (Motorola + decided not to trap all invalid instructions for performance reasons) + --- but I suspect that it is not. + + I reported this to Motorola ISD Technical Communications Support, + which replied that other coldfire assemblers reject movea.b. For + this reason I've decided to not allow moveab. + + jtc@cygnus.com - 97/01/24. */ + +{"moveal", 2, one(0020100), one(0170700), "*lAd", m68000up | mcfisa_a }, +{"moveaw", 2, one(0030100), one(0170700), "*wAd", m68000up | mcfisa_a }, + +{"movclrl", 2, one(0xA1C0), one(0xf9f0), "eFRs", mcfemac }, + +{"movec", 4, one(0047173), one(0177777), "R1Jj", m68010up | mcfisa_a }, +{"movec", 4, one(0047173), one(0177777), "R1#j", m68010up | mcfisa_a }, +{"movec", 4, one(0047172), one(0177777), "JjR1", m68010up }, +{"movec", 4, one(0047172), one(0177777), "#jR1", m68010up }, + +{"movemw", 4, one(0044200), one(0177700), "Lw&s", m68000up }, +{"movemw", 4, one(0044240), one(0177770), "lw-s", m68000up }, +{"movemw", 4, one(0044200), one(0177700), "#w>s", m68000up }, +{"movemw", 4, one(0046200), one(0177700), "s", m68000up }, +{"moveml", 4, one(0046300), one(0177700), ",accX. */ +{"msacw", 4, two(0xa000, 0x0300), two(0xf100, 0x0900), "uMumMh4/RneG", mcfemac },/* Ry,Rx,+1/-1,,accX. */ +{"msacw", 4, two(0xa000, 0x0100), two(0xf100, 0x0f00), "uMum4/RneG", mcfemac },/* Ry,Rx,,accX. */ +{"msacw", 4, two(0xa000, 0x0100), two(0xf130, 0x0900), "uMumiIeH", mcfemac },/* Ry,Rx,SF,accX. */ +{"msacw", 4, two(0xa000, 0x0300), two(0xf130, 0x0900), "uMumMheH", mcfemac },/* Ry,Rx,+1/-1,accX. */ +{"msacw", 4, two(0xa000, 0x0100), two(0xf130, 0x0f00), "uMumeH", mcfemac }, /* Ry,Rx,accX. */ + +{"msacl", 4, two(0xa080, 0x0900), two(0xf180, 0x0910), "RNRoiI4/Rn", mcfmac }, +{"msacl", 4, two(0xa080, 0x0b00), two(0xf180, 0x0910), "RNRoMh4/Rn", mcfmac }, +{"msacl", 4, two(0xa080, 0x0900), two(0xf180, 0x0f10), "RNRo4/Rn", mcfmac }, +{"msacl", 4, two(0xa000, 0x0900), two(0xf1b0, 0x0b00), "RMRmiI", mcfmac }, +{"msacl", 4, two(0xa000, 0x0b00), two(0xf1b0, 0x0b00), "RMRmMh", mcfmac }, +{"msacl", 4, two(0xa000, 0x0900), two(0xf1b0, 0x0800), "RMRm", mcfmac }, + +{"msacl", 4, two(0xa000, 0x0900), two(0xf100, 0x0900), "R3R1iI4/RneG", mcfemac }, +{"msacl", 4, two(0xa000, 0x0b00), two(0xf100, 0x0900), "R3R1Mh4/RneG", mcfemac }, +{"msacl", 4, two(0xa000, 0x0900), two(0xf100, 0x0f00), "R3R14/RneG", mcfemac }, +{"msacl", 4, two(0xa000, 0x0900), two(0xf130, 0x0900), "RMRmiIeH", mcfemac }, +{"msacl", 4, two(0xa000, 0x0b00), two(0xf130, 0x0900), "RMRmMheH", mcfemac }, +{"msacl", 4, two(0xa000, 0x0900), two(0xf130, 0x0f00), "RMRmeH", mcfemac }, + +{"mulsw", 2, one(0140700), one(0170700), ";wDd", m68000up|mcfisa_a }, +{"mulsl", 4, two(0046000,004000), two(0177700,0107770), ";lD1", m68020up|cpu32 }, +{"mulsl", 4, two(0046000,004000), two(0177700,0107770), "qsD1", mcfisa_a }, +{"mulsl", 4, two(0046000,006000), two(0177700,0107770), ";lD3D1",m68020up|cpu32 }, + +{"muluw", 2, one(0140300), one(0170700), ";wDd", m68000up|mcfisa_a }, +{"mulul", 4, two(0046000,000000), two(0177700,0107770), ";lD1", m68020up|cpu32 }, +{"mulul", 4, two(0046000,000000), two(0177700,0107770), "qsD1", mcfisa_a }, +{"mulul", 4, two(0046000,002000), two(0177700,0107770), ";lD3D1",m68020up|cpu32 }, + +{"nbcd", 2, one(0044000), one(0177700), "$s", m68000up }, + +{"negb", 2, one(0042000), one(0177700), "$s", m68000up }, +{"negw", 2, one(0042100), one(0177700), "$s", m68000up }, +{"negl", 2, one(0042200), one(0177700), "$s", m68000up }, +{"negl", 2, one(0042200), one(0177700), "Ds", mcfisa_a}, + +{"negxb", 2, one(0040000), one(0177700), "$s", m68000up }, +{"negxw", 2, one(0040100), one(0177700), "$s", m68000up }, +{"negxl", 2, one(0040200), one(0177700), "$s", m68000up }, +{"negxl", 2, one(0040200), one(0177700), "Ds", mcfisa_a}, + +{"nop", 2, one(0047161), one(0177777), "", m68000up | mcfisa_a}, + +{"notb", 2, one(0043000), one(0177700), "$s", m68000up }, +{"notw", 2, one(0043100), one(0177700), "$s", m68000up }, +{"notl", 2, one(0043200), one(0177700), "$s", m68000up }, +{"notl", 2, one(0043200), one(0177700), "Ds", mcfisa_a}, + +{"orib", 4, one(0000000), one(0177700), "#b$s", m68000up }, +{"orib", 4, one(0000074), one(0177777), "#bCs", m68000up }, +{"oriw", 4, one(0000100), one(0177700), "#w$s", m68000up }, +{"oriw", 4, one(0000174), one(0177777), "#wSs", m68000up }, +{"oril", 6, one(0000200), one(0177700), "#l$s", m68000up }, +{"oril", 6, one(0000200), one(0177700), "#lDs", mcfisa_a }, +{"ori", 4, one(0000074), one(0177777), "#bCs", m68000up }, +{"ori", 4, one(0000100), one(0177700), "#w$s", m68000up }, +{"ori", 4, one(0000174), one(0177777), "#wSs", m68000up }, + +/* The or opcode can generate the ori instruction. */ +{"orb", 4, one(0000000), one(0177700), "#b$s", m68000up }, +{"orb", 4, one(0000074), one(0177777), "#bCs", m68000up }, +{"orb", 2, one(0100000), one(0170700), ";bDd", m68000up }, +{"orb", 2, one(0100400), one(0170700), "Dd~s", m68000up }, +{"orw", 4, one(0000100), one(0177700), "#w$s", m68000up }, +{"orw", 4, one(0000174), one(0177777), "#wSs", m68000up }, +{"orw", 2, one(0100100), one(0170700), ";wDd", m68000up }, +{"orw", 2, one(0100500), one(0170700), "Dd~s", m68000up }, +{"orl", 6, one(0000200), one(0177700), "#l$s", m68000up }, +{"orl", 6, one(0000200), one(0177700), "#lDs", mcfisa_a }, +{"orl", 2, one(0100200), one(0170700), ";lDd", m68000up | mcfisa_a }, +{"orl", 2, one(0100600), one(0170700), "Dd~s", m68000up | mcfisa_a }, +{"or", 4, one(0000074), one(0177777), "#bCs", m68000up }, +{"or", 4, one(0000100), one(0177700), "#w$s", m68000up }, +{"or", 4, one(0000174), one(0177777), "#wSs", m68000up }, +{"or", 2, one(0100100), one(0170700), ";wDd", m68000up }, +{"or", 2, one(0100500), one(0170700), "Dd~s", m68000up }, + +{"pack", 4, one(0100500), one(0170770), "DsDd#w", m68020up }, +{"pack", 4, one(0100510), one(0170770), "-s-d#w", m68020up }, + +{"pbac", 2, one(0xf087), one(0xffbf), "Bc", m68851 }, +{"pbacw", 2, one(0xf087), one(0xffff), "BW", m68851 }, +{"pbas", 2, one(0xf086), one(0xffbf), "Bc", m68851 }, +{"pbasw", 2, one(0xf086), one(0xffff), "BW", m68851 }, +{"pbbc", 2, one(0xf081), one(0xffbf), "Bc", m68851 }, +{"pbbcw", 2, one(0xf081), one(0xffff), "BW", m68851 }, +{"pbbs", 2, one(0xf080), one(0xffbf), "Bc", m68851 }, +{"pbbsw", 2, one(0xf080), one(0xffff), "BW", m68851 }, +{"pbcc", 2, one(0xf08f), one(0xffbf), "Bc", m68851 }, +{"pbccw", 2, one(0xf08f), one(0xffff), "BW", m68851 }, +{"pbcs", 2, one(0xf08e), one(0xffbf), "Bc", m68851 }, +{"pbcsw", 2, one(0xf08e), one(0xffff), "BW", m68851 }, +{"pbgc", 2, one(0xf08d), one(0xffbf), "Bc", m68851 }, +{"pbgcw", 2, one(0xf08d), one(0xffff), "BW", m68851 }, +{"pbgs", 2, one(0xf08c), one(0xffbf), "Bc", m68851 }, +{"pbgsw", 2, one(0xf08c), one(0xffff), "BW", m68851 }, +{"pbic", 2, one(0xf08b), one(0xffbf), "Bc", m68851 }, +{"pbicw", 2, one(0xf08b), one(0xffff), "BW", m68851 }, +{"pbis", 2, one(0xf08a), one(0xffbf), "Bc", m68851 }, +{"pbisw", 2, one(0xf08a), one(0xffff), "BW", m68851 }, +{"pblc", 2, one(0xf083), one(0xffbf), "Bc", m68851 }, +{"pblcw", 2, one(0xf083), one(0xffff), "BW", m68851 }, +{"pbls", 2, one(0xf082), one(0xffbf), "Bc", m68851 }, +{"pblsw", 2, one(0xf082), one(0xffff), "BW", m68851 }, +{"pbsc", 2, one(0xf085), one(0xffbf), "Bc", m68851 }, +{"pbscw", 2, one(0xf085), one(0xffff), "BW", m68851 }, +{"pbss", 2, one(0xf084), one(0xffbf), "Bc", m68851 }, +{"pbssw", 2, one(0xf084), one(0xffff), "BW", m68851 }, +{"pbwc", 2, one(0xf089), one(0xffbf), "Bc", m68851 }, +{"pbwcw", 2, one(0xf089), one(0xffff), "BW", m68851 }, +{"pbws", 2, one(0xf088), one(0xffbf), "Bc", m68851 }, +{"pbwsw", 2, one(0xf088), one(0xffff), "BW", m68851 }, + +{"pdbac", 4, two(0xf048, 0x0007), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbas", 4, two(0xf048, 0x0006), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbbc", 4, two(0xf048, 0x0001), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbbs", 4, two(0xf048, 0x0000), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbcc", 4, two(0xf048, 0x000f), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbcs", 4, two(0xf048, 0x000e), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbgc", 4, two(0xf048, 0x000d), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbgs", 4, two(0xf048, 0x000c), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbic", 4, two(0xf048, 0x000b), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbis", 4, two(0xf048, 0x000a), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdblc", 4, two(0xf048, 0x0003), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbls", 4, two(0xf048, 0x0002), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbsc", 4, two(0xf048, 0x0005), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbss", 4, two(0xf048, 0x0004), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbwc", 4, two(0xf048, 0x0009), two(0xfff8, 0xffff), "DsBw", m68851 }, +{"pdbws", 4, two(0xf048, 0x0008), two(0xfff8, 0xffff), "DsBw", m68851 }, + +{"pea", 2, one(0044100), one(0177700), "!s", m68000up|mcfisa_a }, + +{"pflusha", 2, one(0xf518), one(0xfff8), "", m68040up }, +{"pflusha", 4, two(0xf000,0x2400), two(0xffff,0xffff), "", m68030 | m68851 }, + +{"pflush", 4, two(0xf000,0x3010), two(0xffc0,0xfe10), "T3T9", m68030|m68851 }, +{"pflush", 4, two(0xf000,0x3810), two(0xffc0,0xfe10), "T3T9&s", m68030|m68851 }, +{"pflush", 4, two(0xf000,0x3008), two(0xffc0,0xfe18), "D3T9", m68030|m68851 }, +{"pflush", 4, two(0xf000,0x3808), two(0xffc0,0xfe18), "D3T9&s", m68030|m68851 }, +{"pflush", 4, two(0xf000,0x3000), two(0xffc0,0xfe1e), "f3T9", m68030|m68851 }, +{"pflush", 4, two(0xf000,0x3800), two(0xffc0,0xfe1e), "f3T9&s", m68030|m68851 }, +{"pflush", 2, one(0xf508), one(0xfff8), "as", m68040up }, +{"pflush", 2, one(0xf508), one(0xfff8), "As", m68040up }, + +{"pflushan", 2, one(0xf510), one(0xfff8), "", m68040up }, +{"pflushn", 2, one(0xf500), one(0xfff8), "as", m68040up }, +{"pflushn", 2, one(0xf500), one(0xfff8), "As", m68040up }, + +{"pflushr", 4, two(0xf000, 0xa000), two(0xffc0, 0xffff), "|s", m68851 }, + +{"pflushs", 4, two(0xf000, 0x3410), two(0xfff8, 0xfe10), "T3T9", m68851 }, +{"pflushs", 4, two(0xf000, 0x3c10), two(0xfff8, 0xfe10), "T3T9&s", m68851 }, +{"pflushs", 4, two(0xf000, 0x3408), two(0xfff8, 0xfe18), "D3T9", m68851 }, +{"pflushs", 4, two(0xf000, 0x3c08), two(0xfff8, 0xfe18), "D3T9&s", m68851 }, +{"pflushs", 4, two(0xf000, 0x3400), two(0xfff8, 0xfe1e), "f3T9", m68851 }, +{"pflushs", 4, two(0xf000, 0x3c00), two(0xfff8, 0xfe1e), "f3T9&s", m68851 }, + +{"ploadr", 4, two(0xf000,0x2210), two(0xffc0,0xfff0), "T3&s", m68030|m68851 }, +{"ploadr", 4, two(0xf000,0x2208), two(0xffc0,0xfff8), "D3&s", m68030|m68851 }, +{"ploadr", 4, two(0xf000,0x2200), two(0xffc0,0xfffe), "f3&s", m68030|m68851 }, +{"ploadw", 4, two(0xf000,0x2010), two(0xffc0,0xfff0), "T3&s", m68030|m68851 }, +{"ploadw", 4, two(0xf000,0x2008), two(0xffc0,0xfff8), "D3&s", m68030|m68851 }, +{"ploadw", 4, two(0xf000,0x2000), two(0xffc0,0xfffe), "f3&s", m68030|m68851 }, + +{"plpar", 2, one(0xf5c8), one(0xfff8), "as", m68060 }, +{"plpaw", 2, one(0xf588), one(0xfff8), "as", m68060 }, + +{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xffff), "*l08", m68030|m68851 }, +{"pmove", 4, two(0xf000,0x5c00), two(0xffc0,0xffff), "*w18", m68851 }, +{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xe3ff), "*b28", m68851 }, +{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xffff), "08%s", m68030|m68851 }, +{"pmove", 4, two(0xf000,0x5e00), two(0xffc0,0xffff), "18%s", m68851 }, +{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xe3ff), "28%s", m68851 }, +{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xe3ff), "|sW8", m68030|m68851 }, +{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xe3ff), "W8~s", m68030|m68851 }, +{"pmove", 4, two(0xf000,0x6200), two(0xffc0,0xe3e3), "*wX3", m68851 }, +{"pmove", 4, two(0xf000,0x6000), two(0xffc0,0xe3e3), "X3%s", m68851 }, +{"pmove", 4, two(0xf000,0x6000), two(0xffc0,0xffff), "*wY8", m68030|m68851 }, +{"pmove", 4, two(0xf000,0x6200), two(0xffc0,0xffff), "Y8%s", m68030|m68851 }, +{"pmove", 4, two(0xf000,0x6600), two(0xffc0,0xffff), "Z8%s", m68851 }, +{"pmove", 4, two(0xf000,0x0800), two(0xffc0,0xfbff), "*l38", m68030 }, +{"pmove", 4, two(0xf000,0x0a00), two(0xffc0,0xfbff), "38%s", m68030 }, + +{"pmovefd", 4, two(0xf000, 0x4100), two(0xffc0, 0xe3ff), "*l08", m68030 }, +{"pmovefd", 4, two(0xf000, 0x4100), two(0xffc0, 0xe3ff), "|sW8", m68030 }, +{"pmovefd", 4, two(0xf000, 0x0900), two(0xffc0, 0xfbff), "*l38", m68030 }, + +{"prestore", 2, one(0xf140), one(0xffc0), "s", m68851 }, + +{"psac", 4, two(0xf040, 0x0007), two(0xffc0, 0xffff), "$s", m68851 }, +{"psas", 4, two(0xf040, 0x0006), two(0xffc0, 0xffff), "$s", m68851 }, +{"psbc", 4, two(0xf040, 0x0001), two(0xffc0, 0xffff), "$s", m68851 }, +{"psbs", 4, two(0xf040, 0x0000), two(0xffc0, 0xffff), "$s", m68851 }, +{"pscc", 4, two(0xf040, 0x000f), two(0xffc0, 0xffff), "$s", m68851 }, +{"pscs", 4, two(0xf040, 0x000e), two(0xffc0, 0xffff), "$s", m68851 }, +{"psgc", 4, two(0xf040, 0x000d), two(0xffc0, 0xffff), "$s", m68851 }, +{"psgs", 4, two(0xf040, 0x000c), two(0xffc0, 0xffff), "$s", m68851 }, +{"psic", 4, two(0xf040, 0x000b), two(0xffc0, 0xffff), "$s", m68851 }, +{"psis", 4, two(0xf040, 0x000a), two(0xffc0, 0xffff), "$s", m68851 }, +{"pslc", 4, two(0xf040, 0x0003), two(0xffc0, 0xffff), "$s", m68851 }, +{"psls", 4, two(0xf040, 0x0002), two(0xffc0, 0xffff), "$s", m68851 }, +{"pssc", 4, two(0xf040, 0x0005), two(0xffc0, 0xffff), "$s", m68851 }, +{"psss", 4, two(0xf040, 0x0004), two(0xffc0, 0xffff), "$s", m68851 }, +{"pswc", 4, two(0xf040, 0x0009), two(0xffc0, 0xffff), "$s", m68851 }, +{"psws", 4, two(0xf040, 0x0008), two(0xffc0, 0xffff), "$s", m68851 }, + +{"ptestr", 4, two(0xf000,0x8210), two(0xffc0, 0xe3f0), "T3&st8", m68030|m68851 }, +{"ptestr", 4, two(0xf000,0x8310), two(0xffc0,0xe310), "T3&st8A9", m68030|m68851 }, +{"ptestr", 4, two(0xf000,0x8208), two(0xffc0,0xe3f8), "D3&st8", m68030|m68851 }, +{"ptestr", 4, two(0xf000,0x8308), two(0xffc0,0xe318), "D3&st8A9", m68030|m68851 }, +{"ptestr", 4, two(0xf000,0x8200), two(0xffc0,0xe3fe), "f3&st8", m68030|m68851 }, +{"ptestr", 4, two(0xf000,0x8300), two(0xffc0,0xe31e), "f3&st8A9", m68030|m68851 }, +{"ptestr", 2, one(0xf568), one(0xfff8), "as", m68040 }, + +{"ptestw", 4, two(0xf000,0x8010), two(0xffc0,0xe3f0), "T3&st8", m68030|m68851 }, +{"ptestw", 4, two(0xf000,0x8110), two(0xffc0,0xe310), "T3&st8A9", m68030|m68851 }, +{"ptestw", 4, two(0xf000,0x8008), two(0xffc0,0xe3f8), "D3&st8", m68030|m68851 }, +{"ptestw", 4, two(0xf000,0x8108), two(0xffc0,0xe318), "D3&st8A9", m68030|m68851 }, +{"ptestw", 4, two(0xf000,0x8000), two(0xffc0,0xe3fe), "f3&st8", m68030|m68851 }, +{"ptestw", 4, two(0xf000,0x8100), two(0xffc0,0xe31e), "f3&st8A9", m68030|m68851 }, +{"ptestw", 2, one(0xf548), one(0xfff8), "as", m68040 }, + +{"ptrapacw", 6, two(0xf07a, 0x0007), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapacl", 6, two(0xf07b, 0x0007), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapac", 4, two(0xf07c, 0x0007), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapasw", 6, two(0xf07a, 0x0006), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapasl", 6, two(0xf07b, 0x0006), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapas", 4, two(0xf07c, 0x0006), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapbcw", 6, two(0xf07a, 0x0001), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapbcl", 6, two(0xf07b, 0x0001), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapbc", 4, two(0xf07c, 0x0001), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapbsw", 6, two(0xf07a, 0x0000), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapbsl", 6, two(0xf07b, 0x0000), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapbs", 4, two(0xf07c, 0x0000), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapccw", 6, two(0xf07a, 0x000f), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapccl", 6, two(0xf07b, 0x000f), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapcc", 4, two(0xf07c, 0x000f), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapcsw", 6, two(0xf07a, 0x000e), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapcsl", 6, two(0xf07b, 0x000e), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapcs", 4, two(0xf07c, 0x000e), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapgcw", 6, two(0xf07a, 0x000d), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapgcl", 6, two(0xf07b, 0x000d), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapgc", 4, two(0xf07c, 0x000d), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapgsw", 6, two(0xf07a, 0x000c), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapgsl", 6, two(0xf07b, 0x000c), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapgs", 4, two(0xf07c, 0x000c), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapicw", 6, two(0xf07a, 0x000b), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapicl", 6, two(0xf07b, 0x000b), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapic", 4, two(0xf07c, 0x000b), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapisw", 6, two(0xf07a, 0x000a), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapisl", 6, two(0xf07b, 0x000a), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapis", 4, two(0xf07c, 0x000a), two(0xffff, 0xffff), "", m68851 }, + +{"ptraplcw", 6, two(0xf07a, 0x0003), two(0xffff, 0xffff), "#w", m68851 }, +{"ptraplcl", 6, two(0xf07b, 0x0003), two(0xffff, 0xffff), "#l", m68851 }, +{"ptraplc", 4, two(0xf07c, 0x0003), two(0xffff, 0xffff), "", m68851 }, + +{"ptraplsw", 6, two(0xf07a, 0x0002), two(0xffff, 0xffff), "#w", m68851 }, +{"ptraplsl", 6, two(0xf07b, 0x0002), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapls", 4, two(0xf07c, 0x0002), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapscw", 6, two(0xf07a, 0x0005), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapscl", 6, two(0xf07b, 0x0005), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapsc", 4, two(0xf07c, 0x0005), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapssw", 6, two(0xf07a, 0x0004), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapssl", 6, two(0xf07b, 0x0004), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapss", 4, two(0xf07c, 0x0004), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapwcw", 6, two(0xf07a, 0x0009), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapwcl", 6, two(0xf07b, 0x0009), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapwc", 4, two(0xf07c, 0x0009), two(0xffff, 0xffff), "", m68851 }, + +{"ptrapwsw", 6, two(0xf07a, 0x0008), two(0xffff, 0xffff), "#w", m68851 }, +{"ptrapwsl", 6, two(0xf07b, 0x0008), two(0xffff, 0xffff), "#l", m68851 }, +{"ptrapws", 4, two(0xf07c, 0x0008), two(0xffff, 0xffff), "", m68851 }, + +{"pulse", 2, one(0045314), one(0177777), "", m68060 | mcfisa_a }, + +{"pvalid", 4, two(0xf000, 0x2800), two(0xffc0, 0xffff), "Vs&s", m68851 }, +{"pvalid", 4, two(0xf000, 0x2c00), two(0xffc0, 0xfff8), "A3&s", m68851 }, + + /* FIXME: don't allow Dw==Dx. */ +{"remsl", 4, two(0x4c40, 0x0800), two(0xffc0, 0x8ff8), "qsD3D1", mcfhwdiv }, +{"remul", 4, two(0x4c40, 0x0000), two(0xffc0, 0x8ff8), "qsD3D1", mcfhwdiv }, + +{"reset", 2, one(0047160), one(0177777), "", m68000up }, + +{"rolb", 2, one(0160430), one(0170770), "QdDs", m68000up }, +{"rolb", 2, one(0160470), one(0170770), "DdDs", m68000up }, +{"rolw", 2, one(0160530), one(0170770), "QdDs", m68000up }, +{"rolw", 2, one(0160570), one(0170770), "DdDs", m68000up }, +{"rolw", 2, one(0163700), one(0177700), "~s", m68000up }, +{"roll", 2, one(0160630), one(0170770), "QdDs", m68000up }, +{"roll", 2, one(0160670), one(0170770), "DdDs", m68000up }, + +{"rorb", 2, one(0160030), one(0170770), "QdDs", m68000up }, +{"rorb", 2, one(0160070), one(0170770), "DdDs", m68000up }, +{"rorw", 2, one(0160130), one(0170770), "QdDs", m68000up }, +{"rorw", 2, one(0160170), one(0170770), "DdDs", m68000up }, +{"rorw", 2, one(0163300), one(0177700), "~s", m68000up }, +{"rorl", 2, one(0160230), one(0170770), "QdDs", m68000up }, +{"rorl", 2, one(0160270), one(0170770), "DdDs", m68000up }, + +{"roxlb", 2, one(0160420), one(0170770), "QdDs", m68000up }, +{"roxlb", 2, one(0160460), one(0170770), "DdDs", m68000up }, +{"roxlw", 2, one(0160520), one(0170770), "QdDs", m68000up }, +{"roxlw", 2, one(0160560), one(0170770), "DdDs", m68000up }, +{"roxlw", 2, one(0162700), one(0177700), "~s", m68000up }, +{"roxll", 2, one(0160620), one(0170770), "QdDs", m68000up }, +{"roxll", 2, one(0160660), one(0170770), "DdDs", m68000up }, + +{"roxrb", 2, one(0160020), one(0170770), "QdDs", m68000up }, +{"roxrb", 2, one(0160060), one(0170770), "DdDs", m68000up }, +{"roxrw", 2, one(0160120), one(0170770), "QdDs", m68000up }, +{"roxrw", 2, one(0160160), one(0170770), "DdDs", m68000up }, +{"roxrw", 2, one(0162300), one(0177700), "~s", m68000up }, +{"roxrl", 2, one(0160220), one(0170770), "QdDs", m68000up }, +{"roxrl", 2, one(0160260), one(0170770), "DdDs", m68000up }, + +{"rtd", 4, one(0047164), one(0177777), "#w", m68010up }, + +{"rte", 2, one(0047163), one(0177777), "", m68000up | mcfisa_a }, + +{"rtm", 2, one(0003300), one(0177760), "Rs", m68020 }, + +{"rtr", 2, one(0047167), one(0177777), "", m68000up }, + +{"rts", 2, one(0047165), one(0177777), "", m68000up | mcfisa_a }, + +{"satsl", 2, one(0046200), one(0177770), "Ds", mcfisa_b }, + +{"sbcd", 2, one(0100400), one(0170770), "DsDd", m68000up }, +{"sbcd", 2, one(0100410), one(0170770), "-s-d", m68000up }, + +{"scc", 2, one(0052300), one(0177700), "$s", m68000up }, +{"scc", 2, one(0052300), one(0177700), "Ds", mcfisa_a }, +{"scs", 2, one(0052700), one(0177700), "$s", m68000up }, +{"scs", 2, one(0052700), one(0177700), "Ds", mcfisa_a }, +{"seq", 2, one(0053700), one(0177700), "$s", m68000up }, +{"seq", 2, one(0053700), one(0177700), "Ds", mcfisa_a }, +{"sf", 2, one(0050700), one(0177700), "$s", m68000up }, +{"sf", 2, one(0050700), one(0177700), "Ds", mcfisa_a }, +{"sge", 2, one(0056300), one(0177700), "$s", m68000up }, +{"sge", 2, one(0056300), one(0177700), "Ds", mcfisa_a }, +{"sgt", 2, one(0057300), one(0177700), "$s", m68000up }, +{"sgt", 2, one(0057300), one(0177700), "Ds", mcfisa_a }, +{"shi", 2, one(0051300), one(0177700), "$s", m68000up }, +{"shi", 2, one(0051300), one(0177700), "Ds", mcfisa_a }, +{"sle", 2, one(0057700), one(0177700), "$s", m68000up }, +{"sle", 2, one(0057700), one(0177700), "Ds", mcfisa_a }, +{"sls", 2, one(0051700), one(0177700), "$s", m68000up }, +{"sls", 2, one(0051700), one(0177700), "Ds", mcfisa_a }, +{"slt", 2, one(0056700), one(0177700), "$s", m68000up }, +{"slt", 2, one(0056700), one(0177700), "Ds", mcfisa_a }, +{"smi", 2, one(0055700), one(0177700), "$s", m68000up }, +{"smi", 2, one(0055700), one(0177700), "Ds", mcfisa_a }, +{"sne", 2, one(0053300), one(0177700), "$s", m68000up }, +{"sne", 2, one(0053300), one(0177700), "Ds", mcfisa_a }, +{"spl", 2, one(0055300), one(0177700), "$s", m68000up }, +{"spl", 2, one(0055300), one(0177700), "Ds", mcfisa_a }, +{"st", 2, one(0050300), one(0177700), "$s", m68000up }, +{"st", 2, one(0050300), one(0177700), "Ds", mcfisa_a }, +{"svc", 2, one(0054300), one(0177700), "$s", m68000up }, +{"svc", 2, one(0054300), one(0177700), "Ds", mcfisa_a }, +{"svs", 2, one(0054700), one(0177700), "$s", m68000up }, +{"svs", 2, one(0054700), one(0177700), "Ds", mcfisa_a }, + +{"stop", 4, one(0047162), one(0177777), "#w", m68000up | mcfisa_a }, + +{"strldsr", 4, two(0040347,0043374), two(0177777,0177777), "#w", mcfisa_aa}, + +{"subal", 2, one(0110700), one(0170700), "*lAd", m68000up | mcfisa_a }, +{"subaw", 2, one(0110300), one(0170700), "*wAd", m68000up }, + +{"subib", 4, one(0002000), one(0177700), "#b$s", m68000up }, +{"subiw", 4, one(0002100), one(0177700), "#w$s", m68000up }, +{"subil", 6, one(0002200), one(0177700), "#l$s", m68000up }, +{"subil", 6, one(0002200), one(0177700), "#lDs", mcfisa_a }, + +{"subqb", 2, one(0050400), one(0170700), "Qd%s", m68000up }, +{"subqw", 2, one(0050500), one(0170700), "Qd%s", m68000up }, +{"subql", 2, one(0050600), one(0170700), "Qd%s", m68000up | mcfisa_a }, + +/* The sub opcode can generate the suba, subi, and subq instructions. */ +{"subb", 2, one(0050400), one(0170700), "Qd%s", m68000up }, +{"subb", 4, one(0002000), one(0177700), "#b$s", m68000up }, +{"subb", 2, one(0110000), one(0170700), ";bDd", m68000up }, +{"subb", 2, one(0110400), one(0170700), "Dd~s", m68000up }, +{"subw", 2, one(0050500), one(0170700), "Qd%s", m68000up }, +{"subw", 4, one(0002100), one(0177700), "#w$s", m68000up }, +{"subw", 2, one(0110300), one(0170700), "*wAd", m68000up }, +{"subw", 2, one(0110100), one(0170700), "*wDd", m68000up }, +{"subw", 2, one(0110500), one(0170700), "Dd~s", m68000up }, +{"subl", 2, one(0050600), one(0170700), "Qd%s", m68000up | mcfisa_a }, +{"subl", 6, one(0002200), one(0177700), "#l$s", m68000up }, +{"subl", 6, one(0002200), one(0177700), "#lDs", mcfisa_a }, +{"subl", 2, one(0110700), one(0170700), "*lAd", m68000up | mcfisa_a }, +{"subl", 2, one(0110200), one(0170700), "*lDd", m68000up | mcfisa_a }, +{"subl", 2, one(0110600), one(0170700), "Dd~s", m68000up | mcfisa_a }, + +{"subxb", 2, one(0110400), one(0170770), "DsDd", m68000up }, +{"subxb", 2, one(0110410), one(0170770), "-s-d", m68000up }, +{"subxw", 2, one(0110500), one(0170770), "DsDd", m68000up }, +{"subxw", 2, one(0110510), one(0170770), "-s-d", m68000up }, +{"subxl", 2, one(0110600), one(0170770), "DsDd", m68000up | mcfisa_a }, +{"subxl", 2, one(0110610), one(0170770), "-s-d", m68000up }, + +{"swap", 2, one(0044100), one(0177770), "Ds", m68000up | mcfisa_a }, + +/* swbeg and swbegl are magic constants used on sysV68. The compiler + generates them before a switch table. They tell the debugger and + disassembler that a switch table follows. The parameter is the + number of elements in the table. swbeg means that the entries in + the table are word (2 byte) sized, and swbegl means that the + entries in the table are longword (4 byte) sized. */ +{"swbeg", 4, one(0045374), one(0177777), "#w", m68000up | mcfisa_a }, +{"swbegl", 6, one(0045375), one(0177777), "#l", m68000up | mcfisa_a }, + +{"tas", 2, one(0045300), one(0177700), "$s", m68000up | mcfisa_b}, + +#define TBL1(name,insn_size,signed,round,size) \ + {name, insn_size, two(0174000, (signed<<11)|(!round<<10)|(size<<6)|0000400), \ + two(0177700,0107777), "!sD1", cpu32 }, \ + {name, insn_size, two(0174000, (signed<<11)|(!round<<10)|(size<<6)), \ + two(0177770,0107770), "DsD3D1", cpu32 } +#define TBL(name1, name2, name3, s, r) \ + TBL1(name1, 4, s, r, 0), TBL1(name2, 4, s, r, 1), TBL1(name3, 4, s, r, 2) +TBL("tblsb", "tblsw", "tblsl", 2, 1), +TBL("tblsnb", "tblsnw", "tblsnl", 2, 0), +TBL("tblub", "tbluw", "tblul", 0, 1), +TBL("tblunb", "tblunw", "tblunl", 0, 0), + +{"trap", 2, one(0047100), one(0177760), "Ts", m68000up | mcfisa_a }, + +{"trapcc", 2, one(0052374), one(0177777), "", m68020up | cpu32 }, +{"trapcs", 2, one(0052774), one(0177777), "", m68020up | cpu32 }, +{"trapeq", 2, one(0053774), one(0177777), "", m68020up | cpu32 }, +{"trapf", 2, one(0050774), one(0177777), "", m68020up | cpu32 | mcfisa_a }, +{"trapge", 2, one(0056374), one(0177777), "", m68020up | cpu32 }, +{"trapgt", 2, one(0057374), one(0177777), "", m68020up | cpu32 }, +{"traphi", 2, one(0051374), one(0177777), "", m68020up | cpu32 }, +{"traple", 2, one(0057774), one(0177777), "", m68020up | cpu32 }, +{"trapls", 2, one(0051774), one(0177777), "", m68020up | cpu32 }, +{"traplt", 2, one(0056774), one(0177777), "", m68020up | cpu32 }, +{"trapmi", 2, one(0055774), one(0177777), "", m68020up | cpu32 }, +{"trapne", 2, one(0053374), one(0177777), "", m68020up | cpu32 }, +{"trappl", 2, one(0055374), one(0177777), "", m68020up | cpu32 }, +{"trapt", 2, one(0050374), one(0177777), "", m68020up | cpu32 }, +{"trapvc", 2, one(0054374), one(0177777), "", m68020up | cpu32 }, +{"trapvs", 2, one(0054774), one(0177777), "", m68020up | cpu32 }, + +{"trapccw", 4, one(0052372), one(0177777), "#w", m68020up|cpu32 }, +{"trapcsw", 4, one(0052772), one(0177777), "#w", m68020up|cpu32 }, +{"trapeqw", 4, one(0053772), one(0177777), "#w", m68020up|cpu32 }, +{"trapfw", 4, one(0050772), one(0177777), "#w", m68020up|cpu32|mcfisa_a}, +{"trapgew", 4, one(0056372), one(0177777), "#w", m68020up|cpu32 }, +{"trapgtw", 4, one(0057372), one(0177777), "#w", m68020up|cpu32 }, +{"traphiw", 4, one(0051372), one(0177777), "#w", m68020up|cpu32 }, +{"traplew", 4, one(0057772), one(0177777), "#w", m68020up|cpu32 }, +{"traplsw", 4, one(0051772), one(0177777), "#w", m68020up|cpu32 }, +{"trapltw", 4, one(0056772), one(0177777), "#w", m68020up|cpu32 }, +{"trapmiw", 4, one(0055772), one(0177777), "#w", m68020up|cpu32 }, +{"trapnew", 4, one(0053372), one(0177777), "#w", m68020up|cpu32 }, +{"trapplw", 4, one(0055372), one(0177777), "#w", m68020up|cpu32 }, +{"traptw", 4, one(0050372), one(0177777), "#w", m68020up|cpu32 }, +{"trapvcw", 4, one(0054372), one(0177777), "#w", m68020up|cpu32 }, +{"trapvsw", 4, one(0054772), one(0177777), "#w", m68020up|cpu32 }, + +{"trapccl", 6, one(0052373), one(0177777), "#l", m68020up|cpu32 }, +{"trapcsl", 6, one(0052773), one(0177777), "#l", m68020up|cpu32 }, +{"trapeql", 6, one(0053773), one(0177777), "#l", m68020up|cpu32 }, +{"trapfl", 6, one(0050773), one(0177777), "#l", m68020up|cpu32|mcfisa_a}, +{"trapgel", 6, one(0056373), one(0177777), "#l", m68020up|cpu32 }, +{"trapgtl", 6, one(0057373), one(0177777), "#l", m68020up|cpu32 }, +{"traphil", 6, one(0051373), one(0177777), "#l", m68020up|cpu32 }, +{"traplel", 6, one(0057773), one(0177777), "#l", m68020up|cpu32 }, +{"traplsl", 6, one(0051773), one(0177777), "#l", m68020up|cpu32 }, +{"trapltl", 6, one(0056773), one(0177777), "#l", m68020up|cpu32 }, +{"trapmil", 6, one(0055773), one(0177777), "#l", m68020up|cpu32 }, +{"trapnel", 6, one(0053373), one(0177777), "#l", m68020up|cpu32 }, +{"trappll", 6, one(0055373), one(0177777), "#l", m68020up|cpu32 }, +{"traptl", 6, one(0050373), one(0177777), "#l", m68020up|cpu32 }, +{"trapvcl", 6, one(0054373), one(0177777), "#l", m68020up|cpu32 }, +{"trapvsl", 6, one(0054773), one(0177777), "#l", m68020up|cpu32 }, + +{"trapv", 2, one(0047166), one(0177777), "", m68000up }, + +{"tstb", 2, one(0045000), one(0177700), ";b", m68020up|cpu32|mcfisa_a }, +{"tstb", 2, one(0045000), one(0177700), "$b", m68000up }, +{"tstw", 2, one(0045100), one(0177700), "*w", m68020up|cpu32|mcfisa_a }, +{"tstw", 2, one(0045100), one(0177700), "$w", m68000up }, +{"tstl", 2, one(0045200), one(0177700), "*l", m68020up|cpu32|mcfisa_a }, +{"tstl", 2, one(0045200), one(0177700), "$l", m68000up }, + +{"unlk", 2, one(0047130), one(0177770), "As", m68000up | mcfisa_a }, + +{"unpk", 4, one(0100600), one(0170770), "DsDd#w", m68020up }, +{"unpk", 4, one(0100610), one(0170770), "-s-d#w", m68020up }, + +{"wddatab", 2, one(0175400), one(0177700), "~s", mcfisa_a }, +{"wddataw", 2, one(0175500), one(0177700), "~s", mcfisa_a }, +{"wddatal", 2, one(0175600), one(0177700), "~s", mcfisa_a }, + +{"wdebug", 4, two(0175720, 03), two(0177770, 0xffff), "as", mcfisa_a }, +{"wdebug", 4, two(0175750, 03), two(0177770, 0xffff), "ds", mcfisa_a }, +}; + +const int m68k_numopcodes = sizeof m68k_opcodes / sizeof m68k_opcodes[0]; + +/* These aliases used to be in the above table, each one duplicating + all of the entries for its primary exactly. This table was + constructed by mechanical processing of the opcode table, with a + small number of tweaks done by hand. There are probably a lot more + aliases above that could be moved down here, except for very minor + differences. */ + +const struct m68k_opcode_alias m68k_opcode_aliases[] = +{ + { "add", "addw", }, + { "adda", "addaw", }, + { "addi", "addiw", }, + { "addq", "addqw", }, + { "addx", "addxw", }, + { "asl", "aslw", }, + { "asr", "asrw", }, + { "bhi", "bhiw", }, + { "bls", "blsw", }, + { "bcc", "bccw", }, + { "bcs", "bcsw", }, + { "bne", "bnew", }, + { "beq", "beqw", }, + { "bvc", "bvcw", }, + { "bvs", "bvsw", }, + { "bpl", "bplw", }, + { "bmi", "bmiw", }, + { "bge", "bgew", }, + { "blt", "bltw", }, + { "bgt", "bgtw", }, + { "ble", "blew", }, + { "bra", "braw", }, + { "bsr", "bsrw", }, + { "bhib", "bhis", }, + { "blsb", "blss", }, + { "bccb", "bccs", }, + { "bcsb", "bcss", }, + { "bneb", "bnes", }, + { "beqb", "beqs", }, + { "bvcb", "bvcs", }, + { "bvsb", "bvss", }, + { "bplb", "bpls", }, + { "bmib", "bmis", }, + { "bgeb", "bges", }, + { "bltb", "blts", }, + { "bgtb", "bgts", }, + { "bleb", "bles", }, + { "brab", "bras", }, + { "bsrb", "bsrs", }, + { "bhs", "bccw" }, + { "bhss", "bccs" }, + { "bhsb", "bccs" }, + { "bhsw", "bccw" }, + { "bhsl", "bccl" }, + { "blo", "bcsw" }, + { "blos", "bcss" }, + { "blob", "bcss" }, + { "blow", "bcsw" }, + { "blol", "bcsl" }, + { "br", "braw", }, + { "brs", "bras", }, + { "brb", "bras", }, + { "brw", "braw", }, + { "brl", "bral", }, + { "jfnlt", "bcc", }, /* Apparently a sun alias. */ + { "jfngt", "ble", }, /* Apparently a sun alias. */ + { "jfeq", "beqs", }, /* Apparently a sun alias. */ + { "bchgb", "bchg", }, + { "bchgl", "bchg", }, + { "bclrb", "bclr", }, + { "bclrl", "bclr", }, + { "bsetb", "bset", }, + { "bsetl", "bset", }, + { "btstb", "btst", }, + { "btstl", "btst", }, + { "cas2", "cas2w", }, + { "cas", "casw", }, + { "chk2", "chk2w", }, + { "chk", "chkw", }, + { "clr", "clrw", }, + { "cmp2", "cmp2w", }, + { "cmpa", "cmpaw", }, + { "cmpi", "cmpiw", }, + { "cmpm", "cmpmw", }, + { "cmp", "cmpw", }, + { "dbccw", "dbcc", }, + { "dbcsw", "dbcs", }, + { "dbeqw", "dbeq", }, + { "dbfw", "dbf", }, + { "dbgew", "dbge", }, + { "dbgtw", "dbgt", }, + { "dbhiw", "dbhi", }, + { "dblew", "dble", }, + { "dblsw", "dbls", }, + { "dbltw", "dblt", }, + { "dbmiw", "dbmi", }, + { "dbnew", "dbne", }, + { "dbplw", "dbpl", }, + { "dbtw", "dbt", }, + { "dbvcw", "dbvc", }, + { "dbvsw", "dbvs", }, + { "dbhs", "dbcc", }, + { "dbhsw", "dbcc", }, + { "dbra", "dbf", }, + { "dbraw", "dbf", }, + { "tdivsl", "divsl", }, + { "divs", "divsw", }, + { "divu", "divuw", }, + { "ext", "extw", }, + { "extbw", "extw", }, + { "extwl", "extl", }, + { "fbneq", "fbne", }, + { "fbsneq", "fbsne", }, + { "fdbneq", "fdbne", }, + { "fdbsneq", "fdbsne", }, + { "fmovecr", "fmovecrx", }, + { "fmovm", "fmovem", }, + { "fsneq", "fsne", }, + { "fssneq", "fssne", }, + { "ftrapneq", "ftrapne", }, + { "ftrapsneq", "ftrapsne", }, + { "fjneq", "fjne", }, + { "fjsneq", "fjsne", }, + { "jmpl", "jmp", }, + { "jmps", "jmp", }, + { "jsrl", "jsr", }, + { "jsrs", "jsr", }, + { "leal", "lea", }, + { "lsl", "lslw", }, + { "lsr", "lsrw", }, + { "mac", "macw" }, + { "movea", "moveaw", }, + { "movem", "movemw", }, + { "movml", "moveml", }, + { "movmw", "movemw", }, + { "movm", "movemw", }, + { "movep", "movepw", }, + { "movpw", "movepw", }, + { "moves", "movesw" }, + { "muls", "mulsw", }, + { "mulu", "muluw", }, + { "msac", "msacw" }, + { "nbcdb", "nbcd" }, + { "neg", "negw", }, + { "negx", "negxw", }, + { "not", "notw", }, + { "peal", "pea", }, + { "rol", "rolw", }, + { "ror", "rorw", }, + { "roxl", "roxlw", }, + { "roxr", "roxrw", }, + { "sats", "satsl", }, + { "sbcdb", "sbcd", }, + { "sccb", "scc", }, + { "scsb", "scs", }, + { "seqb", "seq", }, + { "sfb", "sf", }, + { "sgeb", "sge", }, + { "sgtb", "sgt", }, + { "shib", "shi", }, + { "sleb", "sle", }, + { "slsb", "sls", }, + { "sltb", "slt", }, + { "smib", "smi", }, + { "sneb", "sne", }, + { "splb", "spl", }, + { "stb", "st", }, + { "svcb", "svc", }, + { "svsb", "svs", }, + { "sfge", "sge", }, + { "sfgt", "sgt", }, + { "sfle", "sle", }, + { "sflt", "slt", }, + { "sfneq", "sne", }, + { "suba", "subaw", }, + { "subi", "subiw", }, + { "subq", "subqw", }, + { "sub", "subw", }, + { "subx", "subxw", }, + { "swapw", "swap", }, + { "tasb", "tas", }, + { "tpcc", "trapcc", }, + { "tcc", "trapcc", }, + { "tst", "tstw", }, + { "jbra", "jra", }, + { "jbhi", "jhi", }, + { "jbls", "jls", }, + { "jbcc", "jcc", }, + { "jbcs", "jcs", }, + { "jbne", "jne", }, + { "jbeq", "jeq", }, + { "jbvc", "jvc", }, + { "jbvs", "jvs", }, + { "jbpl", "jpl", }, + { "jbmi", "jmi", }, + { "jbge", "jge", }, + { "jblt", "jlt", }, + { "jbgt", "jgt", }, + { "jble", "jle", }, + { "movql", "moveq", }, + { "moveql", "moveq", }, + { "movl", "movel", }, + { "movq", "moveq", }, + { "moval", "moveal", }, + { "movaw", "moveaw", }, + { "movb", "moveb", }, + { "movc", "movec", }, + { "movecl", "movec", }, + { "movpl", "movepl", }, + { "movw", "movew", }, + { "movsb", "movesb", }, + { "movsl", "movesl", }, + { "movsw", "movesw", }, + { "mov3q", "mov3ql", }, + + { "tdivul", "divul", }, /* For m68k-svr4. */ + { "fmovb", "fmoveb", }, + { "fsmovb", "fsmoveb", }, + { "fdmovb", "fdmoveb", }, + { "fmovd", "fmoved", }, + { "fsmovd", "fsmoved", }, + { "fmovl", "fmovel", }, + { "fsmovl", "fsmovel", }, + { "fdmovl", "fdmovel", }, + { "fmovp", "fmovep", }, + { "fsmovp", "fsmovep", }, + { "fdmovp", "fdmovep", }, + { "fmovs", "fmoves", }, + { "fsmovs", "fsmoves", }, + { "fdmovs", "fdmoves", }, + { "fmovw", "fmovew", }, + { "fsmovw", "fsmovew", }, + { "fdmovw", "fdmovew", }, + { "fmovx", "fmovex", }, + { "fsmovx", "fsmovex", }, + { "fdmovx", "fdmovex", }, + { "fmovcr", "fmovecr", }, + { "fmovcrx", "fmovecrx", }, + { "ftestb", "ftstb", }, + { "ftestd", "ftstd", }, + { "ftestl", "ftstl", }, + { "ftestp", "ftstp", }, + { "ftests", "ftsts", }, + { "ftestw", "ftstw", }, + { "ftestx", "ftstx", }, + + { "bitrevl", "bitrev", }, + { "byterevl", "byterev", }, + { "ff1l", "ff1", }, + +}; + +const int m68k_numaliases = + sizeof m68k_opcode_aliases / sizeof m68k_opcode_aliases[0]; +/* **** End of m68k-opc.c */ +/* **** floatformat.c from sourceware.org CVS 2005-08-14. */ +/* IEEE floating point support routines, for GDB, the GNU Debugger. + Copyright (C) 1991, 1994, 1999, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . */ + +/* This is needed to pick up the NAN macro on some systems. */ +//#define _GNU_SOURCE + +#ifndef INFINITY +#ifdef HUGE_VAL +#define INFINITY HUGE_VAL +#else +#define INFINITY (1.0 / 0.0) +#endif +#endif + +#ifndef NAN +#define NAN (0.0 / 0.0) +#endif + +static unsigned long get_field (const unsigned char *, + enum floatformat_byteorders, + unsigned int, + unsigned int, + unsigned int); +static int floatformat_always_valid (const struct floatformat *fmt, + const char *from); + +static int +floatformat_always_valid (const struct floatformat *fmt ATTRIBUTE_UNUSED, + const char *from ATTRIBUTE_UNUSED) +{ + return 1; +} + +/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not + going to bother with trying to muck around with whether it is defined in + a system header, what we do if not, etc. */ +#define FLOATFORMAT_CHAR_BIT 8 + +/* floatformats for IEEE single and double, big and little endian. */ +const struct floatformat floatformat_ieee_single_big = +{ + floatformat_big, 32, 0, 1, 8, 127, 255, 9, 23, + floatformat_intbit_no, + "floatformat_ieee_single_big", + floatformat_always_valid +}; +const struct floatformat floatformat_ieee_single_little = +{ + floatformat_little, 32, 0, 1, 8, 127, 255, 9, 23, + floatformat_intbit_no, + "floatformat_ieee_single_little", + floatformat_always_valid +}; +const struct floatformat floatformat_ieee_double_big = +{ + floatformat_big, 64, 0, 1, 11, 1023, 2047, 12, 52, + floatformat_intbit_no, + "floatformat_ieee_double_big", + floatformat_always_valid +}; +const struct floatformat floatformat_ieee_double_little = +{ + floatformat_little, 64, 0, 1, 11, 1023, 2047, 12, 52, + floatformat_intbit_no, + "floatformat_ieee_double_little", + floatformat_always_valid +}; + +/* floatformat for IEEE double, little endian byte order, with big endian word + ordering, as on the ARM. */ + +const struct floatformat floatformat_ieee_double_littlebyte_bigword = +{ + floatformat_littlebyte_bigword, 64, 0, 1, 11, 1023, 2047, 12, 52, + floatformat_intbit_no, + "floatformat_ieee_double_littlebyte_bigword", + floatformat_always_valid +}; + +static int floatformat_i387_ext_is_valid (const struct floatformat *fmt, const char *from); + +static int +floatformat_i387_ext_is_valid (const struct floatformat *fmt, const char *from) +{ + /* In the i387 double-extended format, if the exponent is all ones, + then the integer bit must be set. If the exponent is neither 0 + nor ~0, the intbit must also be set. Only if the exponent is + zero can it be zero, and then it must be zero. */ + unsigned long exponent, int_bit; + const unsigned char *ufrom = (const unsigned char *) from; + + exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize, + fmt->exp_start, fmt->exp_len); + int_bit = get_field (ufrom, fmt->byteorder, fmt->totalsize, + fmt->man_start, 1); + + if ((exponent == 0) != (int_bit == 0)) + return 0; + else + return 1; +} + +const struct floatformat floatformat_i387_ext = +{ + floatformat_little, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64, + floatformat_intbit_yes, + "floatformat_i387_ext", + floatformat_i387_ext_is_valid +}; +const struct floatformat floatformat_m68881_ext = +{ + /* Note that the bits from 16 to 31 are unused. */ + floatformat_big, 96, 0, 1, 15, 0x3fff, 0x7fff, 32, 64, + floatformat_intbit_yes, + "floatformat_m68881_ext", + floatformat_always_valid +}; +const struct floatformat floatformat_i960_ext = +{ + /* Note that the bits from 0 to 15 are unused. */ + floatformat_little, 96, 16, 17, 15, 0x3fff, 0x7fff, 32, 64, + floatformat_intbit_yes, + "floatformat_i960_ext", + floatformat_always_valid +}; +const struct floatformat floatformat_m88110_ext = +{ + floatformat_big, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64, + floatformat_intbit_yes, + "floatformat_m88110_ext", + floatformat_always_valid +}; +const struct floatformat floatformat_m88110_harris_ext = +{ + /* Harris uses raw format 128 bytes long, but the number is just an ieee + double, and the last 64 bits are wasted. */ + floatformat_big,128, 0, 1, 11, 0x3ff, 0x7ff, 12, 52, + floatformat_intbit_no, + "floatformat_m88110_ext_harris", + floatformat_always_valid +}; +const struct floatformat floatformat_arm_ext_big = +{ + /* Bits 1 to 16 are unused. */ + floatformat_big, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64, + floatformat_intbit_yes, + "floatformat_arm_ext_big", + floatformat_always_valid +}; +const struct floatformat floatformat_arm_ext_littlebyte_bigword = +{ + /* Bits 1 to 16 are unused. */ + floatformat_littlebyte_bigword, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64, + floatformat_intbit_yes, + "floatformat_arm_ext_littlebyte_bigword", + floatformat_always_valid +}; +const struct floatformat floatformat_ia64_spill_big = +{ + floatformat_big, 128, 0, 1, 17, 65535, 0x1ffff, 18, 64, + floatformat_intbit_yes, + "floatformat_ia64_spill_big", + floatformat_always_valid +}; +const struct floatformat floatformat_ia64_spill_little = +{ + floatformat_little, 128, 0, 1, 17, 65535, 0x1ffff, 18, 64, + floatformat_intbit_yes, + "floatformat_ia64_spill_little", + floatformat_always_valid +}; +const struct floatformat floatformat_ia64_quad_big = +{ + floatformat_big, 128, 0, 1, 15, 16383, 0x7fff, 16, 112, + floatformat_intbit_no, + "floatformat_ia64_quad_big", + floatformat_always_valid +}; +const struct floatformat floatformat_ia64_quad_little = +{ + floatformat_little, 128, 0, 1, 15, 16383, 0x7fff, 16, 112, + floatformat_intbit_no, + "floatformat_ia64_quad_little", + floatformat_always_valid +}; + +/* Extract a field which starts at START and is LEN bits long. DATA and + TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */ +static unsigned long +get_field (const unsigned char *data, enum floatformat_byteorders order, + unsigned int total_len, unsigned int start, unsigned int len) +{ + unsigned long result; + unsigned int cur_byte; + int cur_bitshift; + + /* Start at the least significant part of the field. */ + cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1; + cur_bitshift = + ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT; + result = *(data + cur_byte) >> (-cur_bitshift); + cur_bitshift += FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + ++cur_byte; + else + --cur_byte; + + /* Move towards the most significant part of the field. */ + while ((unsigned int) cur_bitshift < len) + { + if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT) + /* This is the last byte; zero out the bits which are not part of + this field. */ + result |= + (*(data + cur_byte) & ((1 << (len - cur_bitshift)) - 1)) + << cur_bitshift; + else + result |= *(data + cur_byte) << cur_bitshift; + cur_bitshift += FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + ++cur_byte; + else + --cur_byte; + } + return result; +} + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/* Convert from FMT to a double. + FROM is the address of the extended float. + Store the double in *TO. */ + +void +floatformat_to_double (const struct floatformat *fmt, + const char *from, double *to) +{ + const unsigned char *ufrom = (const unsigned char *)from; + double dto; + long exponent; + unsigned long mant; + unsigned int mant_bits, mant_off; + int mant_bits_left; + int special_exponent; /* It's a NaN, denorm or zero */ + + exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize, + fmt->exp_start, fmt->exp_len); + + /* If the exponent indicates a NaN, we don't have information to + decide what to do. So we handle it like IEEE, except that we + don't try to preserve the type of NaN. FIXME. */ + if ((unsigned long) exponent == fmt->exp_nan) + { + int nan; + + mant_off = fmt->man_start; + mant_bits_left = fmt->man_len; + nan = 0; + while (mant_bits_left > 0) + { + mant_bits = min (mant_bits_left, 32); + + if (get_field (ufrom, fmt->byteorder, fmt->totalsize, + mant_off, mant_bits) != 0) + { + /* This is a NaN. */ + nan = 1; + break; + } + + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } + + /* On certain systems (such as GNU/Linux), the use of the + INFINITY macro below may generate a warning that can not be + silenced due to a bug in GCC (PR preprocessor/11931). The + preprocessor fails to recognise the __extension__ keyword in + conjunction with the GNU/C99 extension for hexadecimal + floating point constants and will issue a warning when + compiling with -pedantic. */ + if (nan) + dto = NAN; + else + dto = INFINITY; + + if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1)) + dto = -dto; + + *to = dto; + + return; + } + + mant_bits_left = fmt->man_len; + mant_off = fmt->man_start; + dto = 0.0; + + special_exponent = exponent == 0 || (unsigned long) exponent == fmt->exp_nan; + + /* Don't bias zero's, denorms or NaNs. */ + if (!special_exponent) + exponent -= fmt->exp_bias; + + /* Build the result algebraically. Might go infinite, underflow, etc; + who cares. */ + + /* If this format uses a hidden bit, explicitly add it in now. Otherwise, + increment the exponent by one to account for the integer bit. */ + + if (!special_exponent) + { + if (fmt->intbit == floatformat_intbit_no) + dto = ldexp (1.0, exponent); + else + exponent++; + } + + while (mant_bits_left > 0) + { + mant_bits = min (mant_bits_left, 32); + + mant = get_field (ufrom, fmt->byteorder, fmt->totalsize, + mant_off, mant_bits); + + /* Handle denormalized numbers. FIXME: What should we do for + non-IEEE formats? */ + if (exponent == 0 && mant != 0) + dto += ldexp ((double)mant, + (- fmt->exp_bias + - mant_bits + - (mant_off - fmt->man_start) + + 1)); + else + dto += ldexp ((double)mant, exponent - mant_bits); + if (exponent != 0) + exponent -= mant_bits; + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } + + /* Negate it if negative. */ + if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1)) + dto = -dto; + *to = dto; +} + +static void put_field (unsigned char *, enum floatformat_byteorders, + unsigned int, + unsigned int, + unsigned int, + unsigned long); + +/* Set a field which starts at START and is LEN bits long. DATA and + TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */ +static void +put_field (unsigned char *data, enum floatformat_byteorders order, + unsigned int total_len, unsigned int start, unsigned int len, + unsigned long stuff_to_put) +{ + unsigned int cur_byte; + int cur_bitshift; + + /* Start at the least significant part of the field. */ + cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1; + cur_bitshift = + ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT; + *(data + cur_byte) &= + ~(((1 << ((start + len) % FLOATFORMAT_CHAR_BIT)) - 1) << (-cur_bitshift)); + *(data + cur_byte) |= + (stuff_to_put & ((1 << FLOATFORMAT_CHAR_BIT) - 1)) << (-cur_bitshift); + cur_bitshift += FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + ++cur_byte; + else + --cur_byte; + + /* Move towards the most significant part of the field. */ + while ((unsigned int) cur_bitshift < len) + { + if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT) + { + /* This is the last byte. */ + *(data + cur_byte) &= + ~((1 << (len - cur_bitshift)) - 1); + *(data + cur_byte) |= (stuff_to_put >> cur_bitshift); + } + else + *(data + cur_byte) = ((stuff_to_put >> cur_bitshift) + & ((1 << FLOATFORMAT_CHAR_BIT) - 1)); + cur_bitshift += FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + ++cur_byte; + else + --cur_byte; + } +} + +/* The converse: convert the double *FROM to an extended float + and store where TO points. Neither FROM nor TO have any alignment + restrictions. */ + +void +floatformat_from_double (const struct floatformat *fmt, + const double *from, char *to) +{ + double dfrom; + int exponent; + double mant; + unsigned int mant_bits, mant_off; + int mant_bits_left; + unsigned char *uto = (unsigned char *)to; + + dfrom = *from; + memset (uto, 0, fmt->totalsize / FLOATFORMAT_CHAR_BIT); + + /* If negative, set the sign bit. */ + if (dfrom < 0) + { + put_field (uto, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1, 1); + dfrom = -dfrom; + } + + if (dfrom == 0) + { + /* 0.0. */ + return; + } + + if (dfrom != dfrom) + { + /* NaN. */ + put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + /* Be sure it's not infinity, but NaN value is irrelevant. */ + put_field (uto, fmt->byteorder, fmt->totalsize, fmt->man_start, + 32, 1); + return; + } + + if (dfrom + dfrom == dfrom) + { + /* This can only happen for an infinite value (or zero, which we + already handled above). */ + put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + return; + } + + mant = frexp (dfrom, &exponent); + if (exponent + fmt->exp_bias - 1 > 0) + put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start, + fmt->exp_len, exponent + fmt->exp_bias - 1); + else + { + /* Handle a denormalized number. FIXME: What should we do for + non-IEEE formats? */ + put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start, + fmt->exp_len, 0); + mant = ldexp (mant, exponent + fmt->exp_bias - 1); + } + + mant_bits_left = fmt->man_len; + mant_off = fmt->man_start; + while (mant_bits_left > 0) + { + unsigned long mant_long; + mant_bits = mant_bits_left < 32 ? mant_bits_left : 32; + + mant *= 4294967296.0; + mant_long = (unsigned long)mant; + mant -= mant_long; + + /* If the integer bit is implicit, and we are not creating a + denormalized number, then we need to discard it. */ + if ((unsigned int) mant_bits_left == fmt->man_len + && fmt->intbit == floatformat_intbit_no + && exponent + fmt->exp_bias - 1 > 0) + { + mant_long &= 0x7fffffff; + mant_bits -= 1; + } + else if (mant_bits < 32) + { + /* The bits we want are in the most significant MANT_BITS bits of + mant_long. Move them to the least significant. */ + mant_long >>= 32 - mant_bits; + } + + put_field (uto, fmt->byteorder, fmt->totalsize, + mant_off, mant_bits, mant_long); + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } +} + +/* Return non-zero iff the data at FROM is a valid number in format FMT. */ + +int +floatformat_is_valid (const struct floatformat *fmt, const char *from) +{ + return fmt->is_valid (fmt, from); +} + + +#ifdef IEEE_DEBUG + +/* This is to be run on a host which uses IEEE floating point. */ + +void +ieee_test (double n) +{ + double result; + + floatformat_to_double (&floatformat_ieee_double_little, (char *) &n, + &result); + if ((n != result && (! isnan (n) || ! isnan (result))) + || (n < 0 && result >= 0) + || (n >= 0 && result < 0)) + printf ("Differ(to): %.20g -> %.20g\n", n, result); + + floatformat_from_double (&floatformat_ieee_double_little, &n, + (char *) &result); + if ((n != result && (! isnan (n) || ! isnan (result))) + || (n < 0 && result >= 0) + || (n >= 0 && result < 0)) + printf ("Differ(from): %.20g -> %.20g\n", n, result); + +#if 0 + { + char exten[16]; + + floatformat_from_double (&floatformat_m68881_ext, &n, exten); + floatformat_to_double (&floatformat_m68881_ext, exten, &result); + if (n != result) + printf ("Differ(to+from): %.20g -> %.20g\n", n, result); + } +#endif + +#if IEEE_DEBUG > 1 + /* This is to be run on a host which uses 68881 format. */ + { + long double ex = *(long double *)exten; + if (ex != n) + printf ("Differ(from vs. extended): %.20g\n", n); + } +#endif +} + +int +main (void) +{ + ieee_test (0.0); + ieee_test (0.5); + ieee_test (256.0); + ieee_test (0.12345); + ieee_test (234235.78907234); + ieee_test (-512.0); + ieee_test (-0.004321); + ieee_test (1.2E-70); + ieee_test (1.2E-316); + ieee_test (4.9406564584124654E-324); + ieee_test (- 4.9406564584124654E-324); + ieee_test (- 0.0); + ieee_test (- INFINITY); + ieee_test (- NAN); + ieee_test (INFINITY); + ieee_test (NAN); + return 0; +} +#endif +/* **** End of floatformat.c */ diff --git a/disas/microblaze.c b/disas/microblaze.c new file mode 100644 index 0000000..c14ab89 --- /dev/null +++ b/disas/microblaze.c @@ -0,0 +1,1101 @@ +/* Disassemble Xilinx microblaze instructions. + Copyright (C) 1993, 1999, 2000 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . */ + +/* + * Copyright (c) 2001 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Xilinx, Inc. The name of the Company may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Xilinx, Inc. + */ + + +#include +#define STATIC_TABLE +#define DEFINE_TABLE + +#define TRUE 1 +#define FALSE 0 + +#ifndef MICROBLAZE_OPC +#define MICROBLAZE_OPC +/* Assembler instructions for Xilinx's microblaze processor + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . */ + +/* + * Copyright (c) 2001 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Xilinx, Inc. The name of the Company may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Xilinx, Inc. + */ + + +#ifndef MICROBLAZE_OPCM +#define MICROBLAZE_OPCM + +/* + * Copyright (c) 2001 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Xilinx, Inc. The name of the Company may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Xilinx, Inc. + * $Header: + */ + +enum microblaze_instr { + add, rsub, addc, rsubc, addk, rsubk, addkc, rsubkc, cmp, cmpu, + addi, rsubi, addic, rsubic, addik, rsubik, addikc, rsubikc, mul, mulh, mulhu, mulhsu, + idiv, idivu, bsll, bsra, bsrl, get, put, nget, nput, cget, cput, + ncget, ncput, muli, bslli, bsrai, bsrli, mului, or, and, xor, + andn, pcmpbf, pcmpbc, pcmpeq, pcmpne, sra, src, srl, sext8, sext16, wic, wdc, wdcclear, wdcflush, mts, mfs, br, brd, + brld, bra, brad, brald, microblaze_brk, beq, beqd, bne, bned, blt, + bltd, ble, bled, bgt, bgtd, bge, bged, ori, andi, xori, andni, + imm, rtsd, rtid, rtbd, rted, bri, brid, brlid, brai, braid, bralid, + brki, beqi, beqid, bnei, bneid, blti, bltid, blei, bleid, bgti, + bgtid, bgei, bgeid, lbu, lhu, lw, lwx, sb, sh, sw, swx, lbui, lhui, lwi, + sbi, shi, swi, msrset, msrclr, tuqula, fadd, frsub, fmul, fdiv, + fcmp_lt, fcmp_eq, fcmp_le, fcmp_gt, fcmp_ne, fcmp_ge, fcmp_un, flt, fint, fsqrt, + tget, tcget, tnget, tncget, tput, tcput, tnput, tncput, + eget, ecget, neget, necget, eput, ecput, neput, necput, + teget, tecget, tneget, tnecget, teput, tecput, tneput, tnecput, + aget, caget, naget, ncaget, aput, caput, naput, ncaput, + taget, tcaget, tnaget, tncaget, taput, tcaput, tnaput, tncaput, + eaget, ecaget, neaget, necaget, eaput, ecaput, neaput, necaput, + teaget, tecaget, tneaget, tnecaget, teaput, tecaput, tneaput, tnecaput, + getd, tgetd, cgetd, tcgetd, ngetd, tngetd, ncgetd, tncgetd, + putd, tputd, cputd, tcputd, nputd, tnputd, ncputd, tncputd, + egetd, tegetd, ecgetd, tecgetd, negetd, tnegetd, necgetd, tnecgetd, + eputd, teputd, ecputd, tecputd, neputd, tneputd, necputd, tnecputd, + agetd, tagetd, cagetd, tcagetd, nagetd, tnagetd, ncagetd, tncagetd, + aputd, taputd, caputd, tcaputd, naputd, tnaputd, ncaputd, tncaputd, + eagetd, teagetd, ecagetd, tecagetd, neagetd, tneagetd, necagetd, tnecagetd, + eaputd, teaputd, ecaputd, tecaputd, neaputd, tneaputd, necaputd, tnecaputd, + invalid_inst } ; + +enum microblaze_instr_type { + arithmetic_inst, logical_inst, mult_inst, div_inst, branch_inst, + return_inst, immediate_inst, special_inst, memory_load_inst, + memory_store_inst, barrel_shift_inst, anyware_inst }; + +#define INST_WORD_SIZE 4 + +/* gen purpose regs go from 0 to 31 */ +/* mask is reg num - max_reg_num, ie reg_num - 32 in this case */ + +#define REG_PC_MASK 0x8000 +#define REG_MSR_MASK 0x8001 +#define REG_EAR_MASK 0x8003 +#define REG_ESR_MASK 0x8005 +#define REG_FSR_MASK 0x8007 +#define REG_BTR_MASK 0x800b +#define REG_EDR_MASK 0x800d +#define REG_PVR_MASK 0xa000 + +#define REG_PID_MASK 0x9000 +#define REG_ZPR_MASK 0x9001 +#define REG_TLBX_MASK 0x9002 +#define REG_TLBLO_MASK 0x9003 +#define REG_TLBHI_MASK 0x9004 +#define REG_TLBSX_MASK 0x9005 + +#define MIN_REGNUM 0 +#define MAX_REGNUM 31 + +#define MIN_PVR_REGNUM 0 +#define MAX_PVR_REGNUM 15 + +#define REG_PC 32 /* PC */ +#define REG_MSR 33 /* machine status reg */ +#define REG_EAR 35 /* Exception reg */ +#define REG_ESR 37 /* Exception reg */ +#define REG_FSR 39 /* FPU Status reg */ +#define REG_BTR 43 /* Branch Target reg */ +#define REG_EDR 45 /* Exception reg */ +#define REG_PVR 40960 /* Program Verification reg */ + +#define REG_PID 36864 /* MMU: Process ID reg */ +#define REG_ZPR 36865 /* MMU: Zone Protect reg */ +#define REG_TLBX 36866 /* MMU: TLB Index reg */ +#define REG_TLBLO 36867 /* MMU: TLB Low reg */ +#define REG_TLBHI 36868 /* MMU: TLB High reg */ +#define REG_TLBSX 36869 /* MMU: TLB Search Index reg */ + +/* alternate names for gen purpose regs */ +#define REG_SP 1 /* stack pointer */ +#define REG_ROSDP 2 /* read-only small data pointer */ +#define REG_RWSDP 13 /* read-write small data pointer */ + +/* Assembler Register - Used in Delay Slot Optimization */ +#define REG_AS 18 +#define REG_ZERO 0 + +#define RD_LOW 21 /* low bit for RD */ +#define RA_LOW 16 /* low bit for RA */ +#define RB_LOW 11 /* low bit for RB */ +#define IMM_LOW 0 /* low bit for immediate */ + +#define RD_MASK 0x03E00000 +#define RA_MASK 0x001F0000 +#define RB_MASK 0x0000F800 +#define IMM_MASK 0x0000FFFF + +// imm mask for barrel shifts +#define IMM5_MASK 0x0000001F + + +// FSL imm mask for get, put instructions +#define RFSL_MASK 0x000000F + +// imm mask for msrset, msrclr instructions +#define IMM15_MASK 0x00007FFF + +#endif /* MICROBLAZE-OPCM */ + +#define INST_TYPE_RD_R1_R2 0 +#define INST_TYPE_RD_R1_IMM 1 +#define INST_TYPE_RD_R1_UNSIGNED_IMM 2 +#define INST_TYPE_RD_R1 3 +#define INST_TYPE_RD_R2 4 +#define INST_TYPE_RD_IMM 5 +#define INST_TYPE_R2 6 +#define INST_TYPE_R1_R2 7 +#define INST_TYPE_R1_IMM 8 +#define INST_TYPE_IMM 9 +#define INST_TYPE_SPECIAL_R1 10 +#define INST_TYPE_RD_SPECIAL 11 +#define INST_TYPE_R1 12 + // new instn type for barrel shift imms +#define INST_TYPE_RD_R1_IMM5 13 +#define INST_TYPE_RD_RFSL 14 +#define INST_TYPE_R1_RFSL 15 + + // new insn type for insn cache +#define INST_TYPE_RD_R1_SPECIAL 16 + +// new insn type for msrclr, msrset insns. +#define INST_TYPE_RD_IMM15 17 + +// new insn type for tuqula rd - addik rd, r0, 42 +#define INST_TYPE_RD 18 + +// new insn type for t*put +#define INST_TYPE_RFSL 19 + +#define INST_TYPE_NONE 25 + + + +#define INST_PC_OFFSET 1 /* instructions where the label address is resolved as a PC offset (for branch label)*/ +#define INST_NO_OFFSET 0 /* instructions where the label address is resolved as an absolute value (for data mem or abs address)*/ + +#define IMMVAL_MASK_NON_SPECIAL 0x0000 +#define IMMVAL_MASK_MTS 0x4000 +#define IMMVAL_MASK_MFS 0x0000 + +#define OPCODE_MASK_H 0xFC000000 /* High 6 bits only */ +#define OPCODE_MASK_H1 0xFFE00000 /* High 11 bits */ +#define OPCODE_MASK_H2 0xFC1F0000 /* High 6 and bits 20-16 */ +#define OPCODE_MASK_H12 0xFFFF0000 /* High 16 */ +#define OPCODE_MASK_H4 0xFC0007FF /* High 6 and low 11 bits */ +#define OPCODE_MASK_H13S 0xFFE0EFF0 /* High 11 and 15:1 bits and last nibble of last byte for spr */ +#define OPCODE_MASK_H23S 0xFC1FC000 /* High 6, 20-16 and 15:1 bits and last nibble of last byte for spr */ +#define OPCODE_MASK_H34 0xFC00FFFF /* High 6 and low 16 bits */ +#define OPCODE_MASK_H14 0xFFE007FF /* High 11 and low 11 bits */ +#define OPCODE_MASK_H24 0xFC1F07FF /* High 6, bits 20-16 and low 11 bits */ +#define OPCODE_MASK_H124 0xFFFF07FF /* High 16, and low 11 bits */ +#define OPCODE_MASK_H1234 0xFFFFFFFF /* All 32 bits */ +#define OPCODE_MASK_H3 0xFC000600 /* High 6 bits and bits 21, 22 */ +#define OPCODE_MASK_H32 0xFC00FC00 /* High 6 bits and bit 16-21 */ +#define OPCODE_MASK_H34B 0xFC0000FF /* High 6 bits and low 8 bits */ +#define OPCODE_MASK_H34C 0xFC0007E0 /* High 6 bits and bits 21-26 */ + +// New Mask for msrset, msrclr insns. +#define OPCODE_MASK_H23N 0xFC1F8000 /* High 6 and bits 11 - 16 */ + +#define DELAY_SLOT 1 +#define NO_DELAY_SLOT 0 + +#define MAX_OPCODES 280 + +static struct op_code_struct { + const char *name; + short inst_type; /* registers and immediate values involved */ + short inst_offset_type; /* immediate vals offset from PC? (= 1 for branches) */ + short delay_slots; /* info about delay slots needed after this instr. */ + short immval_mask; + unsigned long bit_sequence; /* all the fixed bits for the op are set and all the variable bits (reg names, imm vals) are set to 0 */ + unsigned long opcode_mask; /* which bits define the opcode */ + enum microblaze_instr instr; + enum microblaze_instr_type instr_type; + /* more info about output format here */ +} opcodes[MAX_OPCODES] = + +{ + {"add", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x00000000, OPCODE_MASK_H4, add, arithmetic_inst }, + {"rsub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H4, rsub, arithmetic_inst }, + {"addc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x08000000, OPCODE_MASK_H4, addc, arithmetic_inst }, + {"rsubc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x0C000000, OPCODE_MASK_H4, rsubc, arithmetic_inst }, + {"addk", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x10000000, OPCODE_MASK_H4, addk, arithmetic_inst }, + {"rsubk", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000000, OPCODE_MASK_H4, rsubk, arithmetic_inst }, + {"cmp", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000001, OPCODE_MASK_H4, cmp, arithmetic_inst }, + {"cmpu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000003, OPCODE_MASK_H4, cmpu, arithmetic_inst }, + {"addkc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x18000000, OPCODE_MASK_H4, addkc, arithmetic_inst }, + {"rsubkc",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x1C000000, OPCODE_MASK_H4, rsubkc, arithmetic_inst }, + {"addi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x20000000, OPCODE_MASK_H, addi, arithmetic_inst }, + {"rsubi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x24000000, OPCODE_MASK_H, rsubi, arithmetic_inst }, + {"addic", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x28000000, OPCODE_MASK_H, addic, arithmetic_inst }, + {"rsubic",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x2C000000, OPCODE_MASK_H, rsubic, arithmetic_inst }, + {"addik", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x30000000, OPCODE_MASK_H, addik, arithmetic_inst }, + {"rsubik",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x34000000, OPCODE_MASK_H, rsubik, arithmetic_inst }, + {"addikc",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x38000000, OPCODE_MASK_H, addikc, arithmetic_inst }, + {"rsubikc",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x3C000000, OPCODE_MASK_H, rsubikc, arithmetic_inst }, + {"mul", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000000, OPCODE_MASK_H4, mul, mult_inst }, + {"mulh", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000001, OPCODE_MASK_H4, mulh, mult_inst }, + {"mulhu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000003, OPCODE_MASK_H4, mulhu, mult_inst }, + {"mulhsu",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000002, OPCODE_MASK_H4, mulhsu, mult_inst }, + {"idiv", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x48000000, OPCODE_MASK_H4, idiv, div_inst }, + {"idivu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x48000002, OPCODE_MASK_H4, idivu, div_inst }, + {"bsll", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000400, OPCODE_MASK_H3, bsll, barrel_shift_inst }, + {"bsra", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000200, OPCODE_MASK_H3, bsra, barrel_shift_inst }, + {"bsrl", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000000, OPCODE_MASK_H3, bsrl, barrel_shift_inst }, + {"get", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000000, OPCODE_MASK_H32, get, anyware_inst }, + {"put", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008000, OPCODE_MASK_H32, put, anyware_inst }, + {"nget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004000, OPCODE_MASK_H32, nget, anyware_inst }, + {"nput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C000, OPCODE_MASK_H32, nput, anyware_inst }, + {"cget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002000, OPCODE_MASK_H32, cget, anyware_inst }, + {"cput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A000, OPCODE_MASK_H32, cput, anyware_inst }, + {"ncget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006000, OPCODE_MASK_H32, ncget, anyware_inst }, + {"ncput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E000, OPCODE_MASK_H32, ncput, anyware_inst }, + {"muli", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x60000000, OPCODE_MASK_H, muli, mult_inst }, + {"bslli", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000400, OPCODE_MASK_H3, bslli, barrel_shift_inst }, + {"bsrai", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000200, OPCODE_MASK_H3, bsrai, barrel_shift_inst }, + {"bsrli", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000000, OPCODE_MASK_H3, bsrli, barrel_shift_inst }, + {"or", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000000, OPCODE_MASK_H4, or, logical_inst }, + {"and", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x84000000, OPCODE_MASK_H4, and, logical_inst }, + {"xor", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x88000000, OPCODE_MASK_H4, xor, logical_inst }, + {"andn", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x8C000000, OPCODE_MASK_H4, andn, logical_inst }, + {"pcmpbf",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000400, OPCODE_MASK_H4, pcmpbf, logical_inst }, + {"pcmpbc",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x84000400, OPCODE_MASK_H4, pcmpbc, logical_inst }, + {"pcmpeq",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x88000400, OPCODE_MASK_H4, pcmpeq, logical_inst }, + {"pcmpne",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x8C000400, OPCODE_MASK_H4, pcmpne, logical_inst }, + {"sra", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000001, OPCODE_MASK_H34, sra, logical_inst }, + {"src", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000021, OPCODE_MASK_H34, src, logical_inst }, + {"srl", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000041, OPCODE_MASK_H34, srl, logical_inst }, + {"sext8", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000060, OPCODE_MASK_H34, sext8, logical_inst }, + {"sext16",INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000061, OPCODE_MASK_H34, sext16, logical_inst }, + {"wic", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000068, OPCODE_MASK_H34B, wic, special_inst }, + {"wdc", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000064, OPCODE_MASK_H34B, wdc, special_inst }, + {"wdc.clear", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000066, OPCODE_MASK_H34B, wdcclear, special_inst }, + {"wdc.flush", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000074, OPCODE_MASK_H34B, wdcflush, special_inst }, + {"mts", INST_TYPE_SPECIAL_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MTS, 0x9400C000, OPCODE_MASK_H13S, mts, special_inst }, + {"mfs", INST_TYPE_RD_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MFS, 0x94008000, OPCODE_MASK_H23S, mfs, special_inst }, + {"br", INST_TYPE_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98000000, OPCODE_MASK_H124, br, branch_inst }, + {"brd", INST_TYPE_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98100000, OPCODE_MASK_H124, brd, branch_inst }, + {"brld", INST_TYPE_RD_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98140000, OPCODE_MASK_H24, brld, branch_inst }, + {"bra", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98080000, OPCODE_MASK_H124, bra, branch_inst }, + {"brad", INST_TYPE_R2, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98180000, OPCODE_MASK_H124, brad, branch_inst }, + {"brald", INST_TYPE_RD_R2, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x981C0000, OPCODE_MASK_H24, brald, branch_inst }, + {"brk", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x980C0000, OPCODE_MASK_H24, microblaze_brk, branch_inst }, + {"beq", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C000000, OPCODE_MASK_H14, beq, branch_inst }, + {"beqd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E000000, OPCODE_MASK_H14, beqd, branch_inst }, + {"bne", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C200000, OPCODE_MASK_H14, bne, branch_inst }, + {"bned", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E200000, OPCODE_MASK_H14, bned, branch_inst }, + {"blt", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C400000, OPCODE_MASK_H14, blt, branch_inst }, + {"bltd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E400000, OPCODE_MASK_H14, bltd, branch_inst }, + {"ble", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C600000, OPCODE_MASK_H14, ble, branch_inst }, + {"bled", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E600000, OPCODE_MASK_H14, bled, branch_inst }, + {"bgt", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C800000, OPCODE_MASK_H14, bgt, branch_inst }, + {"bgtd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E800000, OPCODE_MASK_H14, bgtd, branch_inst }, + {"bge", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9CA00000, OPCODE_MASK_H14, bge, branch_inst }, + {"bged", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9EA00000, OPCODE_MASK_H14, bged, branch_inst }, + {"ori", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA0000000, OPCODE_MASK_H, ori, logical_inst }, + {"andi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA4000000, OPCODE_MASK_H, andi, logical_inst }, + {"xori", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA8000000, OPCODE_MASK_H, xori, logical_inst }, + {"andni", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xAC000000, OPCODE_MASK_H, andni, logical_inst }, + {"imm", INST_TYPE_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB0000000, OPCODE_MASK_H12, imm, immediate_inst }, + {"rtsd", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6000000, OPCODE_MASK_H1, rtsd, return_inst }, + {"rtid", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6200000, OPCODE_MASK_H1, rtid, return_inst }, + {"rtbd", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6400000, OPCODE_MASK_H1, rtbd, return_inst }, + {"rted", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6800000, OPCODE_MASK_H1, rted, return_inst }, + {"bri", INST_TYPE_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8000000, OPCODE_MASK_H12, bri, branch_inst }, + {"brid", INST_TYPE_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8100000, OPCODE_MASK_H12, brid, branch_inst }, + {"brlid", INST_TYPE_RD_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8140000, OPCODE_MASK_H2, brlid, branch_inst }, + {"brai", INST_TYPE_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8080000, OPCODE_MASK_H12, brai, branch_inst }, + {"braid", INST_TYPE_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8180000, OPCODE_MASK_H12, braid, branch_inst }, + {"bralid",INST_TYPE_RD_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB81C0000, OPCODE_MASK_H2, bralid, branch_inst }, + {"brki", INST_TYPE_RD_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB80C0000, OPCODE_MASK_H2, brki, branch_inst }, + {"beqi", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC000000, OPCODE_MASK_H1, beqi, branch_inst }, + {"beqid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE000000, OPCODE_MASK_H1, beqid, branch_inst }, + {"bnei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC200000, OPCODE_MASK_H1, bnei, branch_inst }, + {"bneid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE200000, OPCODE_MASK_H1, bneid, branch_inst }, + {"blti", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC400000, OPCODE_MASK_H1, blti, branch_inst }, + {"bltid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE400000, OPCODE_MASK_H1, bltid, branch_inst }, + {"blei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC600000, OPCODE_MASK_H1, blei, branch_inst }, + {"bleid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE600000, OPCODE_MASK_H1, bleid, branch_inst }, + {"bgti", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC800000, OPCODE_MASK_H1, bgti, branch_inst }, + {"bgtid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE800000, OPCODE_MASK_H1, bgtid, branch_inst }, + {"bgei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBCA00000, OPCODE_MASK_H1, bgei, branch_inst }, + {"bgeid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBEA00000, OPCODE_MASK_H1, bgeid, branch_inst }, + {"lbu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC0000000, OPCODE_MASK_H4, lbu, memory_load_inst }, + {"lhu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC4000000, OPCODE_MASK_H4, lhu, memory_load_inst }, + {"lw", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC8000000, OPCODE_MASK_H4, lw, memory_load_inst }, + {"lwx", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC8000400, OPCODE_MASK_H4, lwx, memory_load_inst }, + {"sb", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD0000000, OPCODE_MASK_H4, sb, memory_store_inst }, + {"sh", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD4000000, OPCODE_MASK_H4, sh, memory_store_inst }, + {"sw", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD8000000, OPCODE_MASK_H4, sw, memory_store_inst }, + {"swx", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD8000400, OPCODE_MASK_H4, swx, memory_store_inst }, + {"lbui", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE0000000, OPCODE_MASK_H, lbui, memory_load_inst }, + {"lhui", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE4000000, OPCODE_MASK_H, lhui, memory_load_inst }, + {"lwi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE8000000, OPCODE_MASK_H, lwi, memory_load_inst }, + {"sbi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF0000000, OPCODE_MASK_H, sbi, memory_store_inst }, + {"shi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF4000000, OPCODE_MASK_H, shi, memory_store_inst }, + {"swi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF8000000, OPCODE_MASK_H, swi, memory_store_inst }, + {"nop", INST_TYPE_NONE, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000000, OPCODE_MASK_H1234, invalid_inst, logical_inst }, /* translates to or r0, r0, r0 */ + {"la", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x30000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* la translates to addik */ + {"tuqula",INST_TYPE_RD, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x3000002A, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* tuqula rd translates to addik rd, r0, 42 */ + {"not", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA800FFFF, OPCODE_MASK_H34, invalid_inst, logical_inst }, /* not translates to xori rd,ra,-1 */ + {"neg", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* neg translates to rsub rd, ra, r0 */ + {"rtb", INST_TYPE_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6000004, OPCODE_MASK_H1, invalid_inst, return_inst }, /* rtb translates to rts rd, 4 */ + {"sub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* sub translates to rsub rd, rb, ra */ + {"lmi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE8000000, OPCODE_MASK_H, invalid_inst, memory_load_inst }, + {"smi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF8000000, OPCODE_MASK_H, invalid_inst, memory_store_inst }, + {"msrset",INST_TYPE_RD_IMM15, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94100000, OPCODE_MASK_H23N, msrset, special_inst }, + {"msrclr",INST_TYPE_RD_IMM15, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94110000, OPCODE_MASK_H23N, msrclr, special_inst }, + {"fadd", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000000, OPCODE_MASK_H4, fadd, arithmetic_inst }, + {"frsub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000080, OPCODE_MASK_H4, frsub, arithmetic_inst }, + {"fmul", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000100, OPCODE_MASK_H4, fmul, arithmetic_inst }, + {"fdiv", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000180, OPCODE_MASK_H4, fdiv, arithmetic_inst }, + {"fcmp.lt", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000210, OPCODE_MASK_H4, fcmp_lt, arithmetic_inst }, + {"fcmp.eq", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000220, OPCODE_MASK_H4, fcmp_eq, arithmetic_inst }, + {"fcmp.le", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000230, OPCODE_MASK_H4, fcmp_le, arithmetic_inst }, + {"fcmp.gt", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000240, OPCODE_MASK_H4, fcmp_gt, arithmetic_inst }, + {"fcmp.ne", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000250, OPCODE_MASK_H4, fcmp_ne, arithmetic_inst }, + {"fcmp.ge", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000260, OPCODE_MASK_H4, fcmp_ge, arithmetic_inst }, + {"fcmp.un", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000200, OPCODE_MASK_H4, fcmp_un, arithmetic_inst }, + {"flt", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000280, OPCODE_MASK_H4, flt, arithmetic_inst }, + {"fint", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000300, OPCODE_MASK_H4, fint, arithmetic_inst }, + {"fsqrt", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000380, OPCODE_MASK_H4, fsqrt, arithmetic_inst }, + {"tget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001000, OPCODE_MASK_H32, tget, anyware_inst }, + {"tcget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003000, OPCODE_MASK_H32, tcget, anyware_inst }, + {"tnget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005000, OPCODE_MASK_H32, tnget, anyware_inst }, + {"tncget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007000, OPCODE_MASK_H32, tncget, anyware_inst }, + {"tput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009000, OPCODE_MASK_H32, tput, anyware_inst }, + {"tcput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B000, OPCODE_MASK_H32, tcput, anyware_inst }, + {"tnput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D000, OPCODE_MASK_H32, tnput, anyware_inst }, + {"tncput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F000, OPCODE_MASK_H32, tncput, anyware_inst }, + + {"eget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000400, OPCODE_MASK_H32, eget, anyware_inst }, + {"ecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002400, OPCODE_MASK_H32, ecget, anyware_inst }, + {"neget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004400, OPCODE_MASK_H32, neget, anyware_inst }, + {"necget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006400, OPCODE_MASK_H32, necget, anyware_inst }, + {"eput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008400, OPCODE_MASK_H32, eput, anyware_inst }, + {"ecput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A400, OPCODE_MASK_H32, ecput, anyware_inst }, + {"neput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C400, OPCODE_MASK_H32, neput, anyware_inst }, + {"necput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E400, OPCODE_MASK_H32, necput, anyware_inst }, + + {"teget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001400, OPCODE_MASK_H32, teget, anyware_inst }, + {"tecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003400, OPCODE_MASK_H32, tecget, anyware_inst }, + {"tneget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005400, OPCODE_MASK_H32, tneget, anyware_inst }, + {"tnecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007400, OPCODE_MASK_H32, tnecget, anyware_inst }, + {"teput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009400, OPCODE_MASK_H32, teput, anyware_inst }, + {"tecput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B400, OPCODE_MASK_H32, tecput, anyware_inst }, + {"tneput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D400, OPCODE_MASK_H32, tneput, anyware_inst }, + {"tnecput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F400, OPCODE_MASK_H32, tnecput, anyware_inst }, + + {"aget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000800, OPCODE_MASK_H32, aget, anyware_inst }, + {"caget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002800, OPCODE_MASK_H32, caget, anyware_inst }, + {"naget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004800, OPCODE_MASK_H32, naget, anyware_inst }, + {"ncaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006800, OPCODE_MASK_H32, ncaget, anyware_inst }, + {"aput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008800, OPCODE_MASK_H32, aput, anyware_inst }, + {"caput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A800, OPCODE_MASK_H32, caput, anyware_inst }, + {"naput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C800, OPCODE_MASK_H32, naput, anyware_inst }, + {"ncaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E800, OPCODE_MASK_H32, ncaput, anyware_inst }, + + {"taget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001800, OPCODE_MASK_H32, taget, anyware_inst }, + {"tcaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003800, OPCODE_MASK_H32, tcaget, anyware_inst }, + {"tnaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005800, OPCODE_MASK_H32, tnaget, anyware_inst }, + {"tncaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007800, OPCODE_MASK_H32, tncaget, anyware_inst }, + {"taput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009800, OPCODE_MASK_H32, taput, anyware_inst }, + {"tcaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B800, OPCODE_MASK_H32, tcaput, anyware_inst }, + {"tnaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D800, OPCODE_MASK_H32, tnaput, anyware_inst }, + {"tncaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F800, OPCODE_MASK_H32, tncaput, anyware_inst }, + + {"eaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000C00, OPCODE_MASK_H32, eget, anyware_inst }, + {"ecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002C00, OPCODE_MASK_H32, ecget, anyware_inst }, + {"neaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004C00, OPCODE_MASK_H32, neget, anyware_inst }, + {"necaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006C00, OPCODE_MASK_H32, necget, anyware_inst }, + {"eaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008C00, OPCODE_MASK_H32, eput, anyware_inst }, + {"ecaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00AC00, OPCODE_MASK_H32, ecput, anyware_inst }, + {"neaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00CC00, OPCODE_MASK_H32, neput, anyware_inst }, + {"necaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00EC00, OPCODE_MASK_H32, necput, anyware_inst }, + + {"teaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001C00, OPCODE_MASK_H32, teaget, anyware_inst }, + {"tecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003C00, OPCODE_MASK_H32, tecaget, anyware_inst }, + {"tneaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005C00, OPCODE_MASK_H32, tneaget, anyware_inst }, + {"tnecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007C00, OPCODE_MASK_H32, tnecaget, anyware_inst }, + {"teaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009C00, OPCODE_MASK_H32, teaput, anyware_inst }, + {"tecaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00BC00, OPCODE_MASK_H32, tecaput, anyware_inst }, + {"tneaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00DC00, OPCODE_MASK_H32, tneaput, anyware_inst }, + {"tnecaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00FC00, OPCODE_MASK_H32, tnecaput, anyware_inst }, + + {"getd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000000, OPCODE_MASK_H34C, getd, anyware_inst }, + {"tgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000080, OPCODE_MASK_H34C, tgetd, anyware_inst }, + {"cgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000100, OPCODE_MASK_H34C, cgetd, anyware_inst }, + {"tcgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000180, OPCODE_MASK_H34C, tcgetd, anyware_inst }, + {"ngetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000200, OPCODE_MASK_H34C, ngetd, anyware_inst }, + {"tngetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000280, OPCODE_MASK_H34C, tngetd, anyware_inst }, + {"ncgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000300, OPCODE_MASK_H34C, ncgetd, anyware_inst }, + {"tncgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000380, OPCODE_MASK_H34C, tncgetd, anyware_inst }, + {"putd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000400, OPCODE_MASK_H34C, putd, anyware_inst }, + {"tputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000480, OPCODE_MASK_H34C, tputd, anyware_inst }, + {"cputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000500, OPCODE_MASK_H34C, cputd, anyware_inst }, + {"tcputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000580, OPCODE_MASK_H34C, tcputd, anyware_inst }, + {"nputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000600, OPCODE_MASK_H34C, nputd, anyware_inst }, + {"tnputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000680, OPCODE_MASK_H34C, tnputd, anyware_inst }, + {"ncputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000700, OPCODE_MASK_H34C, ncputd, anyware_inst }, + {"tncputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000780, OPCODE_MASK_H34C, tncputd, anyware_inst }, + + {"egetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000020, OPCODE_MASK_H34C, egetd, anyware_inst }, + {"tegetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000A0, OPCODE_MASK_H34C, tegetd, anyware_inst }, + {"ecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000120, OPCODE_MASK_H34C, ecgetd, anyware_inst }, + {"tecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001A0, OPCODE_MASK_H34C, tecgetd, anyware_inst }, + {"negetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000220, OPCODE_MASK_H34C, negetd, anyware_inst }, + {"tnegetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002A0, OPCODE_MASK_H34C, tnegetd, anyware_inst }, + {"necgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000320, OPCODE_MASK_H34C, necgetd, anyware_inst }, + {"tnecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003A0, OPCODE_MASK_H34C, tnecgetd, anyware_inst }, + {"eputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000420, OPCODE_MASK_H34C, eputd, anyware_inst }, + {"teputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004A0, OPCODE_MASK_H34C, teputd, anyware_inst }, + {"ecputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000520, OPCODE_MASK_H34C, ecputd, anyware_inst }, + {"tecputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005A0, OPCODE_MASK_H34C, tecputd, anyware_inst }, + {"neputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000620, OPCODE_MASK_H34C, neputd, anyware_inst }, + {"tneputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006A0, OPCODE_MASK_H34C, tneputd, anyware_inst }, + {"necputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000720, OPCODE_MASK_H34C, necputd, anyware_inst }, + {"tnecputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007A0, OPCODE_MASK_H34C, tnecputd, anyware_inst }, + + {"agetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000040, OPCODE_MASK_H34C, agetd, anyware_inst }, + {"tagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000C0, OPCODE_MASK_H34C, tagetd, anyware_inst }, + {"cagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000140, OPCODE_MASK_H34C, cagetd, anyware_inst }, + {"tcagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001C0, OPCODE_MASK_H34C, tcagetd, anyware_inst }, + {"nagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000240, OPCODE_MASK_H34C, nagetd, anyware_inst }, + {"tnagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002C0, OPCODE_MASK_H34C, tnagetd, anyware_inst }, + {"ncagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000340, OPCODE_MASK_H34C, ncagetd, anyware_inst }, + {"tncagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003C0, OPCODE_MASK_H34C, tncagetd, anyware_inst }, + {"aputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000440, OPCODE_MASK_H34C, aputd, anyware_inst }, + {"taputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004C0, OPCODE_MASK_H34C, taputd, anyware_inst }, + {"caputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000540, OPCODE_MASK_H34C, caputd, anyware_inst }, + {"tcaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005C0, OPCODE_MASK_H34C, tcaputd, anyware_inst }, + {"naputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000640, OPCODE_MASK_H34C, naputd, anyware_inst }, + {"tnaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006C0, OPCODE_MASK_H34C, tnaputd, anyware_inst }, + {"ncaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000740, OPCODE_MASK_H34C, ncaputd, anyware_inst }, + {"tncaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007C0, OPCODE_MASK_H34C, tncaputd, anyware_inst }, + + {"eagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000060, OPCODE_MASK_H34C, eagetd, anyware_inst }, + {"teagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000E0, OPCODE_MASK_H34C, teagetd, anyware_inst }, + {"ecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000160, OPCODE_MASK_H34C, ecagetd, anyware_inst }, + {"tecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001E0, OPCODE_MASK_H34C, tecagetd, anyware_inst }, + {"neagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000260, OPCODE_MASK_H34C, neagetd, anyware_inst }, + {"tneagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002E0, OPCODE_MASK_H34C, tneagetd, anyware_inst }, + {"necagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000360, OPCODE_MASK_H34C, necagetd, anyware_inst }, + {"tnecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003E0, OPCODE_MASK_H34C, tnecagetd, anyware_inst }, + {"eaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000460, OPCODE_MASK_H34C, eaputd, anyware_inst }, + {"teaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004E0, OPCODE_MASK_H34C, teaputd, anyware_inst }, + {"ecaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000560, OPCODE_MASK_H34C, ecaputd, anyware_inst }, + {"tecaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005E0, OPCODE_MASK_H34C, tecaputd, anyware_inst }, + {"neaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000660, OPCODE_MASK_H34C, neaputd, anyware_inst }, + {"tneaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006E0, OPCODE_MASK_H34C, tneaputd, anyware_inst }, + {"necaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000760, OPCODE_MASK_H34C, necaputd, anyware_inst }, + {"tnecaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007E0, OPCODE_MASK_H34C, tnecaputd, anyware_inst }, + {"", 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +/* prefix for register names */ +static const char register_prefix[] = "r"; +static const char fsl_register_prefix[] = "rfsl"; +static const char pvr_register_prefix[] = "rpvr"; + + +/* #defines for valid immediate range */ +#define MIN_IMM ((int) 0x80000000) +#define MAX_IMM ((int) 0x7fffffff) + +#define MIN_IMM15 ((int) 0x0000) +#define MAX_IMM15 ((int) 0x7fff) + +#endif /* MICROBLAZE_OPC */ + +#include "disas/bfd.h" +#include + +#define get_field_rd(instr) get_field(instr, RD_MASK, RD_LOW) +#define get_field_r1(instr) get_field(instr, RA_MASK, RA_LOW) +#define get_field_r2(instr) get_field(instr, RB_MASK, RB_LOW) +#define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) +#define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) + +/* Local function prototypes. */ + +static char * get_field (long instr, long mask, unsigned short low); +static char * get_field_imm (long instr); +static char * get_field_imm5 (long instr); +static char * get_field_rfsl (long instr); +static char * get_field_imm15 (long instr); +#if 0 +static char * get_field_unsigned_imm (long instr); +#endif +char * get_field_special (long instr, struct op_code_struct * op); +unsigned long read_insn_microblaze (bfd_vma memaddr, + struct disassemble_info *info, + struct op_code_struct **opr); +enum microblaze_instr get_insn_microblaze (long inst, + bfd_boolean *isunsignedimm, + enum microblaze_instr_type *insn_type, + short *delay_slots); +short get_delay_slots_microblaze (long inst); +enum microblaze_instr microblaze_decode_insn (long insn, + int *rd, + int *ra, + int *rb, + int *imm); +unsigned long +microblaze_get_target_address (long inst, + bfd_boolean immfound, + int immval, + long pcval, + long r1val, + long r2val, + bfd_boolean *targetvalid, + bfd_boolean *unconditionalbranch); + +static char * +get_field (long instr, long mask, unsigned short low) +{ + char tmpstr[25]; + sprintf(tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low)); + return(strdup(tmpstr)); +} + +static char * +get_field_imm (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} + +static char * +get_field_imm5 (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} + +static char * +get_field_rfsl (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%s%d", fsl_register_prefix, (short)((instr & RFSL_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} + +static char * +get_field_imm15 (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} + +#if 0 +static char * +get_field_unsigned_imm (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%d", (int)((instr & IMM_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} +#endif + +/* + char * + get_field_special (instr) + long instr; + { + char tmpstr[25]; + + sprintf(tmpstr, "%s%s", register_prefix, (((instr & IMM_MASK) >> IMM_LOW) & REG_MSR_MASK) == 0 ? "pc" : "msr"); + + return(strdup(tmpstr)); + } +*/ + +char * +get_field_special (long instr, struct op_code_struct * op) +{ + char tmpstr[25]; + char spr[6]; + + switch ( (((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ) { + + case REG_MSR_MASK : + strcpy(spr, "msr"); + break; + case REG_PC_MASK : + strcpy(spr, "pc"); + break; + case REG_EAR_MASK : + strcpy(spr, "ear"); + break; + case REG_ESR_MASK : + strcpy(spr, "esr"); + break; + case REG_FSR_MASK : + strcpy(spr, "fsr"); + break; + case REG_BTR_MASK : + strcpy(spr, "btr"); + break; + case REG_EDR_MASK : + strcpy(spr, "edr"); + break; + case REG_PID_MASK : + strcpy(spr, "pid"); + break; + case REG_ZPR_MASK : + strcpy(spr, "zpr"); + break; + case REG_TLBX_MASK : + strcpy(spr, "tlbx"); + break; + case REG_TLBLO_MASK : + strcpy(spr, "tlblo"); + break; + case REG_TLBHI_MASK : + strcpy(spr, "tlbhi"); + break; + case REG_TLBSX_MASK : + strcpy(spr, "tlbsx"); + break; + default : + { + if ( ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) == REG_PVR_MASK) { + sprintf(tmpstr, "%s%u", pvr_register_prefix, + (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ + op->immval_mask) ^ REG_PVR_MASK); + return(strdup(tmpstr)); + } else { + strcpy(spr, "pc"); + } + } + break; + } + + sprintf(tmpstr, "%s%s", register_prefix, spr); + return(strdup(tmpstr)); +} + +unsigned long +read_insn_microblaze (bfd_vma memaddr, + struct disassemble_info *info, + struct op_code_struct **opr) +{ + unsigned char ibytes[4]; + int status; + struct op_code_struct * op; + unsigned long inst; + + status = info->read_memory_func (memaddr, ibytes, 4, info); + + if (status != 0) + { + info->memory_error_func (status, memaddr, info); + return 0; + } + + if (info->endian == BFD_ENDIAN_BIG) + inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3]; + else if (info->endian == BFD_ENDIAN_LITTLE) + inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0]; + else + abort (); + + /* Just a linear search of the table. */ + for (op = opcodes; op->name != 0; op ++) + if (op->bit_sequence == (inst & op->opcode_mask)) + break; + + *opr = op; + return inst; +} + + +int +print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) +{ + fprintf_function fprintf_func = info->fprintf_func; + void * stream = info->stream; + unsigned long inst, prev_inst; + struct op_code_struct * op, *pop; + int immval = 0; + bfd_boolean immfound = FALSE; + static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */ + static int prev_insn_vma = -1; /*init the prev insn vma */ + int curr_insn_vma = info->buffer_vma; + + info->bytes_per_chunk = 4; + + inst = read_insn_microblaze (memaddr, info, &op); + if (inst == 0) { + return -1; + } + + if (prev_insn_vma == curr_insn_vma) { + if (memaddr-(info->bytes_per_chunk) == prev_insn_addr) { + prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop); + if (prev_inst == 0) + return -1; + if (pop->instr == imm) { + immval = (get_int_field_imm(prev_inst) << 16) & 0xffff0000; + immfound = TRUE; + } + else { + immval = 0; + immfound = FALSE; + } + } + } + /* make curr insn as prev insn */ + prev_insn_addr = memaddr; + prev_insn_vma = curr_insn_vma; + + if (op->name == 0) { + fprintf_func (stream, ".short 0x%04lx", inst); + } + else + { + fprintf_func (stream, "%s", op->name); + + switch (op->inst_type) + { + case INST_TYPE_RD_R1_R2: + fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_r2(inst)); + break; + case INST_TYPE_RD_R1_IMM: + fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); + if (info->print_address_func && get_int_field_r1(inst) == 0 && info->symbol_at_address_func) { + if (immfound) + immval |= (get_int_field_imm(inst) & 0x0000ffff); + else { + immval = get_int_field_imm(inst); + if (immval & 0x8000) + immval |= 0xFFFF0000; + } + if (immval > 0 && info->symbol_at_address_func(immval, info)) { + fprintf_func (stream, "\t// "); + info->print_address_func (immval, info); + } + } + break; + case INST_TYPE_RD_R1_IMM5: + fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); + break; + case INST_TYPE_RD_RFSL: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_rfsl(inst)); + break; + case INST_TYPE_R1_RFSL: + fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_rfsl(inst)); + break; + case INST_TYPE_RD_SPECIAL: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_special(inst, op)); + break; + case INST_TYPE_SPECIAL_R1: + fprintf_func(stream, "\t%s, %s", get_field_special(inst, op), get_field_r1(inst)); + break; + case INST_TYPE_RD_R1: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r1(inst)); + break; + case INST_TYPE_R1_R2: + fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_r2(inst)); + break; + case INST_TYPE_R1_IMM: + fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_imm(inst)); + /* The non-pc relative instructions are returns, which shouldn't + have a label printed */ + if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET && info->symbol_at_address_func) { + if (immfound) + immval |= (get_int_field_imm(inst) & 0x0000ffff); + else { + immval = get_int_field_imm(inst); + if (immval & 0x8000) + immval |= 0xFFFF0000; + } + immval += memaddr; + if (immval > 0 && info->symbol_at_address_func(immval, info)) { + fprintf_func (stream, "\t// "); + info->print_address_func (immval, info); + } else { + fprintf_func (stream, "\t\t// "); + fprintf_func (stream, "%x", immval); + } + } + break; + case INST_TYPE_RD_IMM: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm(inst)); + if (info->print_address_func && info->symbol_at_address_func) { + if (immfound) + immval |= (get_int_field_imm(inst) & 0x0000ffff); + else { + immval = get_int_field_imm(inst); + if (immval & 0x8000) + immval |= 0xFFFF0000; + } + if (op->inst_offset_type == INST_PC_OFFSET) + immval += (int) memaddr; + if (info->symbol_at_address_func(immval, info)) { + fprintf_func (stream, "\t// "); + info->print_address_func (immval, info); + } + } + break; + case INST_TYPE_IMM: + fprintf_func(stream, "\t%s", get_field_imm(inst)); + if (info->print_address_func && info->symbol_at_address_func && op->instr != imm) { + if (immfound) + immval |= (get_int_field_imm(inst) & 0x0000ffff); + else { + immval = get_int_field_imm(inst); + if (immval & 0x8000) + immval |= 0xFFFF0000; + } + if (op->inst_offset_type == INST_PC_OFFSET) + immval += (int) memaddr; + if (immval > 0 && info->symbol_at_address_func(immval, info)) { + fprintf_func (stream, "\t// "); + info->print_address_func (immval, info); + } else if (op->inst_offset_type == INST_PC_OFFSET) { + fprintf_func (stream, "\t\t// "); + fprintf_func (stream, "%x", immval); + } + } + break; + case INST_TYPE_RD_R2: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); + break; + case INST_TYPE_R2: + fprintf_func(stream, "\t%s", get_field_r2(inst)); + break; + case INST_TYPE_R1: + fprintf_func(stream, "\t%s", get_field_r1(inst)); + break; + case INST_TYPE_RD_R1_SPECIAL: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); + break; + case INST_TYPE_RD_IMM15: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm15(inst)); + break; + /* For tuqula instruction */ + case INST_TYPE_RD: + fprintf_func(stream, "\t%s", get_field_rd(inst)); + break; + case INST_TYPE_RFSL: + fprintf_func(stream, "\t%s", get_field_rfsl(inst)); + break; + default: + /* if the disassembler lags the instruction set */ + fprintf_func (stream, "\tundecoded operands, inst is 0x%04lx", inst); + break; + } + } + + /* Say how many bytes we consumed? */ + return 4; +} + +enum microblaze_instr +get_insn_microblaze (long inst, + bfd_boolean *isunsignedimm, + enum microblaze_instr_type *insn_type, + short *delay_slots) +{ + struct op_code_struct * op; + *isunsignedimm = FALSE; + + /* Just a linear search of the table. */ + for (op = opcodes; op->name != 0; op ++) + if (op->bit_sequence == (inst & op->opcode_mask)) + break; + + if (op->name == 0) + return invalid_inst; + else { + *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM); + *insn_type = op->instr_type; + *delay_slots = op->delay_slots; + return op->instr; + } +} + +short +get_delay_slots_microblaze (long inst) +{ + bfd_boolean isunsignedimm; + enum microblaze_instr_type insn_type; + enum microblaze_instr op; + short delay_slots; + + op = get_insn_microblaze( inst, &isunsignedimm, &insn_type, &delay_slots); + if (op == invalid_inst) + return 0; + else + return delay_slots; +} + +enum microblaze_instr +microblaze_decode_insn (long insn, + int *rd, + int *ra, + int *rb, + int *imm) +{ + enum microblaze_instr op; + bfd_boolean t1; + enum microblaze_instr_type t2; + short t3; + + op = get_insn_microblaze(insn, &t1, &t2, &t3); + *rd = (insn & RD_MASK) >> RD_LOW; + *ra = (insn & RA_MASK) >> RA_LOW; + *rb = (insn & RB_MASK) >> RB_LOW; + t3 = (insn & IMM_MASK) >> IMM_LOW; + *imm = (int) t3; + return (op); +} + +unsigned long +microblaze_get_target_address (long inst, + bfd_boolean immfound, + int immval, + long pcval, + long r1val, + long r2val, + bfd_boolean *targetvalid, + bfd_boolean *unconditionalbranch) +{ + struct op_code_struct * op; + long targetaddr = 0; + + *unconditionalbranch = FALSE; + /* Just a linear search of the table. */ + for (op = opcodes; op->name != 0; op ++) + if (op->bit_sequence == (inst & op->opcode_mask)) + break; + + if (op->name == 0) { + *targetvalid = FALSE; + } else if (op->instr_type == branch_inst) { + switch (op->inst_type) { + case INST_TYPE_R2: + *unconditionalbranch = TRUE; + /* fallthru */ + case INST_TYPE_RD_R2: + case INST_TYPE_R1_R2: + targetaddr = r2val; + *targetvalid = TRUE; + if (op->inst_offset_type == INST_PC_OFFSET) + targetaddr += pcval; + break; + case INST_TYPE_IMM: + *unconditionalbranch = TRUE; + /* fallthru */ + case INST_TYPE_RD_IMM: + case INST_TYPE_R1_IMM: + if (immfound) { + targetaddr = (immval << 16) & 0xffff0000; + targetaddr |= (get_int_field_imm(inst) & 0x0000ffff); + } else { + targetaddr = get_int_field_imm(inst); + if (targetaddr & 0x8000) + targetaddr |= 0xFFFF0000; + } + if (op->inst_offset_type == INST_PC_OFFSET) + targetaddr += pcval; + *targetvalid = TRUE; + break; + default: + *targetvalid = FALSE; + break; + } + } else if (op->instr_type == return_inst) { + if (immfound) { + targetaddr = (immval << 16) & 0xffff0000; + targetaddr |= (get_int_field_imm(inst) & 0x0000ffff); + } else { + targetaddr = get_int_field_imm(inst); + if (targetaddr & 0x8000) + targetaddr |= 0xFFFF0000; + } + targetaddr += r1val; + *targetvalid = TRUE; + } else { + *targetvalid = FALSE; + } + return targetaddr; +} diff --git a/disas/mips.c b/disas/mips.c new file mode 100644 index 0000000..1afe0c5 --- /dev/null +++ b/disas/mips.c @@ -0,0 +1,5790 @@ +/* Print mips instructions for GDB, the GNU debugger, or for objdump. + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp). + +This file is part of GDB, GAS, and the GNU binutils. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . */ + +#include "disas/bfd.h" + +/* mips.h. Mips opcode list for GDB, the GNU debugger. + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + Contributed by Ralph Campbell and OSF + Commented and modified by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, +see . */ + +/* These are bit masks and shift counts to use to access the various + fields of an instruction. To retrieve the X field of an + instruction, use the expression + (i >> OP_SH_X) & OP_MASK_X + To set the same field (to j), use + i = (i &~ (OP_MASK_X << OP_SH_X)) | (j << OP_SH_X) + + Make sure you use fields that are appropriate for the instruction, + of course. + + The 'i' format uses OP, RS, RT and IMMEDIATE. + + The 'j' format uses OP and TARGET. + + The 'r' format uses OP, RS, RT, RD, SHAMT and FUNCT. + + The 'b' format uses OP, RS, RT and DELTA. + + The floating point 'i' format uses OP, RS, RT and IMMEDIATE. + + The floating point 'r' format uses OP, FMT, FT, FS, FD and FUNCT. + + A breakpoint instruction uses OP, CODE and SPEC (10 bits of the + breakpoint instruction are not defined; Kane says the breakpoint + code field in BREAK is 20 bits; yet MIPS assemblers and debuggers + only use ten bits). An optional two-operand form of break/sdbbp + allows the lower ten bits to be set too, and MIPS32 and later + architectures allow 20 bits to be set with a signal operand + (using CODE20). + + The syscall instruction uses CODE20. + + The general coprocessor instructions use COPZ. */ + +#define OP_MASK_OP 0x3f +#define OP_SH_OP 26 +#define OP_MASK_RS 0x1f +#define OP_SH_RS 21 +#define OP_MASK_FR 0x1f +#define OP_SH_FR 21 +#define OP_MASK_FMT 0x1f +#define OP_SH_FMT 21 +#define OP_MASK_BCC 0x7 +#define OP_SH_BCC 18 +#define OP_MASK_CODE 0x3ff +#define OP_SH_CODE 16 +#define OP_MASK_CODE2 0x3ff +#define OP_SH_CODE2 6 +#define OP_MASK_RT 0x1f +#define OP_SH_RT 16 +#define OP_MASK_FT 0x1f +#define OP_SH_FT 16 +#define OP_MASK_CACHE 0x1f +#define OP_SH_CACHE 16 +#define OP_MASK_RD 0x1f +#define OP_SH_RD 11 +#define OP_MASK_FS 0x1f +#define OP_SH_FS 11 +#define OP_MASK_PREFX 0x1f +#define OP_SH_PREFX 11 +#define OP_MASK_CCC 0x7 +#define OP_SH_CCC 8 +#define OP_MASK_CODE20 0xfffff /* 20 bit syscall/breakpoint code. */ +#define OP_SH_CODE20 6 +#define OP_MASK_SHAMT 0x1f +#define OP_SH_SHAMT 6 +#define OP_MASK_FD 0x1f +#define OP_SH_FD 6 +#define OP_MASK_TARGET 0x3ffffff +#define OP_SH_TARGET 0 +#define OP_MASK_COPZ 0x1ffffff +#define OP_SH_COPZ 0 +#define OP_MASK_IMMEDIATE 0xffff +#define OP_SH_IMMEDIATE 0 +#define OP_MASK_DELTA 0xffff +#define OP_SH_DELTA 0 +#define OP_MASK_DELTA_R6 0x1ff +#define OP_SH_DELTA_R6 7 +#define OP_MASK_FUNCT 0x3f +#define OP_SH_FUNCT 0 +#define OP_MASK_SPEC 0x3f +#define OP_SH_SPEC 0 +#define OP_SH_LOCC 8 /* FP condition code. */ +#define OP_SH_HICC 18 /* FP condition code. */ +#define OP_MASK_CC 0x7 +#define OP_SH_COP1NORM 25 /* Normal COP1 encoding. */ +#define OP_MASK_COP1NORM 0x1 /* a single bit. */ +#define OP_SH_COP1SPEC 21 /* COP1 encodings. */ +#define OP_MASK_COP1SPEC 0xf +#define OP_MASK_COP1SCLR 0x4 +#define OP_MASK_COP1CMP 0x3 +#define OP_SH_COP1CMP 4 +#define OP_SH_FORMAT 21 /* FP short format field. */ +#define OP_MASK_FORMAT 0x7 +#define OP_SH_TRUE 16 +#define OP_MASK_TRUE 0x1 +#define OP_SH_GE 17 +#define OP_MASK_GE 0x01 +#define OP_SH_UNSIGNED 16 +#define OP_MASK_UNSIGNED 0x1 +#define OP_SH_HINT 16 +#define OP_MASK_HINT 0x1f +#define OP_SH_MMI 0 /* Multimedia (parallel) op. */ +#define OP_MASK_MMI 0x3f +#define OP_SH_MMISUB 6 +#define OP_MASK_MMISUB 0x1f +#define OP_MASK_PERFREG 0x1f /* Performance monitoring. */ +#define OP_SH_PERFREG 1 +#define OP_SH_SEL 0 /* Coprocessor select field. */ +#define OP_MASK_SEL 0x7 /* The sel field of mfcZ and mtcZ. */ +#define OP_SH_CODE19 6 /* 19 bit wait code. */ +#define OP_MASK_CODE19 0x7ffff +#define OP_SH_ALN 21 +#define OP_MASK_ALN 0x7 +#define OP_SH_VSEL 21 +#define OP_MASK_VSEL 0x1f +#define OP_MASK_VECBYTE 0x7 /* Selector field is really 4 bits, + but 0x8-0xf don't select bytes. */ +#define OP_SH_VECBYTE 22 +#define OP_MASK_VECALIGN 0x7 /* Vector byte-align (alni.ob) op. */ +#define OP_SH_VECALIGN 21 +#define OP_MASK_INSMSB 0x1f /* "ins" MSB. */ +#define OP_SH_INSMSB 11 +#define OP_MASK_EXTMSBD 0x1f /* "ext" MSBD. */ +#define OP_SH_EXTMSBD 11 + +#define OP_OP_COP0 0x10 +#define OP_OP_COP1 0x11 +#define OP_OP_COP2 0x12 +#define OP_OP_COP3 0x13 +#define OP_OP_LWC1 0x31 +#define OP_OP_LWC2 0x32 +#define OP_OP_LWC3 0x33 /* a.k.a. pref */ +#define OP_OP_LDC1 0x35 +#define OP_OP_LDC2 0x36 +#define OP_OP_LDC3 0x37 /* a.k.a. ld */ +#define OP_OP_SWC1 0x39 +#define OP_OP_SWC2 0x3a +#define OP_OP_SWC3 0x3b +#define OP_OP_SDC1 0x3d +#define OP_OP_SDC2 0x3e +#define OP_OP_SDC3 0x3f /* a.k.a. sd */ + +/* MIPS DSP ASE */ +#define OP_SH_DSPACC 11 +#define OP_MASK_DSPACC 0x3 +#define OP_SH_DSPACC_S 21 +#define OP_MASK_DSPACC_S 0x3 +#define OP_SH_DSPSFT 20 +#define OP_MASK_DSPSFT 0x3f +#define OP_SH_DSPSFT_7 19 +#define OP_MASK_DSPSFT_7 0x7f +#define OP_SH_SA3 21 +#define OP_MASK_SA3 0x7 +#define OP_SH_SA4 21 +#define OP_MASK_SA4 0xf +#define OP_SH_IMM8 16 +#define OP_MASK_IMM8 0xff +#define OP_SH_IMM10 16 +#define OP_MASK_IMM10 0x3ff +#define OP_SH_WRDSP 11 +#define OP_MASK_WRDSP 0x3f +#define OP_SH_RDDSP 16 +#define OP_MASK_RDDSP 0x3f +#define OP_SH_BP 11 +#define OP_MASK_BP 0x3 + +/* MIPS MT ASE */ +#define OP_SH_MT_U 5 +#define OP_MASK_MT_U 0x1 +#define OP_SH_MT_H 4 +#define OP_MASK_MT_H 0x1 +#define OP_SH_MTACC_T 18 +#define OP_MASK_MTACC_T 0x3 +#define OP_SH_MTACC_D 13 +#define OP_MASK_MTACC_D 0x3 + +/* MSA */ +#define OP_MASK_1BIT 0x1 +#define OP_SH_1BIT 16 +#define OP_MASK_2BIT 0x3 +#define OP_SH_2BIT 16 +#define OP_MASK_3BIT 0x7 +#define OP_SH_3BIT 16 +#define OP_MASK_4BIT 0xf +#define OP_SH_4BIT 16 +#define OP_MASK_5BIT 0x1f +#define OP_SH_5BIT 16 +#define OP_MASK_10BIT 0x3ff +#define OP_SH_10BIT 11 +#define OP_MASK_MSACR11 0x1f +#define OP_SH_MSACR11 11 +#define OP_MASK_MSACR6 0x1f +#define OP_SH_MSACR6 6 +#define OP_MASK_GPR 0x1f +#define OP_SH_GPR 6 +#define OP_MASK_1_TO_4 0x3 +#define OP_SH_1_TO_4 6 + +#define OP_OP_COP0 0x10 +#define OP_OP_COP1 0x11 +#define OP_OP_COP2 0x12 +#define OP_OP_COP3 0x13 +#define OP_OP_LWC1 0x31 +#define OP_OP_LWC2 0x32 +#define OP_OP_LWC3 0x33 /* a.k.a. pref */ +#define OP_OP_LDC1 0x35 +#define OP_OP_LDC2 0x36 +#define OP_OP_LDC3 0x37 /* a.k.a. ld */ +#define OP_OP_SWC1 0x39 +#define OP_OP_SWC2 0x3a +#define OP_OP_SWC3 0x3b +#define OP_OP_SDC1 0x3d +#define OP_OP_SDC2 0x3e +#define OP_OP_SDC3 0x3f /* a.k.a. sd */ + +/* Values in the 'VSEL' field. */ +#define MDMX_FMTSEL_IMM_QH 0x1d +#define MDMX_FMTSEL_IMM_OB 0x1e +#define MDMX_FMTSEL_VEC_QH 0x15 +#define MDMX_FMTSEL_VEC_OB 0x16 + +/* UDI */ +#define OP_SH_UDI1 6 +#define OP_MASK_UDI1 0x1f +#define OP_SH_UDI2 6 +#define OP_MASK_UDI2 0x3ff +#define OP_SH_UDI3 6 +#define OP_MASK_UDI3 0x7fff +#define OP_SH_UDI4 6 +#define OP_MASK_UDI4 0xfffff +/* This structure holds information for a particular instruction. */ + +struct mips_opcode +{ + /* The name of the instruction. */ + const char *name; + /* A string describing the arguments for this instruction. */ + const char *args; + /* The basic opcode for the instruction. When assembling, this + opcode is modified by the arguments to produce the actual opcode + that is used. If pinfo is INSN_MACRO, then this is 0. */ + unsigned long match; + /* If pinfo is not INSN_MACRO, then this is a bit mask for the + relevant portions of the opcode when disassembling. If the + actual opcode anded with the match field equals the opcode field, + then we have found the correct instruction. If pinfo is + INSN_MACRO, then this field is the macro identifier. */ + unsigned long mask; + /* For a macro, this is INSN_MACRO. Otherwise, it is a collection + of bits describing the instruction, notably any relevant hazard + information. */ + unsigned long pinfo; + /* A collection of additional bits describing the instruction. */ + unsigned long pinfo2; + /* A collection of bits describing the instruction sets of which this + instruction or macro is a member. */ + unsigned long membership; +}; + +/* These are the characters which may appear in the args field of an + instruction. They appear in the order in which the fields appear + when the instruction is used. Commas and parentheses in the args + string are ignored when assembling, and written into the output + when disassembling. + + Each of these characters corresponds to a mask field defined above. + + "<" 5 bit shift amount (OP_*_SHAMT) + ">" shift amount between 32 and 63, stored after subtracting 32 (OP_*_SHAMT) + "a" 26 bit target address (OP_*_TARGET) + "b" 5 bit base register (OP_*_RS) + "c" 10 bit breakpoint code (OP_*_CODE) + "d" 5 bit destination register specifier (OP_*_RD) + "h" 5 bit prefx hint (OP_*_PREFX) + "i" 16 bit unsigned immediate (OP_*_IMMEDIATE) + "j" 16 bit signed immediate (OP_*_DELTA) + "k" 5 bit cache opcode in target register position (OP_*_CACHE) + Also used for immediate operands in vr5400 vector insns. + "o" 16 bit signed offset (OP_*_DELTA) + "p" 16 bit PC relative branch target address (OP_*_DELTA) + "q" 10 bit extra breakpoint code (OP_*_CODE2) + "r" 5 bit same register used as both source and target (OP_*_RS) + "s" 5 bit source register specifier (OP_*_RS) + "t" 5 bit target register (OP_*_RT) + "u" 16 bit upper 16 bits of address (OP_*_IMMEDIATE) + "v" 5 bit same register used as both source and destination (OP_*_RS) + "w" 5 bit same register used as both target and destination (OP_*_RT) + "U" 5 bit same destination register in both OP_*_RD and OP_*_RT + (used by clo and clz) + "C" 25 bit coprocessor function code (OP_*_COPZ) + "B" 20 bit syscall/breakpoint function code (OP_*_CODE20) + "J" 19 bit wait function code (OP_*_CODE19) + "x" accept and ignore register name + "z" must be zero register + "K" 5 bit Hardware Register (rdhwr instruction) (OP_*_RD) + "+A" 5 bit ins/ext/dins/dext/dinsm/dextm position, which becomes + LSB (OP_*_SHAMT). + Enforces: 0 <= pos < 32. + "+B" 5 bit ins/dins size, which becomes MSB (OP_*_INSMSB). + Requires that "+A" or "+E" occur first to set position. + Enforces: 0 < (pos+size) <= 32. + "+C" 5 bit ext/dext size, which becomes MSBD (OP_*_EXTMSBD). + Requires that "+A" or "+E" occur first to set position. + Enforces: 0 < (pos+size) <= 32. + (Also used by "dext" w/ different limits, but limits for + that are checked by the M_DEXT macro.) + "+E" 5 bit dinsu/dextu position, which becomes LSB-32 (OP_*_SHAMT). + Enforces: 32 <= pos < 64. + "+F" 5 bit "dinsm/dinsu" size, which becomes MSB-32 (OP_*_INSMSB). + Requires that "+A" or "+E" occur first to set position. + Enforces: 32 < (pos+size) <= 64. + "+G" 5 bit "dextm" size, which becomes MSBD-32 (OP_*_EXTMSBD). + Requires that "+A" or "+E" occur first to set position. + Enforces: 32 < (pos+size) <= 64. + "+H" 5 bit "dextu" size, which becomes MSBD (OP_*_EXTMSBD). + Requires that "+A" or "+E" occur first to set position. + Enforces: 32 < (pos+size) <= 64. + + Floating point instructions: + "D" 5 bit destination register (OP_*_FD) + "M" 3 bit compare condition code (OP_*_CCC) (only used for mips4 and up) + "N" 3 bit branch condition code (OP_*_BCC) (only used for mips4 and up) + "S" 5 bit fs source 1 register (OP_*_FS) + "T" 5 bit ft source 2 register (OP_*_FT) + "R" 5 bit fr source 3 register (OP_*_FR) + "V" 5 bit same register used as floating source and destination (OP_*_FS) + "W" 5 bit same register used as floating target and destination (OP_*_FT) + + Coprocessor instructions: + "E" 5 bit target register (OP_*_RT) + "G" 5 bit destination register (OP_*_RD) + "H" 3 bit sel field for (d)mtc* and (d)mfc* (OP_*_SEL) + "P" 5 bit performance-monitor register (OP_*_PERFREG) + "e" 5 bit vector register byte specifier (OP_*_VECBYTE) + "%" 3 bit immediate vr5400 vector alignment operand (OP_*_VECALIGN) + see also "k" above + "+D" Combined destination register ("G") and sel ("H") for CP0 ops, + for pretty-printing in disassembly only. + + Macro instructions: + "A" General 32 bit expression + "I" 32 bit immediate (value placed in imm_expr). + "+I" 32 bit immediate (value placed in imm2_expr). + "F" 64 bit floating point constant in .rdata + "L" 64 bit floating point constant in .lit8 + "f" 32 bit floating point constant + "l" 32 bit floating point constant in .lit4 + + MDMX instruction operands (note that while these use the FP register + fields, they accept both $fN and $vN names for the registers): + "O" MDMX alignment offset (OP_*_ALN) + "Q" MDMX vector/scalar/immediate source (OP_*_VSEL and OP_*_FT) + "X" MDMX destination register (OP_*_FD) + "Y" MDMX source register (OP_*_FS) + "Z" MDMX source register (OP_*_FT) + + DSP ASE usage: + "2" 2 bit unsigned immediate for byte align (OP_*_BP) + "3" 3 bit unsigned immediate (OP_*_SA3) + "4" 4 bit unsigned immediate (OP_*_SA4) + "5" 8 bit unsigned immediate (OP_*_IMM8) + "6" 5 bit unsigned immediate (OP_*_RS) + "7" 2 bit dsp accumulator register (OP_*_DSPACC) + "8" 6 bit unsigned immediate (OP_*_WRDSP) + "9" 2 bit dsp accumulator register (OP_*_DSPACC_S) + "0" 6 bit signed immediate (OP_*_DSPSFT) + ":" 7 bit signed immediate (OP_*_DSPSFT_7) + "'" 6 bit unsigned immediate (OP_*_RDDSP) + "@" 10 bit signed immediate (OP_*_IMM10) + + MT ASE usage: + "!" 1 bit usermode flag (OP_*_MT_U) + "$" 1 bit load high flag (OP_*_MT_H) + "*" 2 bit dsp/smartmips accumulator register (OP_*_MTACC_T) + "&" 2 bit dsp/smartmips accumulator register (OP_*_MTACC_D) + "g" 5 bit coprocessor 1 and 2 destination register (OP_*_RD) + "+t" 5 bit coprocessor 0 destination register (OP_*_RT) + "+T" 5 bit coprocessor 0 destination register (OP_*_RT) - disassembly only + + UDI immediates: + "+1" UDI immediate bits 6-10 + "+2" UDI immediate bits 6-15 + "+3" UDI immediate bits 6-20 + "+4" UDI immediate bits 6-25 + + R6 immediates/displacements : + (adding suffix to 'o' to avoid adding new characters) + "+o" 9 bits immediate/displacement (shift = 7) + "+o1" 18 bits immediate/displacement (shift = 0) + "+o2" 19 bits immediate/displacement (shift = 0) + + Other: + "()" parens surrounding optional value + "," separates operands + "[]" brackets around index for vector-op scalar operand specifier (vr5400) + "+" Start of extension sequence. + + Characters used so far, for quick reference when adding more: + "234567890" + "%[]<>(),+:'@!$*&" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklopqrstuvwxz" + + Extension character sequences used so far ("+" followed by the + following), for quick reference when adding more: + "1234" + "ABCDEFGHIT" + "t" +*/ + +/* These are the bits which may be set in the pinfo field of an + instructions, if it is not equal to INSN_MACRO. */ + +/* Modifies the general purpose register in OP_*_RD. */ +#define INSN_WRITE_GPR_D 0x00000001 +/* Modifies the general purpose register in OP_*_RT. */ +#define INSN_WRITE_GPR_T 0x00000002 +/* Modifies general purpose register 31. */ +#define INSN_WRITE_GPR_31 0x00000004 +/* Modifies the floating point register in OP_*_FD. */ +#define INSN_WRITE_FPR_D 0x00000008 +/* Modifies the floating point register in OP_*_FS. */ +#define INSN_WRITE_FPR_S 0x00000010 +/* Modifies the floating point register in OP_*_FT. */ +#define INSN_WRITE_FPR_T 0x00000020 +/* Reads the general purpose register in OP_*_RS. */ +#define INSN_READ_GPR_S 0x00000040 +/* Reads the general purpose register in OP_*_RT. */ +#define INSN_READ_GPR_T 0x00000080 +/* Reads the floating point register in OP_*_FS. */ +#define INSN_READ_FPR_S 0x00000100 +/* Reads the floating point register in OP_*_FT. */ +#define INSN_READ_FPR_T 0x00000200 +/* Reads the floating point register in OP_*_FR. */ +#define INSN_READ_FPR_R 0x00000400 +/* Modifies coprocessor condition code. */ +#define INSN_WRITE_COND_CODE 0x00000800 +/* Reads coprocessor condition code. */ +#define INSN_READ_COND_CODE 0x00001000 +/* TLB operation. */ +#define INSN_TLB 0x00002000 +/* Reads coprocessor register other than floating point register. */ +#define INSN_COP 0x00004000 +/* Instruction loads value from memory, requiring delay. */ +#define INSN_LOAD_MEMORY_DELAY 0x00008000 +/* Instruction loads value from coprocessor, requiring delay. */ +#define INSN_LOAD_COPROC_DELAY 0x00010000 +/* Instruction has unconditional branch delay slot. */ +#define INSN_UNCOND_BRANCH_DELAY 0x00020000 +/* Instruction has conditional branch delay slot. */ +#define INSN_COND_BRANCH_DELAY 0x00040000 +/* Conditional branch likely: if branch not taken, insn nullified. */ +#define INSN_COND_BRANCH_LIKELY 0x00080000 +/* Moves to coprocessor register, requiring delay. */ +#define INSN_COPROC_MOVE_DELAY 0x00100000 +/* Loads coprocessor register from memory, requiring delay. */ +#define INSN_COPROC_MEMORY_DELAY 0x00200000 +/* Reads the HI register. */ +#define INSN_READ_HI 0x00400000 +/* Reads the LO register. */ +#define INSN_READ_LO 0x00800000 +/* Modifies the HI register. */ +#define INSN_WRITE_HI 0x01000000 +/* Modifies the LO register. */ +#define INSN_WRITE_LO 0x02000000 +/* Takes a trap (easier to keep out of delay slot). */ +#define INSN_TRAP 0x04000000 +/* Instruction stores value into memory. */ +#define INSN_STORE_MEMORY 0x08000000 +/* Instruction uses single precision floating point. */ +#define FP_S 0x10000000 +/* Instruction uses double precision floating point. */ +#define FP_D 0x20000000 +/* Instruction is part of the tx39's integer multiply family. */ +#define INSN_MULT 0x40000000 +/* Instruction synchronize shared memory. */ +#define INSN_SYNC 0x80000000 + +/* These are the bits which may be set in the pinfo2 field of an + instruction. */ + +/* Instruction is a simple alias (I.E. "move" for daddu/addu/or) */ +#define INSN2_ALIAS 0x00000001 +/* Instruction reads MDMX accumulator. */ +#define INSN2_READ_MDMX_ACC 0x00000002 +/* Instruction writes MDMX accumulator. */ +#define INSN2_WRITE_MDMX_ACC 0x00000004 + +/* Reads the general purpose register in OP_*_RD. */ +#define INSN2_READ_GPR_D 0x00000200 + +/* Instruction is actually a macro. It should be ignored by the + disassembler, and requires special treatment by the assembler. */ +#define INSN_MACRO 0xffffffff + +/* Masks used to mark instructions to indicate which MIPS ISA level + they were introduced in. ISAs, as defined below, are logical + ORs of these bits, indicating that they support the instructions + defined at the given level. */ + +#define INSN_ISA_MASK 0x00000fff +#define INSN_ISA1 0x00000001 +#define INSN_ISA2 0x00000002 +#define INSN_ISA3 0x00000004 +#define INSN_ISA4 0x00000008 +#define INSN_ISA5 0x00000010 +#define INSN_ISA32 0x00000020 +#define INSN_ISA64 0x00000040 +#define INSN_ISA32R2 0x00000080 +#define INSN_ISA64R2 0x00000100 +#define INSN_ISA32R6 0x00000200 +#define INSN_ISA64R6 0x00000400 + +/* Masks used for MIPS-defined ASEs. */ +#define INSN_ASE_MASK 0x0000f000 + +/* DSP ASE */ +#define INSN_DSP 0x00001000 +#define INSN_DSP64 0x00002000 +/* MIPS 16 ASE */ +#define INSN_MIPS16 0x00004000 +/* MIPS-3D ASE */ +#define INSN_MIPS3D 0x00008000 + +/* Chip specific instructions. These are bitmasks. */ + +/* MIPS R4650 instruction. */ +#define INSN_4650 0x00010000 +/* LSI R4010 instruction. */ +#define INSN_4010 0x00020000 +/* NEC VR4100 instruction. */ +#define INSN_4100 0x00040000 +/* Toshiba R3900 instruction. */ +#define INSN_3900 0x00080000 +/* MIPS R10000 instruction. */ +#define INSN_10000 0x00100000 +/* Broadcom SB-1 instruction. */ +#define INSN_SB1 0x00200000 +/* NEC VR4111/VR4181 instruction. */ +#define INSN_4111 0x00400000 +/* NEC VR4120 instruction. */ +#define INSN_4120 0x00800000 +/* NEC VR5400 instruction. */ +#define INSN_5400 0x01000000 +/* NEC VR5500 instruction. */ +#define INSN_5500 0x02000000 + +/* MDMX ASE */ +#define INSN_MDMX 0x00000000 /* Deprecated */ + +/* MIPS MSA Extension */ +#define INSN_MSA 0x04000000 +#define INSN_MSA64 0x04000000 + +/* MT ASE */ +#define INSN_MT 0x08000000 +/* SmartMIPS ASE */ +#define INSN_SMARTMIPS 0x10000000 +/* DSP R2 ASE */ +#define INSN_DSPR2 0x20000000 + +/* ST Microelectronics Loongson 2E. */ +#define INSN_LOONGSON_2E 0x40000000 +/* ST Microelectronics Loongson 2F. */ +#define INSN_LOONGSON_2F 0x80000000 + +/* MIPS ISA defines, use instead of hardcoding ISA level. */ + +#define ISA_UNKNOWN 0 /* Gas internal use. */ +#define ISA_MIPS1 (INSN_ISA1) +#define ISA_MIPS2 (ISA_MIPS1 | INSN_ISA2) +#define ISA_MIPS3 (ISA_MIPS2 | INSN_ISA3) +#define ISA_MIPS4 (ISA_MIPS3 | INSN_ISA4) +#define ISA_MIPS5 (ISA_MIPS4 | INSN_ISA5) + +#define ISA_MIPS32 (ISA_MIPS2 | INSN_ISA32) +#define ISA_MIPS64 (ISA_MIPS5 | INSN_ISA32 | INSN_ISA64) + +#define ISA_MIPS32R2 (ISA_MIPS32 | INSN_ISA32R2) +#define ISA_MIPS64R2 (ISA_MIPS64 | INSN_ISA32R2 | INSN_ISA64R2) + +#define ISA_MIPS32R6 (ISA_MIPS32R2 | INSN_ISA32R6) +#define ISA_MIPS64R6 (ISA_MIPS64R2 | INSN_ISA32R6 | INSN_ISA64R6) + +/* CPU defines, use instead of hardcoding processor number. Keep this + in sync with bfd/archures.c in order for machine selection to work. */ +#define CPU_UNKNOWN 0 /* Gas internal use. */ +#define CPU_R3000 3000 +#define CPU_R3900 3900 +#define CPU_R4000 4000 +#define CPU_R4010 4010 +#define CPU_VR4100 4100 +#define CPU_R4111 4111 +#define CPU_VR4120 4120 +#define CPU_R4300 4300 +#define CPU_R4400 4400 +#define CPU_R4600 4600 +#define CPU_R4650 4650 +#define CPU_R5000 5000 +#define CPU_VR5400 5400 +#define CPU_VR5500 5500 +#define CPU_R6000 6000 +#define CPU_RM7000 7000 +#define CPU_R8000 8000 +#define CPU_R10000 10000 +#define CPU_R12000 12000 +#define CPU_MIPS16 16 +#define CPU_MIPS32 32 +#define CPU_MIPS32R2 33 +#define CPU_MIPS5 5 +#define CPU_MIPS64 64 +#define CPU_MIPS64R2 65 +#define CPU_SB1 12310201 /* octal 'SB', 01. */ + +/* Test for membership in an ISA including chip specific ISAs. INSN + is pointer to an element of the opcode table; ISA is the specified + ISA/ASE bitmask to test against; and CPU is the CPU specific ISA to + test, or zero if no CPU specific ISA test is desired. */ + +#if 0 +#define OPCODE_IS_MEMBER(insn, isa, cpu) \ + (((insn)->membership & isa) != 0 \ + || (cpu == CPU_R4650 && ((insn)->membership & INSN_4650) != 0) \ + || (cpu == CPU_RM7000 && ((insn)->membership & INSN_4650) != 0) \ + || (cpu == CPU_RM9000 && ((insn)->membership & INSN_4650) != 0) \ + || (cpu == CPU_R4010 && ((insn)->membership & INSN_4010) != 0) \ + || (cpu == CPU_VR4100 && ((insn)->membership & INSN_4100) != 0) \ + || (cpu == CPU_R3900 && ((insn)->membership & INSN_3900) != 0) \ + || ((cpu == CPU_R10000 || cpu == CPU_R12000) \ + && ((insn)->membership & INSN_10000) != 0) \ + || (cpu == CPU_SB1 && ((insn)->membership & INSN_SB1) != 0) \ + || (cpu == CPU_R4111 && ((insn)->membership & INSN_4111) != 0) \ + || (cpu == CPU_VR4120 && ((insn)->membership & INSN_4120) != 0) \ + || (cpu == CPU_VR5400 && ((insn)->membership & INSN_5400) != 0) \ + || (cpu == CPU_VR5500 && ((insn)->membership & INSN_5500) != 0) \ + || 0) /* Please keep this term for easier source merging. */ +#else +#define OPCODE_IS_MEMBER(insn, isa, cpu) \ + (1 != 0) +#endif + +/* This is a list of macro expanded instructions. + + _I appended means immediate + _A appended means address + _AB appended means address with base register + _D appended means 64 bit floating point constant + _S appended means 32 bit floating point constant. */ + +enum +{ + M_ABS, + M_ADD_I, + M_ADDU_I, + M_AND_I, + M_BALIGN, + M_BEQ, + M_BEQ_I, + M_BEQL_I, + M_BGE, + M_BGEL, + M_BGE_I, + M_BGEL_I, + M_BGEU, + M_BGEUL, + M_BGEU_I, + M_BGEUL_I, + M_BGT, + M_BGTL, + M_BGT_I, + M_BGTL_I, + M_BGTU, + M_BGTUL, + M_BGTU_I, + M_BGTUL_I, + M_BLE, + M_BLEL, + M_BLE_I, + M_BLEL_I, + M_BLEU, + M_BLEUL, + M_BLEU_I, + M_BLEUL_I, + M_BLT, + M_BLTL, + M_BLT_I, + M_BLTL_I, + M_BLTU, + M_BLTUL, + M_BLTU_I, + M_BLTUL_I, + M_BNE, + M_BNE_I, + M_BNEL_I, + M_CACHE_AB, + M_DABS, + M_DADD_I, + M_DADDU_I, + M_DDIV_3, + M_DDIV_3I, + M_DDIVU_3, + M_DDIVU_3I, + M_DEXT, + M_DINS, + M_DIV_3, + M_DIV_3I, + M_DIVU_3, + M_DIVU_3I, + M_DLA_AB, + M_DLCA_AB, + M_DLI, + M_DMUL, + M_DMUL_I, + M_DMULO, + M_DMULO_I, + M_DMULOU, + M_DMULOU_I, + M_DREM_3, + M_DREM_3I, + M_DREMU_3, + M_DREMU_3I, + M_DSUB_I, + M_DSUBU_I, + M_DSUBU_I_2, + M_J_A, + M_JAL_1, + M_JAL_2, + M_JAL_A, + M_L_DOB, + M_L_DAB, + M_LA_AB, + M_LB_A, + M_LB_AB, + M_LBU_A, + M_LBU_AB, + M_LCA_AB, + M_LD_A, + M_LD_OB, + M_LD_AB, + M_LDC1_AB, + M_LDC2_AB, + M_LDC3_AB, + M_LDL_AB, + M_LDR_AB, + M_LH_A, + M_LH_AB, + M_LHU_A, + M_LHU_AB, + M_LI, + M_LI_D, + M_LI_DD, + M_LI_S, + M_LI_SS, + M_LL_AB, + M_LLD_AB, + M_LS_A, + M_LW_A, + M_LW_AB, + M_LWC0_A, + M_LWC0_AB, + M_LWC1_A, + M_LWC1_AB, + M_LWC2_A, + M_LWC2_AB, + M_LWC3_A, + M_LWC3_AB, + M_LWL_A, + M_LWL_AB, + M_LWR_A, + M_LWR_AB, + M_LWU_AB, + M_MOVE, + M_MUL, + M_MUL_I, + M_MULO, + M_MULO_I, + M_MULOU, + M_MULOU_I, + M_NOR_I, + M_OR_I, + M_REM_3, + M_REM_3I, + M_REMU_3, + M_REMU_3I, + M_DROL, + M_ROL, + M_DROL_I, + M_ROL_I, + M_DROR, + M_ROR, + M_DROR_I, + M_ROR_I, + M_S_DA, + M_S_DOB, + M_S_DAB, + M_S_S, + M_SC_AB, + M_SCD_AB, + M_SD_A, + M_SD_OB, + M_SD_AB, + M_SDC1_AB, + M_SDC2_AB, + M_SDC3_AB, + M_SDL_AB, + M_SDR_AB, + M_SEQ, + M_SEQ_I, + M_SGE, + M_SGE_I, + M_SGEU, + M_SGEU_I, + M_SGT, + M_SGT_I, + M_SGTU, + M_SGTU_I, + M_SLE, + M_SLE_I, + M_SLEU, + M_SLEU_I, + M_SLT_I, + M_SLTU_I, + M_SNE, + M_SNE_I, + M_SB_A, + M_SB_AB, + M_SH_A, + M_SH_AB, + M_SW_A, + M_SW_AB, + M_SWC0_A, + M_SWC0_AB, + M_SWC1_A, + M_SWC1_AB, + M_SWC2_A, + M_SWC2_AB, + M_SWC3_A, + M_SWC3_AB, + M_SWL_A, + M_SWL_AB, + M_SWR_A, + M_SWR_AB, + M_SUB_I, + M_SUBU_I, + M_SUBU_I_2, + M_TEQ_I, + M_TGE_I, + M_TGEU_I, + M_TLT_I, + M_TLTU_I, + M_TNE_I, + M_TRUNCWD, + M_TRUNCWS, + M_ULD, + M_ULD_A, + M_ULH, + M_ULH_A, + M_ULHU, + M_ULHU_A, + M_ULW, + M_ULW_A, + M_USH, + M_USH_A, + M_USW, + M_USW_A, + M_USD, + M_USD_A, + M_XOR_I, + M_COP0, + M_COP1, + M_COP2, + M_COP3, + M_NUM_MACROS +}; + + +/* The order of overloaded instructions matters. Label arguments and + register arguments look the same. Instructions that can have either + for arguments must apear in the correct order in this table for the + assembler to pick the right one. In other words, entries with + immediate operands must apear after the same instruction with + registers. + + Many instructions are short hand for other instructions (i.e., The + jal instruction is short for jalr ). */ + +extern const struct mips_opcode mips_builtin_opcodes[]; +extern const int bfd_mips_num_builtin_opcodes; +extern struct mips_opcode *mips_opcodes; +extern int bfd_mips_num_opcodes; +#define NUMOPCODES bfd_mips_num_opcodes + + +/* The rest of this file adds definitions for the mips16 TinyRISC + processor. */ + +/* These are the bitmasks and shift counts used for the different + fields in the instruction formats. Other than OP, no masks are + provided for the fixed portions of an instruction, since they are + not needed. + + The I format uses IMM11. + + The RI format uses RX and IMM8. + + The RR format uses RX, and RY. + + The RRI format uses RX, RY, and IMM5. + + The RRR format uses RX, RY, and RZ. + + The RRI_A format uses RX, RY, and IMM4. + + The SHIFT format uses RX, RY, and SHAMT. + + The I8 format uses IMM8. + + The I8_MOVR32 format uses RY and REGR32. + + The IR_MOV32R format uses REG32R and MOV32Z. + + The I64 format uses IMM8. + + The RI64 format uses RY and IMM5. + */ + +#define MIPS16OP_MASK_OP 0x1f +#define MIPS16OP_SH_OP 11 +#define MIPS16OP_MASK_IMM11 0x7ff +#define MIPS16OP_SH_IMM11 0 +#define MIPS16OP_MASK_RX 0x7 +#define MIPS16OP_SH_RX 8 +#define MIPS16OP_MASK_IMM8 0xff +#define MIPS16OP_SH_IMM8 0 +#define MIPS16OP_MASK_RY 0x7 +#define MIPS16OP_SH_RY 5 +#define MIPS16OP_MASK_IMM5 0x1f +#define MIPS16OP_SH_IMM5 0 +#define MIPS16OP_MASK_RZ 0x7 +#define MIPS16OP_SH_RZ 2 +#define MIPS16OP_MASK_IMM4 0xf +#define MIPS16OP_SH_IMM4 0 +#define MIPS16OP_MASK_REGR32 0x1f +#define MIPS16OP_SH_REGR32 0 +#define MIPS16OP_MASK_REG32R 0x1f +#define MIPS16OP_SH_REG32R 3 +#define MIPS16OP_EXTRACT_REG32R(i) ((((i) >> 5) & 7) | ((i) & 0x18)) +#define MIPS16OP_MASK_MOVE32Z 0x7 +#define MIPS16OP_SH_MOVE32Z 0 +#define MIPS16OP_MASK_IMM6 0x3f +#define MIPS16OP_SH_IMM6 5 + +/* These are the characters which may appears in the args field of an + instruction. They appear in the order in which the fields appear + when the instruction is used. Commas and parentheses in the args + string are ignored when assembling, and written into the output + when disassembling. + + "y" 3 bit register (MIPS16OP_*_RY) + "x" 3 bit register (MIPS16OP_*_RX) + "z" 3 bit register (MIPS16OP_*_RZ) + "Z" 3 bit register (MIPS16OP_*_MOVE32Z) + "v" 3 bit same register as source and destination (MIPS16OP_*_RX) + "w" 3 bit same register as source and destination (MIPS16OP_*_RY) + "0" zero register ($0) + "S" stack pointer ($sp or $29) + "P" program counter + "R" return address register ($ra or $31) + "X" 5 bit MIPS register (MIPS16OP_*_REGR32) + "Y" 5 bit MIPS register (MIPS16OP_*_REG32R) + "6" 6 bit unsigned break code (MIPS16OP_*_IMM6) + "a" 26 bit jump address + "e" 11 bit extension value + "l" register list for entry instruction + "L" register list for exit instruction + + The remaining codes may be extended. Except as otherwise noted, + the full extended operand is a 16 bit signed value. + "<" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 5 bit unsigned) + ">" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 5 bit unsigned) + "[" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 6 bit unsigned) + "]" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 6 bit unsigned) + "4" 4 bit signed immediate * 0 (MIPS16OP_*_IMM4) (full 15 bit signed) + "5" 5 bit unsigned immediate * 0 (MIPS16OP_*_IMM5) + "H" 5 bit unsigned immediate * 2 (MIPS16OP_*_IMM5) + "W" 5 bit unsigned immediate * 4 (MIPS16OP_*_IMM5) + "D" 5 bit unsigned immediate * 8 (MIPS16OP_*_IMM5) + "j" 5 bit signed immediate * 0 (MIPS16OP_*_IMM5) + "8" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8) + "V" 8 bit unsigned immediate * 4 (MIPS16OP_*_IMM8) + "C" 8 bit unsigned immediate * 8 (MIPS16OP_*_IMM8) + "U" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8) (full 16 bit unsigned) + "k" 8 bit signed immediate * 0 (MIPS16OP_*_IMM8) + "K" 8 bit signed immediate * 8 (MIPS16OP_*_IMM8) + "p" 8 bit conditional branch address (MIPS16OP_*_IMM8) + "q" 11 bit branch address (MIPS16OP_*_IMM11) + "A" 8 bit PC relative address * 4 (MIPS16OP_*_IMM8) + "B" 5 bit PC relative address * 8 (MIPS16OP_*_IMM5) + "E" 5 bit PC relative address * 4 (MIPS16OP_*_IMM5) + */ + +/* Save/restore encoding for the args field when all 4 registers are + either saved as arguments or saved/restored as statics. */ +#define MIPS16_ALL_ARGS 0xe +#define MIPS16_ALL_STATICS 0xb + +/* For the mips16, we use the same opcode table format and a few of + the same flags. However, most of the flags are different. */ + +/* Modifies the register in MIPS16OP_*_RX. */ +#define MIPS16_INSN_WRITE_X 0x00000001 +/* Modifies the register in MIPS16OP_*_RY. */ +#define MIPS16_INSN_WRITE_Y 0x00000002 +/* Modifies the register in MIPS16OP_*_RZ. */ +#define MIPS16_INSN_WRITE_Z 0x00000004 +/* Modifies the T ($24) register. */ +#define MIPS16_INSN_WRITE_T 0x00000008 +/* Modifies the SP ($29) register. */ +#define MIPS16_INSN_WRITE_SP 0x00000010 +/* Modifies the RA ($31) register. */ +#define MIPS16_INSN_WRITE_31 0x00000020 +/* Modifies the general purpose register in MIPS16OP_*_REG32R. */ +#define MIPS16_INSN_WRITE_GPR_Y 0x00000040 +/* Reads the register in MIPS16OP_*_RX. */ +#define MIPS16_INSN_READ_X 0x00000080 +/* Reads the register in MIPS16OP_*_RY. */ +#define MIPS16_INSN_READ_Y 0x00000100 +/* Reads the register in MIPS16OP_*_MOVE32Z. */ +#define MIPS16_INSN_READ_Z 0x00000200 +/* Reads the T ($24) register. */ +#define MIPS16_INSN_READ_T 0x00000400 +/* Reads the SP ($29) register. */ +#define MIPS16_INSN_READ_SP 0x00000800 +/* Reads the RA ($31) register. */ +#define MIPS16_INSN_READ_31 0x00001000 +/* Reads the program counter. */ +#define MIPS16_INSN_READ_PC 0x00002000 +/* Reads the general purpose register in MIPS16OP_*_REGR32. */ +#define MIPS16_INSN_READ_GPR_X 0x00004000 +/* Is a branch insn. */ +#define MIPS16_INSN_BRANCH 0x00010000 + +/* The following flags have the same value for the mips16 opcode + table: + INSN_UNCOND_BRANCH_DELAY + INSN_COND_BRANCH_DELAY + INSN_COND_BRANCH_LIKELY (never used) + INSN_READ_HI + INSN_READ_LO + INSN_WRITE_HI + INSN_WRITE_LO + INSN_TRAP + INSN_ISA3 + */ + +extern const struct mips_opcode mips16_opcodes[]; +extern const int bfd_mips16_num_opcodes; + +/* Short hand so the lines aren't too long. */ + +#define LDD INSN_LOAD_MEMORY_DELAY +#define LCD INSN_LOAD_COPROC_DELAY +#define UBD INSN_UNCOND_BRANCH_DELAY +#define CBD INSN_COND_BRANCH_DELAY +#define COD INSN_COPROC_MOVE_DELAY +#define CLD INSN_COPROC_MEMORY_DELAY +#define CBL INSN_COND_BRANCH_LIKELY +#define TRAP INSN_TRAP +#define SM INSN_STORE_MEMORY + +#define WR_d INSN_WRITE_GPR_D +#define WR_t INSN_WRITE_GPR_T +#define WR_31 INSN_WRITE_GPR_31 +#define WR_D INSN_WRITE_FPR_D +#define WR_T INSN_WRITE_FPR_T +#define WR_S INSN_WRITE_FPR_S +#define RD_s INSN_READ_GPR_S +#define RD_b INSN_READ_GPR_S +#define RD_t INSN_READ_GPR_T +#define RD_S INSN_READ_FPR_S +#define RD_T INSN_READ_FPR_T +#define RD_R INSN_READ_FPR_R +#define WR_CC INSN_WRITE_COND_CODE +#define RD_CC INSN_READ_COND_CODE +#define RD_C0 INSN_COP +#define RD_C1 INSN_COP +#define RD_C2 INSN_COP +#define RD_C3 INSN_COP +#define WR_C0 INSN_COP +#define WR_C1 INSN_COP +#define WR_C2 INSN_COP +#define WR_C3 INSN_COP + +#define WR_HI INSN_WRITE_HI +#define RD_HI INSN_READ_HI +#define MOD_HI WR_HI|RD_HI + +#define WR_LO INSN_WRITE_LO +#define RD_LO INSN_READ_LO +#define MOD_LO WR_LO|RD_LO + +#define WR_HILO WR_HI|WR_LO +#define RD_HILO RD_HI|RD_LO +#define MOD_HILO WR_HILO|RD_HILO + +#define IS_M INSN_MULT + +#define WR_MACC INSN2_WRITE_MDMX_ACC +#define RD_MACC INSN2_READ_MDMX_ACC + +#define I1 INSN_ISA1 +#define I2 INSN_ISA2 +#define I3 INSN_ISA3 +#define I4 INSN_ISA4 +#define I5 INSN_ISA5 +#define I32 INSN_ISA32 +#define I64 INSN_ISA64 +#define I33 INSN_ISA32R2 +#define I65 INSN_ISA64R2 +#define I32R6 INSN_ISA32R6 +#define I64R6 INSN_ISA64R6 + +/* MIPS64 MIPS-3D ASE support. */ +#define I16 INSN_MIPS16 + +/* MIPS32 SmartMIPS ASE support. */ +#define SMT INSN_SMARTMIPS + +/* MIPS64 MIPS-3D ASE support. */ +#define M3D INSN_MIPS3D + +/* MIPS64 MDMX ASE support. */ +#define MX INSN_MDMX + +#define IL2E (INSN_LOONGSON_2E) +#define IL2F (INSN_LOONGSON_2F) + +#define P3 INSN_4650 +#define L1 INSN_4010 +#define V1 (INSN_4100 | INSN_4111 | INSN_4120) +#define T3 INSN_3900 +#define M1 INSN_10000 +#define SB1 INSN_SB1 +#define N411 INSN_4111 +#define N412 INSN_4120 +#define N5 (INSN_5400 | INSN_5500) +#define N54 INSN_5400 +#define N55 INSN_5500 + +#define G1 (T3 \ + ) + +#define G2 (T3 \ + ) + +#define G3 (I4 \ + ) + +/* MIPS DSP ASE support. + NOTE: + 1. MIPS DSP ASE includes 4 accumulators ($ac0 - $ac3). $ac0 is the pair + of original HI and LO. $ac1, $ac2 and $ac3 are new registers, and have + the same structure as $ac0 (HI + LO). For DSP instructions that write or + read accumulators (that may be $ac0), we add WR_a (WR_HILO) or RD_a + (RD_HILO) attributes, such that HILO dependencies are maintained + conservatively. + + 2. For some mul. instructions that use integer registers as destinations + but destroy HI+LO as side-effect, we add WR_HILO to their attributes. + + 3. MIPS DSP ASE includes a new DSP control register, which has 6 fields + (ccond, outflag, EFI, c, scount, pos). Many DSP instructions read or write + certain fields of the DSP control register. For simplicity, we decide not + to track dependencies of these fields. + However, "bposge32" is a branch instruction that depends on the "pos" + field. In order to make sure that GAS does not reorder DSP instructions + that writes the "pos" field and "bposge32", we add DSP_VOLA (INSN_TRAP) + attribute to those instructions that write the "pos" field. */ + +#define WR_a WR_HILO /* Write dsp accumulators (reuse WR_HILO) */ +#define RD_a RD_HILO /* Read dsp accumulators (reuse RD_HILO) */ +#define MOD_a WR_a|RD_a +#define DSP_VOLA INSN_TRAP +#define D32 INSN_DSP +#define D33 INSN_DSPR2 +#define D64 INSN_DSP64 + +/* MIPS MT ASE support. */ +#define MT32 INSN_MT + +/* MSA */ +#define MSA INSN_MSA +#define MSA64 INSN_MSA64 +#define WR_VD INSN_WRITE_FPR_D /* Reuse INSN_WRITE_FPR_D */ +#define RD_VD WR_VD /* Reuse WR_VD */ +#define RD_VT INSN_READ_FPR_T /* Reuse INSN_READ_FPR_T */ +#define RD_VS INSN_READ_FPR_S /* Reuse INSN_READ_FPR_S */ +#define RD_d INSN2_READ_GPR_D /* Reuse INSN2_READ_GPR_D */ + +#define RD_rd6 0 + +/* The order of overloaded instructions matters. Label arguments and + register arguments look the same. Instructions that can have either + for arguments must apear in the correct order in this table for the + assembler to pick the right one. In other words, entries with + immediate operands must apear after the same instruction with + registers. + + Because of the lookup algorithm used, entries with the same opcode + name must be contiguous. + + Many instructions are short hand for other instructions (i.e., The + jal instruction is short for jalr ). */ + +const struct mips_opcode mips_builtin_opcodes[] = +{ +/* These instructions appear first so that the disassembler will find + them first. The assemblers uses a hash table based on the + instruction name anyhow. */ +/* name, args, match, mask, pinfo, membership */ +{"lwpc", "s,+o2", 0xec080000, 0xfc180000, WR_d, 0, I32R6}, +{"lwupc", "s,+o2", 0xec100000, 0xfc180000, WR_d, 0, I64R6}, +{"ldpc", "s,+o1", 0xec180000, 0xfc1c0000, WR_d, 0, I64R6}, +{"addiupc", "s,+o2", 0xec000000, 0xfc180000, WR_d, 0, I32R6}, +{"auipc", "s,u", 0xec1e0000, 0xfc1f0000, WR_d, 0, I32R6}, +{"aluipc", "s,u", 0xec1f0000, 0xfc1f0000, WR_d, 0, I32R6}, +{"daui", "s,t,u", 0x74000000, 0xfc000000, RD_s|WR_t, 0, I64R6}, +{"dahi", "s,u", 0x04060000, 0xfc1f0000, RD_s, 0, I64R6}, +{"dati", "s,u", 0x041e0000, 0xfc1f0000, RD_s, 0, I64R6}, +{"lsa", "d,s,t", 0x00000005, 0xfc00073f, WR_d|RD_s|RD_t, 0, I32R6}, +{"dlsa", "d,s,t", 0x00000015, 0xfc00073f, WR_d|RD_s|RD_t, 0, I64R6}, +{"clz", "U,s", 0x00000050, 0xfc1f07ff, WR_d|RD_s, 0, I32R6}, +{"clo", "U,s", 0x00000051, 0xfc1f07ff, WR_d|RD_s, 0, I32R6}, +{"dclz", "U,s", 0x00000052, 0xfc1f07ff, WR_d|RD_s, 0, I64R6}, +{"dclo", "U,s", 0x00000053, 0xfc1f07ff, WR_d|RD_s, 0, I64R6}, +{"sdbbp", "B", 0x0000000e, 0xfc00003f, TRAP, 0, I32R6}, +{"mul", "d,s,t", 0x00000098, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"muh", "d,s,t", 0x000000d8, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"mulu", "d,s,t", 0x00000099, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"muhu", "d,s,t", 0x000000d9, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"div", "d,s,t", 0x0000009a, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"mod", "d,s,t", 0x000000da, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"divu", "d,s,t", 0x0000009b, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"modu", "d,s,t", 0x000000db, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"dmul", "d,s,t", 0x0000009c, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmuh", "d,s,t", 0x000000dc, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmulu", "d,s,t", 0x0000009d, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmuhu", "d,s,t", 0x000000dd, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"ddiv", "d,s,t", 0x0000009e, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmod", "d,s,t", 0x000000de, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"ddivu", "d,s,t", 0x0000009f, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmodu", "d,s,t", 0x000000df, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"ll", "t,o(b)", 0x7c000036, 0xfc00007f, LDD|RD_b|WR_t, 0, I32R6}, +{"sc", "t,o(b)", 0x7c000026, 0xfc00007f, LDD|RD_b|WR_t, 0, I32R6}, +{"lld", "t,o(b)", 0x7c000037, 0xfc00007f, LDD|RD_b|WR_t, 0, I64R6}, +{"scd", "t,o(b)", 0x7c000027, 0xfc00007f, LDD|RD_b|WR_t, 0, I64R6}, +{"pref", "h,o(b)", 0x7c000035, 0xfc00007f, RD_b, 0, I32R6}, +{"cache", "k,o(b)", 0x7c000025, 0xfc00007f, RD_b, 0, I32R6}, +{"seleqz", "d,v,t", 0x00000035, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"selnez", "d,v,t", 0x00000037, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"maddf.s", "D,S,T", 0x46000018, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"maddf.d", "D,S,T", 0x46200018, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"msubf.s", "D,S,T", 0x46000019, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"msubf.d", "D,S,T", 0x46200019, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"max.s", "D,S,T", 0x4600001e, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"max.d", "D,S,T", 0x4620001e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"maxa.s", "D,S,T", 0x4600001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"maxa.d", "D,S,T", 0x4620001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"rint.s", "D,S", 0x4600001a, 0xffff003f, WR_D|RD_S|FP_S, 0, I32R6}, +{"rint.d", "D,S", 0x4620001a, 0xffff003f, WR_D|RD_S|FP_D, 0, I32R6}, +{"class.s", "D,S", 0x4600001b, 0xffff003f, WR_D|RD_S|FP_S, 0, I32R6}, +{"class.d", "D,S", 0x4620001b, 0xffff003f, WR_D|RD_S|FP_D, 0, I32R6}, +{"min.s", "D,S,T", 0x4600001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"min.d", "D,S,T", 0x4620001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"mina.s", "D,S,T", 0x4600001d, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"mina.d", "D,S,T", 0x4620001d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"sel.s", "D,S,T", 0x46000010, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"sel.d", "D,S,T", 0x46200010, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"seleqz.s", "D,S,T", 0x46000014, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"seleqz.d", "D,S,T", 0x46200014, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"selnez.s", "D,S,T", 0x46000017, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"selnez.d", "D,S,T", 0x46200017, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"align", "d,v,t", 0x7c000220, 0xfc00073f, WR_d|RD_s|RD_t, 0, I32R6}, +{"dalign", "d,v,t", 0x7c000224, 0xfc00063f, WR_d|RD_s|RD_t, 0, I64R6}, +{"bitswap", "d,w", 0x7c000020, 0xffe007ff, WR_d|RD_t, 0, I32R6}, +{"dbitswap","d,w", 0x7c000024, 0xffe007ff, WR_d|RD_t, 0, I64R6}, +{"balc", "+p", 0xe8000000, 0xfc000000, UBD|WR_31, 0, I32R6}, +{"bc", "+p", 0xc8000000, 0xfc000000, UBD|WR_31, 0, I32R6}, +{"jic", "t,o", 0xd8000000, 0xffe00000, UBD|RD_t, 0, I32R6}, +{"beqzc", "s,+p", 0xd8000000, 0xfc000000, CBD|RD_s, 0, I32R6}, +{"jialc", "t,o", 0xf8000000, 0xffe00000, UBD|RD_t, 0, I32R6}, +{"bnezc", "s,+p", 0xf8000000, 0xfc000000, CBD|RD_s, 0, I32R6}, +{"beqzalc", "s,t,p", 0x20000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bovc", "s,t,p", 0x20000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"beqc", "s,t,p", 0x20000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bnezalc", "s,t,p", 0x60000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bnvc", "s,t,p", 0x60000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bnec", "s,t,p", 0x60000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"blezc", "s,t,p", 0x58000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgezc", "s,t,p", 0x58000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgec", "s,t,p", 0x58000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgtzc", "s,t,p", 0x5c000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bltzc", "s,t,p", 0x5c000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bltc", "s,t,p", 0x5c000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"blezalc", "s,t,p", 0x18000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgezalc", "s,t,p", 0x18000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgeuc", "s,t,p", 0x18000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgtzalc", "s,t,p", 0x1c000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bltzalc", "s,t,p", 0x1c000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bltuc", "s,t,p", 0x1c000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"nal", "p", 0x04100000, 0xffff0000, WR_31, 0, I32R6}, +{"bal", "p", 0x04110000, 0xffff0000, UBD|WR_31, 0, I32R6}, +{"bc1eqz", "T,p", 0x45200000, 0xffe00000, CBD|RD_T|FP_S|FP_D, 0, I32R6}, +{"bc1nez", "T,p", 0x45a00000, 0xffe00000, CBD|RD_T|FP_S|FP_D, 0, I32R6}, +{"bc2eqz", "E,p", 0x49200000, 0xffe00000, CBD|RD_C2, 0, I32R6}, +{"bc2nez", "E,p", 0x49a00000, 0xffe00000, CBD|RD_C2, 0, I32R6}, +{"cmp.af.s", "D,S,T", 0x46800000, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.un.s", "D,S,T", 0x46800001, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.eq.s", "D,S,T", 0x46800002, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.ueq.s", "D,S,T", 0x46800003, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.lt.s", "D,S,T", 0x46800004, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.ult.s", "D,S,T", 0x46800005, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.le.s", "D,S,T", 0x46800006, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.ule.s", "D,S,T", 0x46800007, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.saf.s", "D,S,T", 0x46800008, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sun.s", "D,S,T", 0x46800009, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.seq.s", "D,S,T", 0x4680000a, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sueq.s", "D,S,T", 0x4680000b, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.slt.s", "D,S,T", 0x4680000c, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sult.s", "D,S,T", 0x4680000d, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sle.s", "D,S,T", 0x4680000e, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sule.s", "D,S,T", 0x4680000f, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.or.s", "D,S,T", 0x46800011, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.une.s", "D,S,T", 0x46800012, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.ne.s", "D,S,T", 0x46800013, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sor.s", "D,S,T", 0x46800019, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sune.s", "D,S,T", 0x4680001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sne.s", "D,S,T", 0x4680001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.af.d", "D,S,T", 0x46a00000, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.un.d", "D,S,T", 0x46a00001, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.eq.d", "D,S,T", 0x46a00002, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.ueq.d", "D,S,T", 0x46a00003, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.lt.d", "D,S,T", 0x46a00004, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.ult.d", "D,S,T", 0x46a00005, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.le.d", "D,S,T", 0x46a00006, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.ule.d", "D,S,T", 0x46a00007, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.saf.d", "D,S,T", 0x46a00008, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sun.d", "D,S,T", 0x46a00009, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.seq.d", "D,S,T", 0x46a0000a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sueq.d", "D,S,T", 0x46a0000b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.slt.d", "D,S,T", 0x46a0000c, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sult.d", "D,S,T", 0x46a0000d, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sle.d", "D,S,T", 0x46a0000e, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sule.d", "D,S,T", 0x46a0000f, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.or.d", "D,S,T", 0x46a00011, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.une.d", "D,S,T", 0x46a00012, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.ne.d", "D,S,T", 0x46a00013, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sor.d", "D,S,T", 0x46a00019, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sune.d", "D,S,T", 0x46a0001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sne.d", "D,S,T", 0x46a0001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, + +/* MSA */ +{"sll.b", "+d,+e,+f", 0x7800000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sll.h", "+d,+e,+f", 0x7820000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sll.w", "+d,+e,+f", 0x7840000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sll.d", "+d,+e,+f", 0x7860000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"slli.b", "+d,+e,+7", 0x78700009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"slli.h", "+d,+e,+8", 0x78600009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"slli.w", "+d,+e,+9", 0x78400009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"slli.d", "+d,+e,'", 0x78000009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"sra.b", "+d,+e,+f", 0x7880000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sra.h", "+d,+e,+f", 0x78a0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sra.w", "+d,+e,+f", 0x78c0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sra.d", "+d,+e,+f", 0x78e0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srai.b", "+d,+e,+7", 0x78f00009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"srai.h", "+d,+e,+8", 0x78e00009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"srai.w", "+d,+e,+9", 0x78c00009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"srai.d", "+d,+e,'", 0x78800009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"srl.b", "+d,+e,+f", 0x7900000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srl.h", "+d,+e,+f", 0x7920000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srl.w", "+d,+e,+f", 0x7940000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srl.d", "+d,+e,+f", 0x7960000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srli.b", "+d,+e,+7", 0x79700009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"srli.h", "+d,+e,+8", 0x79600009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"srli.w", "+d,+e,+9", 0x79400009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"srli.d", "+d,+e,'", 0x79000009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"bclr.b", "+d,+e,+f", 0x7980000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bclr.h", "+d,+e,+f", 0x79a0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bclr.w", "+d,+e,+f", 0x79c0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bclr.d", "+d,+e,+f", 0x79e0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bclri.b", "+d,+e,+7", 0x79f00009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"bclri.h", "+d,+e,+8", 0x79e00009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"bclri.w", "+d,+e,+9", 0x79c00009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"bclri.d", "+d,+e,'", 0x79800009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"bset.b", "+d,+e,+f", 0x7a00000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bset.h", "+d,+e,+f", 0x7a20000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bset.w", "+d,+e,+f", 0x7a40000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bset.d", "+d,+e,+f", 0x7a60000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bseti.b", "+d,+e,+7", 0x7a700009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"bseti.h", "+d,+e,+8", 0x7a600009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"bseti.w", "+d,+e,+9", 0x7a400009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"bseti.d", "+d,+e,'", 0x7a000009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"bneg.b", "+d,+e,+f", 0x7a80000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bneg.h", "+d,+e,+f", 0x7aa0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bneg.w", "+d,+e,+f", 0x7ac0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bneg.d", "+d,+e,+f", 0x7ae0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bnegi.b", "+d,+e,+7", 0x7af00009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"bnegi.h", "+d,+e,+8", 0x7ae00009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"bnegi.w", "+d,+e,+9", 0x7ac00009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"bnegi.d", "+d,+e,'", 0x7a800009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"binsl.b", "+d,+e,+f", 0x7b00000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsl.h", "+d,+e,+f", 0x7b20000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsl.w", "+d,+e,+f", 0x7b40000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsl.d", "+d,+e,+f", 0x7b60000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsli.b", "+d,+e,+7", 0x7b700009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"binsli.h", "+d,+e,+8", 0x7b600009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"binsli.w", "+d,+e,+9", 0x7b400009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"binsli.d", "+d,+e,'", 0x7b000009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"binsr.b", "+d,+e,+f", 0x7b80000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsr.h", "+d,+e,+f", 0x7ba0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsr.w", "+d,+e,+f", 0x7bc0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsr.d", "+d,+e,+f", 0x7be0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsri.b", "+d,+e,+7", 0x7bf00009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"binsri.h", "+d,+e,+8", 0x7be00009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"binsri.w", "+d,+e,+9", 0x7bc00009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"binsri.d", "+d,+e,'", 0x7b800009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"addv.b", "+d,+e,+f", 0x7800000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"addv.h", "+d,+e,+f", 0x7820000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"addv.w", "+d,+e,+f", 0x7840000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"addv.d", "+d,+e,+f", 0x7860000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"addvi.b", "+d,+e,k", 0x78000006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"addvi.h", "+d,+e,k", 0x78200006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"addvi.w", "+d,+e,k", 0x78400006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"addvi.d", "+d,+e,k", 0x78600006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"subv.b", "+d,+e,+f", 0x7880000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subv.h", "+d,+e,+f", 0x78a0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subv.w", "+d,+e,+f", 0x78c0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subv.d", "+d,+e,+f", 0x78e0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subvi.b", "+d,+e,k", 0x78800006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"subvi.h", "+d,+e,k", 0x78a00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"subvi.w", "+d,+e,k", 0x78c00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"subvi.d", "+d,+e,k", 0x78e00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"max_s.b", "+d,+e,+f", 0x7900000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_s.h", "+d,+e,+f", 0x7920000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_s.w", "+d,+e,+f", 0x7940000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_s.d", "+d,+e,+f", 0x7960000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maxi_s.b", "+d,+e,+5", 0x79000006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_s.h", "+d,+e,+5", 0x79200006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_s.w", "+d,+e,+5", 0x79400006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_s.d", "+d,+e,+5", 0x79600006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"max_u.b", "+d,+e,+f", 0x7980000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_u.h", "+d,+e,+f", 0x79a0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_u.w", "+d,+e,+f", 0x79c0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_u.d", "+d,+e,+f", 0x79e0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maxi_u.b", "+d,+e,k", 0x79800006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_u.h", "+d,+e,k", 0x79a00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_u.w", "+d,+e,k", 0x79c00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_u.d", "+d,+e,k", 0x79e00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"min_s.b", "+d,+e,+f", 0x7a00000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_s.h", "+d,+e,+f", 0x7a20000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_s.w", "+d,+e,+f", 0x7a40000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_s.d", "+d,+e,+f", 0x7a60000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mini_s.b", "+d,+e,+5", 0x7a000006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_s.h", "+d,+e,+5", 0x7a200006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_s.w", "+d,+e,+5", 0x7a400006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_s.d", "+d,+e,+5", 0x7a600006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"min_u.b", "+d,+e,+f", 0x7a80000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_u.h", "+d,+e,+f", 0x7aa0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_u.w", "+d,+e,+f", 0x7ac0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_u.d", "+d,+e,+f", 0x7ae0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mini_u.b", "+d,+e,k", 0x7a800006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_u.h", "+d,+e,k", 0x7aa00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_u.w", "+d,+e,k", 0x7ac00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_u.d", "+d,+e,k", 0x7ae00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"max_a.b", "+d,+e,+f", 0x7b00000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_a.h", "+d,+e,+f", 0x7b20000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_a.w", "+d,+e,+f", 0x7b40000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_a.d", "+d,+e,+f", 0x7b60000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_a.b", "+d,+e,+f", 0x7b80000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_a.h", "+d,+e,+f", 0x7ba0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_a.w", "+d,+e,+f", 0x7bc0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_a.d", "+d,+e,+f", 0x7be0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceq.b", "+d,+e,+f", 0x7800000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceq.h", "+d,+e,+f", 0x7820000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceq.w", "+d,+e,+f", 0x7840000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceq.d", "+d,+e,+f", 0x7860000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceqi.b", "+d,+e,+5", 0x78000007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"ceqi.h", "+d,+e,+5", 0x78200007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"ceqi.w", "+d,+e,+5", 0x78400007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"ceqi.d", "+d,+e,+5", 0x78600007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clt_s.b", "+d,+e,+f", 0x7900000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_s.h", "+d,+e,+f", 0x7920000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_s.w", "+d,+e,+f", 0x7940000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_s.d", "+d,+e,+f", 0x7960000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clti_s.b", "+d,+e,+5", 0x79000007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_s.h", "+d,+e,+5", 0x79200007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_s.w", "+d,+e,+5", 0x79400007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_s.d", "+d,+e,+5", 0x79600007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clt_u.b", "+d,+e,+f", 0x7980000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_u.h", "+d,+e,+f", 0x79a0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_u.w", "+d,+e,+f", 0x79c0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_u.d", "+d,+e,+f", 0x79e0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clti_u.b", "+d,+e,k", 0x79800007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_u.h", "+d,+e,k", 0x79a00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_u.w", "+d,+e,k", 0x79c00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_u.d", "+d,+e,k", 0x79e00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"cle_s.b", "+d,+e,+f", 0x7a00000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_s.h", "+d,+e,+f", 0x7a20000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_s.w", "+d,+e,+f", 0x7a40000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_s.d", "+d,+e,+f", 0x7a60000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clei_s.b", "+d,+e,+5", 0x7a000007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_s.h", "+d,+e,+5", 0x7a200007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_s.w", "+d,+e,+5", 0x7a400007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_s.d", "+d,+e,+5", 0x7a600007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"cle_u.b", "+d,+e,+f", 0x7a80000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_u.h", "+d,+e,+f", 0x7aa0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_u.w", "+d,+e,+f", 0x7ac0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_u.d", "+d,+e,+f", 0x7ae0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clei_u.b", "+d,+e,k", 0x7a800007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_u.h", "+d,+e,k", 0x7aa00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_u.w", "+d,+e,k", 0x7ac00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_u.d", "+d,+e,k", 0x7ae00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"ld.b", "+d,+^(d)", 0x78000020, 0xfc00003f, WR_VD|LDD, RD_d, MSA}, +{"ld.h", "+d,+#(d)", 0x78000021, 0xfc00003f, WR_VD|LDD, RD_d, MSA}, +{"ld.w", "+d,+$(d)", 0x78000022, 0xfc00003f, WR_VD|LDD, RD_d, MSA}, +{"ld.d", "+d,+%(d)", 0x78000023, 0xfc00003f, WR_VD|LDD, RD_d, MSA}, +{"st.b", "+d,+^(d)", 0x78000024, 0xfc00003f, RD_VD|SM, RD_d, MSA}, +{"st.h", "+d,+#(d)", 0x78000025, 0xfc00003f, RD_VD|SM, RD_d, MSA}, +{"st.w", "+d,+$(d)", 0x78000026, 0xfc00003f, RD_VD|SM, RD_d, MSA}, +{"st.d", "+d,+%(d)", 0x78000027, 0xfc00003f, RD_VD|SM, RD_d, MSA}, +{"sat_s.b", "+d,+e,+7", 0x7870000a, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"sat_s.h", "+d,+e,+8", 0x7860000a, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_s.w", "+d,+e,+9", 0x7840000a, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_s.d", "+d,+e,'", 0x7800000a, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_u.b", "+d,+e,+7", 0x78f0000a, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"sat_u.h", "+d,+e,+8", 0x78e0000a, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_u.w", "+d,+e,+9", 0x78c0000a, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_u.d", "+d,+e,'", 0x7880000a, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"add_a.b", "+d,+e,+f", 0x78000010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"add_a.h", "+d,+e,+f", 0x78200010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"add_a.w", "+d,+e,+f", 0x78400010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"add_a.d", "+d,+e,+f", 0x78600010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_a.b", "+d,+e,+f", 0x78800010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_a.h", "+d,+e,+f", 0x78a00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_a.w", "+d,+e,+f", 0x78c00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_a.d", "+d,+e,+f", 0x78e00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_s.b", "+d,+e,+f", 0x79000010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_s.h", "+d,+e,+f", 0x79200010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_s.w", "+d,+e,+f", 0x79400010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_s.d", "+d,+e,+f", 0x79600010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_u.b", "+d,+e,+f", 0x79800010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_u.h", "+d,+e,+f", 0x79a00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_u.w", "+d,+e,+f", 0x79c00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_u.d", "+d,+e,+f", 0x79e00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_s.b", "+d,+e,+f", 0x7a000010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_s.h", "+d,+e,+f", 0x7a200010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_s.w", "+d,+e,+f", 0x7a400010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_s.d", "+d,+e,+f", 0x7a600010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_u.b", "+d,+e,+f", 0x7a800010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_u.h", "+d,+e,+f", 0x7aa00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_u.w", "+d,+e,+f", 0x7ac00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_u.d", "+d,+e,+f", 0x7ae00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_s.b", "+d,+e,+f", 0x7b000010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_s.h", "+d,+e,+f", 0x7b200010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_s.w", "+d,+e,+f", 0x7b400010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_s.d", "+d,+e,+f", 0x7b600010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_u.b", "+d,+e,+f", 0x7b800010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_u.h", "+d,+e,+f", 0x7ba00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_u.w", "+d,+e,+f", 0x7bc00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_u.d", "+d,+e,+f", 0x7be00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_s.b", "+d,+e,+f", 0x78000011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_s.h", "+d,+e,+f", 0x78200011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_s.w", "+d,+e,+f", 0x78400011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_s.d", "+d,+e,+f", 0x78600011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_u.b", "+d,+e,+f", 0x78800011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_u.h", "+d,+e,+f", 0x78a00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_u.w", "+d,+e,+f", 0x78c00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_u.d", "+d,+e,+f", 0x78e00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsus_u.b", "+d,+e,+f", 0x79000011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsus_u.h", "+d,+e,+f", 0x79200011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsus_u.w", "+d,+e,+f", 0x79400011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsus_u.d", "+d,+e,+f", 0x79600011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsuu_s.b", "+d,+e,+f", 0x79800011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsuu_s.h", "+d,+e,+f", 0x79a00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsuu_s.w", "+d,+e,+f", 0x79c00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsuu_s.d", "+d,+e,+f", 0x79e00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_s.b", "+d,+e,+f", 0x7a000011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_s.h", "+d,+e,+f", 0x7a200011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_s.w", "+d,+e,+f", 0x7a400011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_s.d", "+d,+e,+f", 0x7a600011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_u.b", "+d,+e,+f", 0x7a800011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_u.h", "+d,+e,+f", 0x7aa00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_u.w", "+d,+e,+f", 0x7ac00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_u.d", "+d,+e,+f", 0x7ae00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulv.b", "+d,+e,+f", 0x78000012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulv.h", "+d,+e,+f", 0x78200012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulv.w", "+d,+e,+f", 0x78400012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulv.d", "+d,+e,+f", 0x78600012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddv.b", "+d,+e,+f", 0x78800012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddv.h", "+d,+e,+f", 0x78a00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddv.w", "+d,+e,+f", 0x78c00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddv.d", "+d,+e,+f", 0x78e00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubv.b", "+d,+e,+f", 0x79000012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubv.h", "+d,+e,+f", 0x79200012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubv.w", "+d,+e,+f", 0x79400012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubv.d", "+d,+e,+f", 0x79600012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_s.b", "+d,+e,+f", 0x7a000012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_s.h", "+d,+e,+f", 0x7a200012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_s.w", "+d,+e,+f", 0x7a400012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_s.d", "+d,+e,+f", 0x7a600012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_u.b", "+d,+e,+f", 0x7a800012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_u.h", "+d,+e,+f", 0x7aa00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_u.w", "+d,+e,+f", 0x7ac00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_u.d", "+d,+e,+f", 0x7ae00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_s.b", "+d,+e,+f", 0x7b000012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_s.h", "+d,+e,+f", 0x7b200012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_s.w", "+d,+e,+f", 0x7b400012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_s.d", "+d,+e,+f", 0x7b600012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_u.b", "+d,+e,+f", 0x7b800012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_u.h", "+d,+e,+f", 0x7ba00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_u.w", "+d,+e,+f", 0x7bc00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_u.d", "+d,+e,+f", 0x7be00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_s.h", "+d,+e,+f", 0x78200013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_s.w", "+d,+e,+f", 0x78400013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_s.d", "+d,+e,+f", 0x78600013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_u.h", "+d,+e,+f", 0x78a00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_u.w", "+d,+e,+f", 0x78c00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_u.d", "+d,+e,+f", 0x78e00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_s.h", "+d,+e,+f", 0x79200013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_s.w", "+d,+e,+f", 0x79400013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_s.d", "+d,+e,+f", 0x79600013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_u.h", "+d,+e,+f", 0x79a00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_u.w", "+d,+e,+f", 0x79c00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_u.d", "+d,+e,+f", 0x79e00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_s.h", "+d,+e,+f", 0x7a200013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_s.w", "+d,+e,+f", 0x7a400013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_s.d", "+d,+e,+f", 0x7a600013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_u.h", "+d,+e,+f", 0x7aa00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_u.w", "+d,+e,+f", 0x7ac00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_u.d", "+d,+e,+f", 0x7ae00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sld.b", "+d,+e[t]", 0x78000014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"sld.h", "+d,+e[t]", 0x78200014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"sld.w", "+d,+e[t]", 0x78400014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"sld.d", "+d,+e[t]", 0x78600014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"sldi.b", "+d,+e[+9]", 0x78000019, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"sldi.h", "+d,+e[+8]", 0x78200019, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"sldi.w", "+d,+e[+7]", 0x78300019, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"sldi.d", "+d,+e[+6]", 0x78380019, 0xfffc003f, WR_VD|RD_VS, 0, MSA}, +{"splat.b", "+d,+e[t]", 0x78800014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"splat.h", "+d,+e[t]", 0x78a00014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"splat.w", "+d,+e[t]", 0x78c00014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"splat.d", "+d,+e[t]", 0x78e00014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"splati.b", "+d,+e[+9]", 0x78400019, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"splati.h", "+d,+e[+8]", 0x78600019, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"splati.w", "+d,+e[+7]", 0x78700019, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"splati.d", "+d,+e[+6]", 0x78780019, 0xfffc003f, WR_VD|RD_VS, 0, MSA}, +{"pckev.b", "+d,+e,+f", 0x79000014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckev.h", "+d,+e,+f", 0x79200014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckev.w", "+d,+e,+f", 0x79400014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckev.d", "+d,+e,+f", 0x79600014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckod.b", "+d,+e,+f", 0x79800014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckod.h", "+d,+e,+f", 0x79a00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckod.w", "+d,+e,+f", 0x79c00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckod.d", "+d,+e,+f", 0x79e00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvl.b", "+d,+e,+f", 0x7a000014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvl.h", "+d,+e,+f", 0x7a200014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvl.w", "+d,+e,+f", 0x7a400014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvl.d", "+d,+e,+f", 0x7a600014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvr.b", "+d,+e,+f", 0x7a800014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvr.h", "+d,+e,+f", 0x7aa00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvr.w", "+d,+e,+f", 0x7ac00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvr.d", "+d,+e,+f", 0x7ae00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvev.b", "+d,+e,+f", 0x7b000014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvev.h", "+d,+e,+f", 0x7b200014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvev.w", "+d,+e,+f", 0x7b400014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvev.d", "+d,+e,+f", 0x7b600014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvod.b", "+d,+e,+f", 0x7b800014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvod.h", "+d,+e,+f", 0x7ba00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvod.w", "+d,+e,+f", 0x7bc00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvod.d", "+d,+e,+f", 0x7be00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"vshf.b", "+d,+e,+f", 0x78000015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"vshf.h", "+d,+e,+f", 0x78200015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"vshf.w", "+d,+e,+f", 0x78400015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"vshf.d", "+d,+e,+f", 0x78600015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srar.b", "+d,+e,+f", 0x78800015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srar.h", "+d,+e,+f", 0x78a00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srar.w", "+d,+e,+f", 0x78c00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srar.d", "+d,+e,+f", 0x78e00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srari.b", "+d,+e,+7", 0x7970000a, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"srari.h", "+d,+e,+8", 0x7960000a, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"srari.w", "+d,+e,+9", 0x7940000a, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"srari.d", "+d,+e,'", 0x7900000a, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"srlr.b", "+d,+e,+f", 0x79000015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srlr.h", "+d,+e,+f", 0x79200015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srlr.w", "+d,+e,+f", 0x79400015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srlr.d", "+d,+e,+f", 0x79600015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srlri.b", "+d,+e,+7", 0x79f0000a, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"srlri.h", "+d,+e,+8", 0x79e0000a, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"srlri.w", "+d,+e,+9", 0x79c0000a, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"srlri.d", "+d,+e,'", 0x7980000a, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"hadd_s.h", "+d,+e,+f", 0x7a200015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_s.w", "+d,+e,+f", 0x7a400015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_s.d", "+d,+e,+f", 0x7a600015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_u.h", "+d,+e,+f", 0x7aa00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_u.w", "+d,+e,+f", 0x7ac00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_u.d", "+d,+e,+f", 0x7ae00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_s.h", "+d,+e,+f", 0x7b200015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_s.w", "+d,+e,+f", 0x7b400015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_s.d", "+d,+e,+f", 0x7b600015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_u.h", "+d,+e,+f", 0x7ba00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_u.w", "+d,+e,+f", 0x7bc00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_u.d", "+d,+e,+f", 0x7be00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"and.v", "+d,+e,+f", 0x7800001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"andi.b", "+d,+e,5", 0x78000000, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"or.v", "+d,+e,+f", 0x7820001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ori.b", "+d,+e,5", 0x79000000, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"nor.v", "+d,+e,+f", 0x7840001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"nori.b", "+d,+e,5", 0x7a000000, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"xor.v", "+d,+e,+f", 0x7860001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"xori.b", "+d,+e,5", 0x7b000000, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"bmnz.v", "+d,+e,+f", 0x7880001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bmnzi.b", "+d,+e,5", 0x78000001, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"bmz.v", "+d,+e,+f", 0x78a0001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bmzi.b", "+d,+e,5", 0x79000001, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"bsel.v", "+d,+e,+f", 0x78c0001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bseli.b", "+d,+e,5", 0x7a000001, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"shf.b", "+d,+e,5", 0x78000002, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"shf.h", "+d,+e,5", 0x79000002, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"shf.w", "+d,+e,5", 0x7a000002, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"bnz.v", "+f,p", 0x45e00000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.v", "+f,p", 0x45600000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"fill.b", "+d,d", 0x7b00001e, 0xffff003f, WR_VD, RD_d, MSA}, +{"fill.h", "+d,d", 0x7b01001e, 0xffff003f, WR_VD, RD_d, MSA}, +{"fill.w", "+d,d", 0x7b02001e, 0xffff003f, WR_VD, RD_d, MSA}, +{"fill.d", "+d,d", 0x7b03001e, 0xffff003f, WR_VD, RD_d, MSA64}, +{"pcnt.b", "+d,+e", 0x7b04001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"pcnt.h", "+d,+e", 0x7b05001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"pcnt.w", "+d,+e", 0x7b06001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"pcnt.d", "+d,+e", 0x7b07001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nloc.b", "+d,+e", 0x7b08001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nloc.h", "+d,+e", 0x7b09001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nloc.w", "+d,+e", 0x7b0a001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nloc.d", "+d,+e", 0x7b0b001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nlzc.b", "+d,+e", 0x7b0c001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nlzc.h", "+d,+e", 0x7b0d001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nlzc.w", "+d,+e", 0x7b0e001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nlzc.d", "+d,+e", 0x7b0f001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"copy_s.b", "+i,+e[+9]", 0x78800019, 0xffe0003f, RD_VS, RD_rd6, MSA}, +{"copy_s.h", "+i,+e[+8]", 0x78a00019, 0xfff0003f, RD_VS, RD_rd6, MSA}, +{"copy_s.w", "+i,+e[+7]", 0x78b00019, 0xfff8003f, RD_VS, RD_rd6, MSA}, +{"copy_s.d", "+i,+e[+6]", 0x78b80019, 0xfffc003f, RD_VS, RD_rd6, MSA64}, +{"copy_u.b", "+i,+e[+9]", 0x78c00019, 0xffe0003f, RD_VS, RD_rd6, MSA}, +{"copy_u.h", "+i,+e[+8]", 0x78e00019, 0xfff0003f, RD_VS, RD_rd6, MSA}, +{"copy_u.w", "+i,+e[+7]", 0x78f00019, 0xfff8003f, RD_VS, RD_rd6, MSA}, +{"copy_u.d", "+i,+e[+6]", 0x78f80019, 0xfffc003f, RD_VS, RD_rd6, MSA64}, +{"insert.b", "+d[+9],d", 0x79000019, 0xffe0003f, WR_VD|RD_VD, RD_d, MSA}, +{"insert.h", "+d[+8],d", 0x79200019, 0xfff0003f, WR_VD|RD_VD, RD_d, MSA}, +{"insert.w", "+d[+7],d", 0x79300019, 0xfff8003f, WR_VD|RD_VD, RD_d, MSA}, +{"insert.d", "+d[+6],d", 0x79380019, 0xfffc003f, WR_VD|RD_VD, RD_d, MSA64}, +{"insve.b", "+d[+9],+e[+~]", 0x79400019, 0xffe0003f, WR_VD|RD_VD|RD_VS, 0, MSA}, +{"insve.h", "+d[+8],+e[+~]", 0x79600019, 0xfff0003f, WR_VD|RD_VD|RD_VS, 0, MSA}, +{"insve.w", "+d[+7],+e[+~]", 0x79700019, 0xfff8003f, WR_VD|RD_VD|RD_VS, 0, MSA}, +{"insve.d", "+d[+6],+e[+~]", 0x79780019, 0xfffc003f, WR_VD|RD_VD|RD_VS, 0, MSA}, +{"bnz.b", "+f,p", 0x47800000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bnz.h", "+f,p", 0x47a00000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bnz.w", "+f,p", 0x47c00000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bnz.d", "+f,p", 0x47e00000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.b", "+f,p", 0x47000000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.h", "+f,p", 0x47200000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.w", "+f,p", 0x47400000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.d", "+f,p", 0x47600000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"ldi.b", "+d,+0", 0x7b000007, 0xffe0003f, WR_VD, 0, MSA}, +{"ldi.h", "+d,+0", 0x7b200007, 0xffe0003f, WR_VD, 0, MSA}, +{"ldi.w", "+d,+0", 0x7b400007, 0xffe0003f, WR_VD, 0, MSA}, +{"ldi.d", "+d,+0", 0x7b600007, 0xffe0003f, WR_VD, 0, MSA}, +{"fcaf.w", "+d,+e,+f", 0x7800001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcaf.d", "+d,+e,+f", 0x7820001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcun.w", "+d,+e,+f", 0x7840001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcun.d", "+d,+e,+f", 0x7860001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fceq.w", "+d,+e,+f", 0x7880001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fceq.d", "+d,+e,+f", 0x78a0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcueq.w", "+d,+e,+f", 0x78c0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcueq.d", "+d,+e,+f", 0x78e0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fclt.w", "+d,+e,+f", 0x7900001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fclt.d", "+d,+e,+f", 0x7920001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcult.w", "+d,+e,+f", 0x7940001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcult.d", "+d,+e,+f", 0x7960001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcle.w", "+d,+e,+f", 0x7980001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcle.d", "+d,+e,+f", 0x79a0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcule.w", "+d,+e,+f", 0x79c0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcule.d", "+d,+e,+f", 0x79e0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsaf.w", "+d,+e,+f", 0x7a00001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsaf.d", "+d,+e,+f", 0x7a20001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsun.w", "+d,+e,+f", 0x7a40001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsun.d", "+d,+e,+f", 0x7a60001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fseq.w", "+d,+e,+f", 0x7a80001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fseq.d", "+d,+e,+f", 0x7aa0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsueq.w", "+d,+e,+f", 0x7ac0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsueq.d", "+d,+e,+f", 0x7ae0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fslt.w", "+d,+e,+f", 0x7b00001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fslt.d", "+d,+e,+f", 0x7b20001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsult.w", "+d,+e,+f", 0x7b40001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsult.d", "+d,+e,+f", 0x7b60001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsle.w", "+d,+e,+f", 0x7b80001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsle.d", "+d,+e,+f", 0x7ba0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsule.w", "+d,+e,+f", 0x7bc0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsule.d", "+d,+e,+f", 0x7be0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fadd.w", "+d,+e,+f", 0x7800001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fadd.d", "+d,+e,+f", 0x7820001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsub.w", "+d,+e,+f", 0x7840001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsub.d", "+d,+e,+f", 0x7860001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmul.w", "+d,+e,+f", 0x7880001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmul.d", "+d,+e,+f", 0x78a0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fdiv.w", "+d,+e,+f", 0x78c0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fdiv.d", "+d,+e,+f", 0x78e0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmadd.w", "+d,+e,+f", 0x7900001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmadd.d", "+d,+e,+f", 0x7920001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmsub.w", "+d,+e,+f", 0x7940001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmsub.d", "+d,+e,+f", 0x7960001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fexp2.w", "+d,+e,+f", 0x79c0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fexp2.d", "+d,+e,+f", 0x79e0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fexdo.h", "+d,+e,+f", 0x7a00001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fexdo.w", "+d,+e,+f", 0x7a20001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ftq.h", "+d,+e,+f", 0x7a80001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ftq.w", "+d,+e,+f", 0x7aa0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmin.w", "+d,+e,+f", 0x7b00001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmin.d", "+d,+e,+f", 0x7b20001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmin_a.w", "+d,+e,+f", 0x7b40001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmin_a.d", "+d,+e,+f", 0x7b60001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmax.w", "+d,+e,+f", 0x7b80001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmax.d", "+d,+e,+f", 0x7ba0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmax_a.w", "+d,+e,+f", 0x7bc0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmax_a.d", "+d,+e,+f", 0x7be0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcor.w", "+d,+e,+f", 0x7840001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcor.d", "+d,+e,+f", 0x7860001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcune.w", "+d,+e,+f", 0x7880001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcune.d", "+d,+e,+f", 0x78a0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcne.w", "+d,+e,+f", 0x78c0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcne.d", "+d,+e,+f", 0x78e0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mul_q.h", "+d,+e,+f", 0x7900001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mul_q.w", "+d,+e,+f", 0x7920001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"madd_q.h", "+d,+e,+f", 0x7940001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"madd_q.w", "+d,+e,+f", 0x7960001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msub_q.h", "+d,+e,+f", 0x7980001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msub_q.w", "+d,+e,+f", 0x79a0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsor.w", "+d,+e,+f", 0x7a40001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsor.d", "+d,+e,+f", 0x7a60001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsune.w", "+d,+e,+f", 0x7a80001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsune.d", "+d,+e,+f", 0x7aa0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsne.w", "+d,+e,+f", 0x7ac0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsne.d", "+d,+e,+f", 0x7ae0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulr_q.h", "+d,+e,+f", 0x7b00001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulr_q.w", "+d,+e,+f", 0x7b20001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddr_q.h", "+d,+e,+f", 0x7b40001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddr_q.w", "+d,+e,+f", 0x7b60001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubr_q.h", "+d,+e,+f", 0x7b80001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubr_q.w", "+d,+e,+f", 0x7ba0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fclass.w", "+d,+e", 0x7b20001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fclass.d", "+d,+e", 0x7b21001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fsqrt.w", "+d,+e", 0x7b26001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fsqrt.d", "+d,+e", 0x7b27001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frsqrt.w", "+d,+e", 0x7b28001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frsqrt.d", "+d,+e", 0x7b29001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frcp.w", "+d,+e", 0x7b2a001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frcp.d", "+d,+e", 0x7b2b001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frint.w", "+d,+e", 0x7b2c001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frint.d", "+d,+e", 0x7b2d001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"flog2.w", "+d,+e", 0x7b2e001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"flog2.d", "+d,+e", 0x7b2f001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fexupl.w", "+d,+e", 0x7b30001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fexupl.d", "+d,+e", 0x7b31001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fexupr.w", "+d,+e", 0x7b32001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fexupr.d", "+d,+e", 0x7b33001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffql.w", "+d,+e", 0x7b34001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffql.d", "+d,+e", 0x7b35001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffqr.w", "+d,+e", 0x7b36001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffqr.d", "+d,+e", 0x7b37001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftint_s.w", "+d,+e", 0x7b38001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftint_s.d", "+d,+e", 0x7b39001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftint_u.w", "+d,+e", 0x7b3a001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftint_u.d", "+d,+e", 0x7b3b001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffint_s.w", "+d,+e", 0x7b3c001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffint_s.d", "+d,+e", 0x7b3d001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffint_u.w", "+d,+e", 0x7b3e001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffint_u.d", "+d,+e", 0x7b3f001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftrunc_s.w", "+d,+e", 0x7b40001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftrunc_s.d", "+d,+e", 0x7b41001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftrunc_u.w", "+d,+e", 0x7b42001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftrunc_u.d", "+d,+e", 0x7b43001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ctcmsa", "+h,d", 0x783e0019, 0xffff003f, COD, RD_d, MSA}, +{"cfcmsa", "+i,+g", 0x787e0019, 0xffff003f, COD, 0, MSA}, +{"move.v", "+d,+e", 0x78be0019, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"lsa", "d,v,t,+@", 0x00000005, 0xfc00073f, WR_d|RD_s|RD_t, 0, MSA}, +{"dlsa", "d,v,t,+@", 0x00000015, 0xfc00073f, WR_d|RD_s|RD_t, 0, MSA64}, + +{"pref", "k,o(b)", 0xcc000000, 0xfc000000, RD_b, 0, I4|I32|G3 }, +{"prefx", "h,t(b)", 0x4c00000f, 0xfc0007ff, RD_b|RD_t, 0, I4|I33 }, +{"nop", "", 0x00000000, 0xffffffff, 0, INSN2_ALIAS, I1 }, /* sll */ +{"ssnop", "", 0x00000040, 0xffffffff, 0, INSN2_ALIAS, I32|N55 }, /* sll */ +{"ehb", "", 0x000000c0, 0xffffffff, 0, INSN2_ALIAS, I33 }, /* sll */ +{"li", "t,j", 0x24000000, 0xffe00000, WR_t, INSN2_ALIAS, I1 }, /* addiu */ +{"li", "t,i", 0x34000000, 0xffe00000, WR_t, INSN2_ALIAS, I1 }, /* ori */ +{"li", "t,I", 0, (int) M_LI, INSN_MACRO, 0, I1 }, +{"move", "d,s", 0, (int) M_MOVE, INSN_MACRO, 0, I1 }, +{"move", "d,s", 0x0000002d, 0xfc1f07ff, WR_d|RD_s, INSN2_ALIAS, I3 },/* daddu */ +{"move", "d,s", 0x00000021, 0xfc1f07ff, WR_d|RD_s, INSN2_ALIAS, I1 },/* addu */ +{"move", "d,s", 0x00000025, 0xfc1f07ff, WR_d|RD_s, INSN2_ALIAS, I1 },/* or */ +{"b", "p", 0x10000000, 0xffff0000, UBD, INSN2_ALIAS, I1 },/* beq 0,0 */ +{"b", "p", 0x04010000, 0xffff0000, UBD, INSN2_ALIAS, I1 },/* bgez 0 */ +{"bal", "p", 0x04110000, 0xffff0000, UBD|WR_31, INSN2_ALIAS, I1 },/* bgezal 0*/ + +{"abs", "d,v", 0, (int) M_ABS, INSN_MACRO, 0, I1 }, +{"abs.s", "D,V", 0x46000005, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 }, +{"abs.d", "D,V", 0x46200005, 0xffff003f, WR_D|RD_S|FP_D, 0, I1 }, +{"abs.ps", "D,V", 0x46c00005, 0xffff003f, WR_D|RD_S|FP_D, 0, I5|I33 }, +{"add", "d,v,t", 0x00000020, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"add", "t,r,I", 0, (int) M_ADD_I, INSN_MACRO, 0, I1 }, +{"add.s", "D,V,T", 0x46000000, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I1 }, +{"add.d", "D,V,T", 0x46200000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I1 }, +{"add.ob", "X,Y,Q", 0x7800000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"add.ob", "D,S,T", 0x4ac0000b, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"add.ob", "D,S,T[e]", 0x4800000b, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"add.ob", "D,S,k", 0x4bc0000b, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"add.ps", "D,V,T", 0x46c00000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 }, +{"add.qh", "X,Y,Q", 0x7820000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"adda.ob", "Y,Q", 0x78000037, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"adda.qh", "Y,Q", 0x78200037, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"addi", "t,r,j", 0x20000000, 0xfc000000, WR_t|RD_s, 0, I1 }, +{"addiu", "t,r,j", 0x24000000, 0xfc000000, WR_t|RD_s, 0, I1 }, +{"addl.ob", "Y,Q", 0x78000437, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"addl.qh", "Y,Q", 0x78200437, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"addr.ps", "D,S,T", 0x46c00018, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, M3D }, +{"addu", "d,v,t", 0x00000021, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"addu", "t,r,I", 0, (int) M_ADDU_I, INSN_MACRO, 0, I1 }, +{"alni.ob", "X,Y,Z,O", 0x78000018, 0xff00003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"alni.ob", "D,S,T,%", 0x48000018, 0xff00003f, WR_D|RD_S|RD_T, 0, N54 }, +{"alni.qh", "X,Y,Z,O", 0x7800001a, 0xff00003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"alnv.ps", "D,V,T,s", 0x4c00001e, 0xfc00003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 }, +{"alnv.ob", "X,Y,Z,s", 0x78000019, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, 0, MX|SB1 }, +{"alnv.qh", "X,Y,Z,s", 0x7800001b, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, 0, MX }, +{"and", "d,v,t", 0x00000024, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"and", "t,r,I", 0, (int) M_AND_I, INSN_MACRO, 0, I1 }, +{"and.ob", "X,Y,Q", 0x7800000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"and.ob", "D,S,T", 0x4ac0000c, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"and.ob", "D,S,T[e]", 0x4800000c, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"and.ob", "D,S,k", 0x4bc0000c, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"and.qh", "X,Y,Q", 0x7820000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"andi", "t,r,i", 0x30000000, 0xfc000000, WR_t|RD_s, 0, I1 }, +/* b is at the top of the table. */ +/* bal is at the top of the table. */ +/* bc0[tf]l? are at the bottom of the table. */ +{"bc1any2f", "N,p", 0x45200000, 0xffe30000, CBD|RD_CC|FP_S, 0, M3D }, +{"bc1any2t", "N,p", 0x45210000, 0xffe30000, CBD|RD_CC|FP_S, 0, M3D }, +{"bc1any4f", "N,p", 0x45400000, 0xffe30000, CBD|RD_CC|FP_S, 0, M3D }, +{"bc1any4t", "N,p", 0x45410000, 0xffe30000, CBD|RD_CC|FP_S, 0, M3D }, +{"bc1f", "p", 0x45000000, 0xffff0000, CBD|RD_CC|FP_S, 0, I1 }, +{"bc1f", "N,p", 0x45000000, 0xffe30000, CBD|RD_CC|FP_S, 0, I4|I32 }, +{"bc1fl", "p", 0x45020000, 0xffff0000, CBL|RD_CC|FP_S, 0, I2|T3 }, +{"bc1fl", "N,p", 0x45020000, 0xffe30000, CBL|RD_CC|FP_S, 0, I4|I32 }, +{"bc1t", "p", 0x45010000, 0xffff0000, CBD|RD_CC|FP_S, 0, I1 }, +{"bc1t", "N,p", 0x45010000, 0xffe30000, CBD|RD_CC|FP_S, 0, I4|I32 }, +{"bc1tl", "p", 0x45030000, 0xffff0000, CBL|RD_CC|FP_S, 0, I2|T3 }, +{"bc1tl", "N,p", 0x45030000, 0xffe30000, CBL|RD_CC|FP_S, 0, I4|I32 }, +/* bc2* are at the bottom of the table. */ +/* bc3* are at the bottom of the table. */ +{"beqz", "s,p", 0x10000000, 0xfc1f0000, CBD|RD_s, 0, I1 }, +{"beqzl", "s,p", 0x50000000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 }, +{"beq", "s,t,p", 0x10000000, 0xfc000000, CBD|RD_s|RD_t, 0, I1 }, +{"beq", "s,I,p", 0, (int) M_BEQ_I, INSN_MACRO, 0, I1 }, +{"beql", "s,t,p", 0x50000000, 0xfc000000, CBL|RD_s|RD_t, 0, I2|T3 }, +{"beql", "s,I,p", 0, (int) M_BEQL_I, INSN_MACRO, 0, I2|T3 }, +{"bge", "s,t,p", 0, (int) M_BGE, INSN_MACRO, 0, I1 }, +{"bge", "s,I,p", 0, (int) M_BGE_I, INSN_MACRO, 0, I1 }, +{"bgel", "s,t,p", 0, (int) M_BGEL, INSN_MACRO, 0, I2|T3 }, +{"bgel", "s,I,p", 0, (int) M_BGEL_I, INSN_MACRO, 0, I2|T3 }, +{"bgeu", "s,t,p", 0, (int) M_BGEU, INSN_MACRO, 0, I1 }, +{"bgeu", "s,I,p", 0, (int) M_BGEU_I, INSN_MACRO, 0, I1 }, +{"bgeul", "s,t,p", 0, (int) M_BGEUL, INSN_MACRO, 0, I2|T3 }, +{"bgeul", "s,I,p", 0, (int) M_BGEUL_I, INSN_MACRO, 0, I2|T3 }, +{"bgez", "s,p", 0x04010000, 0xfc1f0000, CBD|RD_s, 0, I1 }, +{"bgezl", "s,p", 0x04030000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 }, +{"bgezal", "s,p", 0x04110000, 0xfc1f0000, CBD|RD_s|WR_31, 0, I1 }, +{"bgezall", "s,p", 0x04130000, 0xfc1f0000, CBL|RD_s|WR_31, 0, I2|T3 }, +{"bgt", "s,t,p", 0, (int) M_BGT, INSN_MACRO, 0, I1 }, +{"bgt", "s,I,p", 0, (int) M_BGT_I, INSN_MACRO, 0, I1 }, +{"bgtl", "s,t,p", 0, (int) M_BGTL, INSN_MACRO, 0, I2|T3 }, +{"bgtl", "s,I,p", 0, (int) M_BGTL_I, INSN_MACRO, 0, I2|T3 }, +{"bgtu", "s,t,p", 0, (int) M_BGTU, INSN_MACRO, 0, I1 }, +{"bgtu", "s,I,p", 0, (int) M_BGTU_I, INSN_MACRO, 0, I1 }, +{"bgtul", "s,t,p", 0, (int) M_BGTUL, INSN_MACRO, 0, I2|T3 }, +{"bgtul", "s,I,p", 0, (int) M_BGTUL_I, INSN_MACRO, 0, I2|T3 }, +{"bgtz", "s,p", 0x1c000000, 0xfc1f0000, CBD|RD_s, 0, I1 }, +{"bgtzl", "s,p", 0x5c000000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 }, +{"ble", "s,t,p", 0, (int) M_BLE, INSN_MACRO, 0, I1 }, +{"ble", "s,I,p", 0, (int) M_BLE_I, INSN_MACRO, 0, I1 }, +{"blel", "s,t,p", 0, (int) M_BLEL, INSN_MACRO, 0, I2|T3 }, +{"blel", "s,I,p", 0, (int) M_BLEL_I, INSN_MACRO, 0, I2|T3 }, +{"bleu", "s,t,p", 0, (int) M_BLEU, INSN_MACRO, 0, I1 }, +{"bleu", "s,I,p", 0, (int) M_BLEU_I, INSN_MACRO, 0, I1 }, +{"bleul", "s,t,p", 0, (int) M_BLEUL, INSN_MACRO, 0, I2|T3 }, +{"bleul", "s,I,p", 0, (int) M_BLEUL_I, INSN_MACRO, 0, I2|T3 }, +{"blez", "s,p", 0x18000000, 0xfc1f0000, CBD|RD_s, 0, I1 }, +{"blezl", "s,p", 0x58000000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 }, +{"blt", "s,t,p", 0, (int) M_BLT, INSN_MACRO, 0, I1 }, +{"blt", "s,I,p", 0, (int) M_BLT_I, INSN_MACRO, 0, I1 }, +{"bltl", "s,t,p", 0, (int) M_BLTL, INSN_MACRO, 0, I2|T3 }, +{"bltl", "s,I,p", 0, (int) M_BLTL_I, INSN_MACRO, 0, I2|T3 }, +{"bltu", "s,t,p", 0, (int) M_BLTU, INSN_MACRO, 0, I1 }, +{"bltu", "s,I,p", 0, (int) M_BLTU_I, INSN_MACRO, 0, I1 }, +{"bltul", "s,t,p", 0, (int) M_BLTUL, INSN_MACRO, 0, I2|T3 }, +{"bltul", "s,I,p", 0, (int) M_BLTUL_I, INSN_MACRO, 0, I2|T3 }, +{"bltz", "s,p", 0x04000000, 0xfc1f0000, CBD|RD_s, 0, I1 }, +{"bltzl", "s,p", 0x04020000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 }, +{"bltzal", "s,p", 0x04100000, 0xfc1f0000, CBD|RD_s|WR_31, 0, I1 }, +{"bltzall", "s,p", 0x04120000, 0xfc1f0000, CBL|RD_s|WR_31, 0, I2|T3 }, +{"bnez", "s,p", 0x14000000, 0xfc1f0000, CBD|RD_s, 0, I1 }, +{"bnezl", "s,p", 0x54000000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 }, +{"bne", "s,t,p", 0x14000000, 0xfc000000, CBD|RD_s|RD_t, 0, I1 }, +{"bne", "s,I,p", 0, (int) M_BNE_I, INSN_MACRO, 0, I1 }, +{"bnel", "s,t,p", 0x54000000, 0xfc000000, CBL|RD_s|RD_t, 0, I2|T3 }, +{"bnel", "s,I,p", 0, (int) M_BNEL_I, INSN_MACRO, 0, I2|T3 }, +{"break", "", 0x0000000d, 0xffffffff, TRAP, 0, I1 }, +{"break", "c", 0x0000000d, 0xfc00ffff, TRAP, 0, I1 }, +{"break", "c,q", 0x0000000d, 0xfc00003f, TRAP, 0, I1 }, +{"c.f.d", "S,T", 0x46200030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.f.d", "M,S,T", 0x46200030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.f.s", "S,T", 0x46000030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.f.s", "M,S,T", 0x46000030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.f.ps", "S,T", 0x46c00030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.f.ps", "M,S,T", 0x46c00030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.un.d", "S,T", 0x46200031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.un.d", "M,S,T", 0x46200031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.un.s", "S,T", 0x46000031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.un.s", "M,S,T", 0x46000031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.un.ps", "S,T", 0x46c00031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.un.ps", "M,S,T", 0x46c00031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.eq.d", "S,T", 0x46200032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.eq.d", "M,S,T", 0x46200032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.eq.s", "S,T", 0x46000032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.eq.s", "M,S,T", 0x46000032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.eq.ob", "Y,Q", 0x78000001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"c.eq.ob", "S,T", 0x4ac00001, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.eq.ob", "S,T[e]", 0x48000001, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.eq.ob", "S,k", 0x4bc00001, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.eq.ps", "S,T", 0x46c00032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.eq.ps", "M,S,T", 0x46c00032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.eq.qh", "Y,Q", 0x78200001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX }, +{"c.ueq.d", "S,T", 0x46200033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.ueq.d", "M,S,T", 0x46200033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.ueq.s", "S,T", 0x46000033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.ueq.s", "M,S,T", 0x46000033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.ueq.ps","S,T", 0x46c00033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ueq.ps","M,S,T", 0x46c00033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.olt.d", "S,T", 0x46200034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.olt.d", "M,S,T", 0x46200034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.olt.s", "S,T", 0x46000034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.olt.s", "M,S,T", 0x46000034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.olt.ps","S,T", 0x46c00034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.olt.ps","M,S,T", 0x46c00034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ult.d", "S,T", 0x46200035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.ult.d", "M,S,T", 0x46200035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.ult.s", "S,T", 0x46000035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.ult.s", "M,S,T", 0x46000035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.ult.ps","S,T", 0x46c00035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ult.ps","M,S,T", 0x46c00035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ole.d", "S,T", 0x46200036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.ole.d", "M,S,T", 0x46200036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.ole.s", "S,T", 0x46000036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.ole.s", "M,S,T", 0x46000036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.ole.ps","S,T", 0x46c00036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ole.ps","M,S,T", 0x46c00036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ule.d", "S,T", 0x46200037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.ule.d", "M,S,T", 0x46200037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.ule.s", "S,T", 0x46000037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.ule.s", "M,S,T", 0x46000037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.ule.ps","S,T", 0x46c00037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ule.ps","M,S,T", 0x46c00037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.sf.d", "S,T", 0x46200038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.sf.d", "M,S,T", 0x46200038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.sf.s", "S,T", 0x46000038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.sf.s", "M,S,T", 0x46000038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.sf.ps", "S,T", 0x46c00038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.sf.ps", "M,S,T", 0x46c00038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ngle.d","S,T", 0x46200039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.ngle.d","M,S,T", 0x46200039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.ngle.s","S,T", 0x46000039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.ngle.s","M,S,T", 0x46000039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.ngle.ps","S,T", 0x46c00039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ngle.ps","M,S,T", 0x46c00039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.seq.d", "S,T", 0x4620003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.seq.d", "M,S,T", 0x4620003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.seq.s", "S,T", 0x4600003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.seq.s", "M,S,T", 0x4600003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.seq.ps","S,T", 0x46c0003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.seq.ps","M,S,T", 0x46c0003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ngl.d", "S,T", 0x4620003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.ngl.d", "M,S,T", 0x4620003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.ngl.s", "S,T", 0x4600003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.ngl.s", "M,S,T", 0x4600003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.ngl.ps","S,T", 0x46c0003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ngl.ps","M,S,T", 0x46c0003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.lt.d", "S,T", 0x4620003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.lt.d", "M,S,T", 0x4620003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.lt.s", "S,T", 0x4600003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.lt.s", "M,S,T", 0x4600003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.lt.ob", "Y,Q", 0x78000004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"c.lt.ob", "S,T", 0x4ac00004, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.lt.ob", "S,T[e]", 0x48000004, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.lt.ob", "S,k", 0x4bc00004, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.lt.ps", "S,T", 0x46c0003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.lt.ps", "M,S,T", 0x46c0003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.lt.qh", "Y,Q", 0x78200004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX }, +{"c.nge.d", "S,T", 0x4620003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.nge.d", "M,S,T", 0x4620003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.nge.s", "S,T", 0x4600003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.nge.s", "M,S,T", 0x4600003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.nge.ps","S,T", 0x46c0003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.nge.ps","M,S,T", 0x46c0003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.le.d", "S,T", 0x4620003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.le.d", "M,S,T", 0x4620003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.le.s", "S,T", 0x4600003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.le.s", "M,S,T", 0x4600003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.le.ob", "Y,Q", 0x78000005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"c.le.ob", "S,T", 0x4ac00005, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.le.ob", "S,T[e]", 0x48000005, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.le.ob", "S,k", 0x4bc00005, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"c.le.ps", "S,T", 0x46c0003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.le.ps", "M,S,T", 0x46c0003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.le.qh", "Y,Q", 0x78200005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX }, +{"c.ngt.d", "S,T", 0x4620003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 }, +{"c.ngt.d", "M,S,T", 0x4620003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 }, +{"c.ngt.s", "S,T", 0x4600003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 }, +{"c.ngt.s", "M,S,T", 0x4600003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 }, +{"c.ngt.ps","S,T", 0x46c0003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"c.ngt.ps","M,S,T", 0x46c0003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 }, +{"cabs.eq.d", "M,S,T", 0x46200072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.eq.ps", "M,S,T", 0x46c00072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.eq.s", "M,S,T", 0x46000072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.f.d", "M,S,T", 0x46200070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.f.ps", "M,S,T", 0x46c00070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.f.s", "M,S,T", 0x46000070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.le.d", "M,S,T", 0x4620007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.le.ps", "M,S,T", 0x46c0007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.le.s", "M,S,T", 0x4600007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.lt.d", "M,S,T", 0x4620007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.lt.ps", "M,S,T", 0x46c0007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.lt.s", "M,S,T", 0x4600007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.nge.d", "M,S,T", 0x4620007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.nge.ps","M,S,T", 0x46c0007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.nge.s", "M,S,T", 0x4600007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.ngl.d", "M,S,T", 0x4620007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ngl.ps","M,S,T", 0x46c0007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ngl.s", "M,S,T", 0x4600007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.ngle.d","M,S,T", 0x46200079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ngle.ps","M,S,T",0x46c00079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ngle.s","M,S,T", 0x46000079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.ngt.d", "M,S,T", 0x4620007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ngt.ps","M,S,T", 0x46c0007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ngt.s", "M,S,T", 0x4600007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.ole.d", "M,S,T", 0x46200076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ole.ps","M,S,T", 0x46c00076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ole.s", "M,S,T", 0x46000076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.olt.d", "M,S,T", 0x46200074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.olt.ps","M,S,T", 0x46c00074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.olt.s", "M,S,T", 0x46000074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.seq.d", "M,S,T", 0x4620007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.seq.ps","M,S,T", 0x46c0007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.seq.s", "M,S,T", 0x4600007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.sf.d", "M,S,T", 0x46200078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.sf.ps", "M,S,T", 0x46c00078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.sf.s", "M,S,T", 0x46000078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.ueq.d", "M,S,T", 0x46200073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ueq.ps","M,S,T", 0x46c00073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ueq.s", "M,S,T", 0x46000073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.ule.d", "M,S,T", 0x46200077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ule.ps","M,S,T", 0x46c00077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ule.s", "M,S,T", 0x46000077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.ult.d", "M,S,T", 0x46200075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ult.ps","M,S,T", 0x46c00075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.ult.s", "M,S,T", 0x46000075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +{"cabs.un.d", "M,S,T", 0x46200071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.un.ps", "M,S,T", 0x46c00071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D }, +{"cabs.un.s", "M,S,T", 0x46000071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D }, +/* CW4010 instructions which are aliases for the cache instruction. */ +{"flushi", "", 0xbc010000, 0xffffffff, 0, 0, L1 }, +{"flushd", "", 0xbc020000, 0xffffffff, 0, 0, L1 }, +{"flushid", "", 0xbc030000, 0xffffffff, 0, 0, L1 }, +{"wb", "o(b)", 0xbc040000, 0xfc1f0000, SM|RD_b, 0, L1 }, +{"cache", "k,o(b)", 0xbc000000, 0xfc000000, RD_b, 0, I3|I32|T3}, +{"cache", "k,A(b)", 0, (int) M_CACHE_AB, INSN_MACRO, 0, I3|I32|T3}, +{"ceil.l.d", "D,S", 0x4620000a, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 }, +{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 }, +{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 }, +{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 }, +{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I1 }, +{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 }, +{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 }, +/* cfc2 is at the bottom of the table. */ +/* cfc3 is at the bottom of the table. */ +{"cftc1", "d,E", 0x41000023, 0xffe007ff, TRAP|LCD|WR_d|RD_C1|FP_S, 0, MT32 }, +{"cftc1", "d,T", 0x41000023, 0xffe007ff, TRAP|LCD|WR_d|RD_C1|FP_S, 0, MT32 }, +{"cftc2", "d,E", 0x41000025, 0xffe007ff, TRAP|LCD|WR_d|RD_C2, 0, MT32 }, +{"clo", "U,s", 0x70000021, 0xfc0007ff, WR_d|WR_t|RD_s, 0, I32|N55 }, +{"clz", "U,s", 0x70000020, 0xfc0007ff, WR_d|WR_t|RD_s, 0, I32|N55 }, +{"ctc0", "t,G", 0x40c00000, 0xffe007ff, COD|RD_t|WR_CC, 0, I1 }, +{"ctc1", "t,G", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, 0, I1 }, +{"ctc1", "t,S", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, 0, I1 }, +/* ctc2 is at the bottom of the table. */ +/* ctc3 is at the bottom of the table. */ +{"cttc1", "t,g", 0x41800023, 0xffe007ff, TRAP|COD|RD_t|WR_CC|FP_S, 0, MT32 }, +{"cttc1", "t,S", 0x41800023, 0xffe007ff, TRAP|COD|RD_t|WR_CC|FP_S, 0, MT32 }, +{"cttc2", "t,g", 0x41800025, 0xffe007ff, TRAP|COD|RD_t|WR_CC, 0, MT32 }, +{"cvt.d.l", "D,S", 0x46a00021, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 }, +{"cvt.d.s", "D,S", 0x46000021, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I1 }, +{"cvt.d.w", "D,S", 0x46800021, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I1 }, +{"cvt.l.d", "D,S", 0x46200025, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 }, +{"cvt.l.s", "D,S", 0x46000025, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 }, +{"cvt.s.l", "D,S", 0x46a00020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 }, +{"cvt.s.d", "D,S", 0x46200020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I1 }, +{"cvt.s.w", "D,S", 0x46800020, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 }, +{"cvt.s.pl","D,S", 0x46c00028, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I5|I33 }, +{"cvt.s.pu","D,S", 0x46c00020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I5|I33 }, +{"cvt.w.d", "D,S", 0x46200024, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I1 }, +{"cvt.w.s", "D,S", 0x46000024, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 }, +{"cvt.ps.pw", "D,S", 0x46800026, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, M3D }, +{"cvt.ps.s","D,V,T", 0x46000026, 0xffe0003f, WR_D|RD_S|RD_T|FP_S|FP_D, 0, I5|I33 }, +{"cvt.pw.ps", "D,S", 0x46c00024, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, M3D }, +{"dabs", "d,v", 0, (int) M_DABS, INSN_MACRO, 0, I3 }, +{"dadd", "d,v,t", 0x0000002c, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I3 }, +{"dadd", "t,r,I", 0, (int) M_DADD_I, INSN_MACRO, 0, I3 }, +{"daddi", "t,r,j", 0x60000000, 0xfc000000, WR_t|RD_s, 0, I3 }, +{"daddiu", "t,r,j", 0x64000000, 0xfc000000, WR_t|RD_s, 0, I3 }, +{"daddu", "d,v,t", 0x0000002d, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I3 }, +{"daddu", "t,r,I", 0, (int) M_DADDU_I, INSN_MACRO, 0, I3 }, +{"dbreak", "", 0x7000003f, 0xffffffff, 0, 0, N5 }, +{"dclo", "U,s", 0x70000025, 0xfc0007ff, RD_s|WR_d|WR_t, 0, I64|N55 }, +{"dclz", "U,s", 0x70000024, 0xfc0007ff, RD_s|WR_d|WR_t, 0, I64|N55 }, +/* dctr and dctw are used on the r5000. */ +{"dctr", "o(b)", 0xbc050000, 0xfc1f0000, RD_b, 0, I3 }, +{"dctw", "o(b)", 0xbc090000, 0xfc1f0000, RD_b, 0, I3 }, +{"deret", "", 0x4200001f, 0xffffffff, 0, 0, I32|G2 }, +{"dext", "t,r,I,+I", 0, (int) M_DEXT, INSN_MACRO, 0, I65 }, +{"dext", "t,r,+A,+C", 0x7c000003, 0xfc00003f, WR_t|RD_s, 0, I65 }, +{"dextm", "t,r,+A,+G", 0x7c000001, 0xfc00003f, WR_t|RD_s, 0, I65 }, +{"dextu", "t,r,+E,+H", 0x7c000002, 0xfc00003f, WR_t|RD_s, 0, I65 }, +/* For ddiv, see the comments about div. */ +{"ddiv", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 }, +{"ddiv", "d,v,t", 0, (int) M_DDIV_3, INSN_MACRO, 0, I3 }, +{"ddiv", "d,v,I", 0, (int) M_DDIV_3I, INSN_MACRO, 0, I3 }, +/* For ddivu, see the comments about div. */ +{"ddivu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 }, +{"ddivu", "d,v,t", 0, (int) M_DDIVU_3, INSN_MACRO, 0, I3 }, +{"ddivu", "d,v,I", 0, (int) M_DDIVU_3I, INSN_MACRO, 0, I3 }, +{"di", "", 0x41606000, 0xffffffff, WR_t|WR_C0, 0, I33 }, +{"di", "t", 0x41606000, 0xffe0ffff, WR_t|WR_C0, 0, I33 }, +{"dins", "t,r,I,+I", 0, (int) M_DINS, INSN_MACRO, 0, I65 }, +{"dins", "t,r,+A,+B", 0x7c000007, 0xfc00003f, WR_t|RD_s, 0, I65 }, +{"dinsm", "t,r,+A,+F", 0x7c000005, 0xfc00003f, WR_t|RD_s, 0, I65 }, +{"dinsu", "t,r,+E,+F", 0x7c000006, 0xfc00003f, WR_t|RD_s, 0, I65 }, +/* The MIPS assembler treats the div opcode with two operands as + though the first operand appeared twice (the first operand is both + a source and a destination). To get the div machine instruction, + you must use an explicit destination of $0. */ +{"div", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I1 }, +{"div", "z,t", 0x0000001a, 0xffe0ffff, RD_s|RD_t|WR_HILO, 0, I1 }, +{"div", "d,v,t", 0, (int) M_DIV_3, INSN_MACRO, 0, I1 }, +{"div", "d,v,I", 0, (int) M_DIV_3I, INSN_MACRO, 0, I1 }, +{"div.d", "D,V,T", 0x46200003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I1 }, +{"div.s", "D,V,T", 0x46000003, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I1 }, +{"div.ps", "D,V,T", 0x46c00003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, SB1 }, +/* For divu, see the comments about div. */ +{"divu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I1 }, +{"divu", "z,t", 0x0000001b, 0xffe0ffff, RD_s|RD_t|WR_HILO, 0, I1 }, +{"divu", "d,v,t", 0, (int) M_DIVU_3, INSN_MACRO, 0, I1 }, +{"divu", "d,v,I", 0, (int) M_DIVU_3I, INSN_MACRO, 0, I1 }, +{"dla", "t,A(b)", 0, (int) M_DLA_AB, INSN_MACRO, 0, I3 }, +{"dlca", "t,A(b)", 0, (int) M_DLCA_AB, INSN_MACRO, 0, I3 }, +{"dli", "t,j", 0x24000000, 0xffe00000, WR_t, 0, I3 }, /* addiu */ +{"dli", "t,i", 0x34000000, 0xffe00000, WR_t, 0, I3 }, /* ori */ +{"dli", "t,I", 0, (int) M_DLI, INSN_MACRO, 0, I3 }, +{"dmacc", "d,s,t", 0x00000029, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 }, +{"dmacchi", "d,s,t", 0x00000229, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 }, +{"dmacchis", "d,s,t", 0x00000629, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 }, +{"dmacchiu", "d,s,t", 0x00000269, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 }, +{"dmacchius", "d,s,t", 0x00000669, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 }, +{"dmaccs", "d,s,t", 0x00000429, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 }, +{"dmaccu", "d,s,t", 0x00000069, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 }, +{"dmaccus", "d,s,t", 0x00000469, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 }, +{"dmadd16", "s,t", 0x00000029, 0xfc00ffff, RD_s|RD_t|MOD_LO, 0, N411 }, +{"dmfc0", "t,G", 0x40200000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I3 }, +{"dmfc0", "t,+D", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I64 }, +{"dmfc0", "t,G,H", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I64 }, +{"dmt", "", 0x41600bc1, 0xffffffff, TRAP, 0, MT32 }, +{"dmt", "t", 0x41600bc1, 0xffe0ffff, TRAP|WR_t, 0, MT32 }, +{"dmtc0", "t,G", 0x40a00000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, 0, I3 }, +{"dmtc0", "t,+D", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I64 }, +{"dmtc0", "t,G,H", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I64 }, +{"dmfc1", "t,S", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_D, 0, I3 }, +{"dmfc1", "t,G", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_D, 0, I3 }, +{"dmtc1", "t,S", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_D, 0, I3 }, +{"dmtc1", "t,G", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_D, 0, I3 }, +/* dmfc2 is at the bottom of the table. */ +/* dmtc2 is at the bottom of the table. */ +/* dmfc3 is at the bottom of the table. */ +/* dmtc3 is at the bottom of the table. */ +{"dmul", "d,v,t", 0, (int) M_DMUL, INSN_MACRO, 0, I3 }, +{"dmul", "d,v,I", 0, (int) M_DMUL_I, INSN_MACRO, 0, I3 }, +{"dmulo", "d,v,t", 0, (int) M_DMULO, INSN_MACRO, 0, I3 }, +{"dmulo", "d,v,I", 0, (int) M_DMULO_I, INSN_MACRO, 0, I3 }, +{"dmulou", "d,v,t", 0, (int) M_DMULOU, INSN_MACRO, 0, I3 }, +{"dmulou", "d,v,I", 0, (int) M_DMULOU_I, INSN_MACRO, 0, I3 }, +{"dmult", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 }, +{"dmultu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 }, +{"dneg", "d,w", 0x0000002e, 0xffe007ff, WR_d|RD_t, 0, I3 }, /* dsub 0 */ +{"dnegu", "d,w", 0x0000002f, 0xffe007ff, WR_d|RD_t, 0, I3 }, /* dsubu 0*/ +{"drem", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 }, +{"drem", "d,v,t", 3, (int) M_DREM_3, INSN_MACRO, 0, I3 }, +{"drem", "d,v,I", 3, (int) M_DREM_3I, INSN_MACRO, 0, I3 }, +{"dremu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 }, +{"dremu", "d,v,t", 3, (int) M_DREMU_3, INSN_MACRO, 0, I3 }, +{"dremu", "d,v,I", 3, (int) M_DREMU_3I, INSN_MACRO, 0, I3 }, +{"dret", "", 0x7000003e, 0xffffffff, 0, 0, N5 }, +{"drol", "d,v,t", 0, (int) M_DROL, INSN_MACRO, 0, I3 }, +{"drol", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, 0, I3 }, +{"dror", "d,v,t", 0, (int) M_DROR, INSN_MACRO, 0, I3 }, +{"dror", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, 0, I3 }, +{"dror", "d,w,<", 0x0020003a, 0xffe0003f, WR_d|RD_t, 0, N5|I65 }, +{"drorv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, 0, N5|I65 }, +{"dror32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, 0, N5|I65 }, +{"drotl", "d,v,t", 0, (int) M_DROL, INSN_MACRO, 0, I65 }, +{"drotl", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, 0, I65 }, +{"drotr", "d,v,t", 0, (int) M_DROR, INSN_MACRO, 0, I65 }, +{"drotr", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, 0, I65 }, +{"drotrv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, 0, I65 }, +{"drotr32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, 0, I65 }, +{"dsbh", "d,w", 0x7c0000a4, 0xffe007ff, WR_d|RD_t, 0, I65 }, +{"dshd", "d,w", 0x7c000164, 0xffe007ff, WR_d|RD_t, 0, I65 }, +{"dsllv", "d,t,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, +{"dsll32", "d,w,<", 0x0000003c, 0xffe0003f, WR_d|RD_t, 0, I3 }, +{"dsll", "d,w,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, /* dsllv */ +{"dsll", "d,w,>", 0x0000003c, 0xffe0003f, WR_d|RD_t, 0, I3 }, /* dsll32 */ +{"dsll", "d,w,<", 0x00000038, 0xffe0003f, WR_d|RD_t, 0, I3 }, +{"dsrav", "d,t,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, +{"dsra32", "d,w,<", 0x0000003f, 0xffe0003f, WR_d|RD_t, 0, I3 }, +{"dsra", "d,w,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, /* dsrav */ +{"dsra", "d,w,>", 0x0000003f, 0xffe0003f, WR_d|RD_t, 0, I3 }, /* dsra32 */ +{"dsra", "d,w,<", 0x0000003b, 0xffe0003f, WR_d|RD_t, 0, I3 }, +{"dsrlv", "d,t,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, +{"dsrl32", "d,w,<", 0x0000003e, 0xffe0003f, WR_d|RD_t, 0, I3 }, +{"dsrl", "d,w,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, /* dsrlv */ +{"dsrl", "d,w,>", 0x0000003e, 0xffe0003f, WR_d|RD_t, 0, I3 }, /* dsrl32 */ +{"dsrl", "d,w,<", 0x0000003a, 0xffe0003f, WR_d|RD_t, 0, I3 }, +{"dsub", "d,v,t", 0x0000002e, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I3 }, +{"dsub", "d,v,I", 0, (int) M_DSUB_I, INSN_MACRO, 0, I3 }, +{"dsubu", "d,v,t", 0x0000002f, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I3 }, +{"dsubu", "d,v,I", 0, (int) M_DSUBU_I, INSN_MACRO, 0, I3 }, +{"dvpe", "", 0x41600001, 0xffffffff, TRAP, 0, MT32 }, +{"dvpe", "t", 0x41600001, 0xffe0ffff, TRAP|WR_t, 0, MT32 }, +{"ei", "", 0x41606020, 0xffffffff, WR_t|WR_C0, 0, I33 }, +{"ei", "t", 0x41606020, 0xffe0ffff, WR_t|WR_C0, 0, I33 }, +{"emt", "", 0x41600be1, 0xffffffff, TRAP, 0, MT32 }, +{"emt", "t", 0x41600be1, 0xffe0ffff, TRAP|WR_t, 0, MT32 }, +{"eret", "", 0x42000018, 0xffffffff, 0, 0, I3|I32 }, +{"evpe", "", 0x41600021, 0xffffffff, TRAP, 0, MT32 }, +{"evpe", "t", 0x41600021, 0xffe0ffff, TRAP|WR_t, 0, MT32 }, +{"ext", "t,r,+A,+C", 0x7c000000, 0xfc00003f, WR_t|RD_s, 0, I33 }, +{"floor.l.d", "D,S", 0x4620000b, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 }, +{"floor.l.s", "D,S", 0x4600000b, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 }, +{"floor.w.d", "D,S", 0x4620000f, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 }, +{"floor.w.s", "D,S", 0x4600000f, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 }, +{"hibernate","", 0x42000023, 0xffffffff, 0, 0, V1 }, +{"ins", "t,r,+A,+B", 0x7c000004, 0xfc00003f, WR_t|RD_s, 0, I33 }, +{"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, 0, I1 }, +/* jr.hb is officially MIPS{32,64}R2, but it works on R1 as jr with + the same hazard barrier effect. */ +{"jr.hb", "s", 0x00000408, 0xfc1fffff, UBD|RD_s, 0, I32 }, +{"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, 0, I1 }, /* jr */ +/* SVR4 PIC code requires special handling for j, so it must be a + macro. */ +{"j", "a", 0, (int) M_J_A, INSN_MACRO, 0, I1 }, +/* This form of j is used by the disassembler and internally by the + assembler, but will never match user input (because the line above + will match first). */ +{"j", "a", 0x08000000, 0xfc000000, UBD, 0, I1 }, +{"jalr", "s", 0x0000f809, 0xfc1fffff, UBD|RD_s|WR_d, 0, I1 }, +{"jalr", "d,s", 0x00000009, 0xfc1f07ff, UBD|RD_s|WR_d, 0, I1 }, +/* jalr.hb is officially MIPS{32,64}R2, but it works on R1 as jalr + with the same hazard barrier effect. */ +{"jalr.hb", "s", 0x0000fc09, 0xfc1fffff, UBD|RD_s|WR_d, 0, I32 }, +{"jalr.hb", "d,s", 0x00000409, 0xfc1f07ff, UBD|RD_s|WR_d, 0, I32 }, +/* SVR4 PIC code requires special handling for jal, so it must be a + macro. */ +{"jal", "d,s", 0, (int) M_JAL_2, INSN_MACRO, 0, I1 }, +{"jal", "s", 0, (int) M_JAL_1, INSN_MACRO, 0, I1 }, +{"jal", "a", 0, (int) M_JAL_A, INSN_MACRO, 0, I1 }, +/* This form of jal is used by the disassembler and internally by the + assembler, but will never match user input (because the line above + will match first). */ +{"jal", "a", 0x0c000000, 0xfc000000, UBD|WR_31, 0, I1 }, +{"jalx", "a", 0x74000000, 0xfc000000, UBD|WR_31, 0, I16 }, +{"la", "t,A(b)", 0, (int) M_LA_AB, INSN_MACRO, 0, I1 }, +{"lb", "t,o(b)", 0x80000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 }, +{"lb", "t,A(b)", 0, (int) M_LB_AB, INSN_MACRO, 0, I1 }, +{"lbu", "t,o(b)", 0x90000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 }, +{"lbu", "t,A(b)", 0, (int) M_LBU_AB, INSN_MACRO, 0, I1 }, +{"lca", "t,A(b)", 0, (int) M_LCA_AB, INSN_MACRO, 0, I1 }, +{"ld", "t,o(b)", 0xdc000000, 0xfc000000, WR_t|RD_b, 0, I3 }, +{"ld", "t,o(b)", 0, (int) M_LD_OB, INSN_MACRO, 0, I1 }, +{"ld", "t,A(b)", 0, (int) M_LD_AB, INSN_MACRO, 0, I1 }, +{"ldc1", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, 0, I2 }, +{"ldc1", "E,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, 0, I2 }, +{"ldc1", "T,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, 0, I2 }, +{"ldc1", "E,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, 0, I2 }, +{"l.d", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, 0, I2 }, /* ldc1 */ +{"l.d", "T,o(b)", 0, (int) M_L_DOB, INSN_MACRO, 0, I1 }, +{"l.d", "T,A(b)", 0, (int) M_L_DAB, INSN_MACRO, 0, I1 }, +{"ldc2", "E,o(b)", 0xd8000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I2 }, +{"ldc2", "E,A(b)", 0, (int) M_LDC2_AB, INSN_MACRO, 0, I2 }, +{"ldc3", "E,o(b)", 0xdc000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I2 }, +{"ldc3", "E,A(b)", 0, (int) M_LDC3_AB, INSN_MACRO, 0, I2 }, +{"ldl", "t,o(b)", 0x68000000, 0xfc000000, LDD|WR_t|RD_b, 0, I3 }, +{"ldl", "t,A(b)", 0, (int) M_LDL_AB, INSN_MACRO, 0, I3 }, +{"ldr", "t,o(b)", 0x6c000000, 0xfc000000, LDD|WR_t|RD_b, 0, I3 }, +{"ldr", "t,A(b)", 0, (int) M_LDR_AB, INSN_MACRO, 0, I3 }, +{"ldxc1", "D,t(b)", 0x4c000001, 0xfc00f83f, LDD|WR_D|RD_t|RD_b|FP_D, 0, I4|I33 }, +{"lh", "t,o(b)", 0x84000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 }, +{"lh", "t,A(b)", 0, (int) M_LH_AB, INSN_MACRO, 0, I1 }, +{"lhu", "t,o(b)", 0x94000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 }, +{"lhu", "t,A(b)", 0, (int) M_LHU_AB, INSN_MACRO, 0, I1 }, +/* li is at the start of the table. */ +{"li.d", "t,F", 0, (int) M_LI_D, INSN_MACRO, 0, I1 }, +{"li.d", "T,L", 0, (int) M_LI_DD, INSN_MACRO, 0, I1 }, +{"li.s", "t,f", 0, (int) M_LI_S, INSN_MACRO, 0, I1 }, +{"li.s", "T,l", 0, (int) M_LI_SS, INSN_MACRO, 0, I1 }, +{"ll", "t,o(b)", 0xc0000000, 0xfc000000, LDD|RD_b|WR_t, 0, I2 }, +{"ll", "t,A(b)", 0, (int) M_LL_AB, INSN_MACRO, 0, I2 }, +{"lld", "t,o(b)", 0xd0000000, 0xfc000000, LDD|RD_b|WR_t, 0, I3 }, +{"lld", "t,A(b)", 0, (int) M_LLD_AB, INSN_MACRO, 0, I3 }, +{"lui", "t,u", 0x3c000000, 0xffe00000, WR_t, 0, I1 }, +{"aui", "s,t,u", 0x3c000000, 0xfc000000, RD_s|WR_t, 0, I32R6}, +{"luxc1", "D,t(b)", 0x4c000005, 0xfc00f83f, LDD|WR_D|RD_t|RD_b|FP_D, 0, I5|I33|N55}, +{"lw", "t,o(b)", 0x8c000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 }, +{"lw", "t,A(b)", 0, (int) M_LW_AB, INSN_MACRO, 0, I1 }, +{"lwc0", "E,o(b)", 0xc0000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I1 }, +{"lwc0", "E,A(b)", 0, (int) M_LWC0_AB, INSN_MACRO, 0, I1 }, +{"lwc1", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, 0, I1 }, +{"lwc1", "E,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, 0, I1 }, +{"lwc1", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, 0, I1 }, +{"lwc1", "E,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, 0, I1 }, +{"l.s", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, 0, I1 }, /* lwc1 */ +{"l.s", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, 0, I1 }, +{"lwc2", "E,o(b)", 0xc8000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I1 }, +{"lwc2", "E,A(b)", 0, (int) M_LWC2_AB, INSN_MACRO, 0, I1 }, +{"lwc3", "E,o(b)", 0xcc000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I1 }, +{"lwc3", "E,A(b)", 0, (int) M_LWC3_AB, INSN_MACRO, 0, I1 }, +{"lwl", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 }, +{"lwl", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, 0, I1 }, +{"lcache", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, 0, I2 }, /* same */ +{"lcache", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, 0, I2 }, /* as lwl */ +{"lwr", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 }, +{"lwr", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, 0, I1 }, +{"flush", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, 0, I2 }, /* same */ +{"flush", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, 0, I2 }, /* as lwr */ +{"fork", "d,s,t", 0x7c000008, 0xfc0007ff, TRAP|WR_d|RD_s|RD_t, 0, MT32 }, +{"lwu", "t,o(b)", 0x9c000000, 0xfc000000, LDD|RD_b|WR_t, 0, I3 }, +{"lwu", "t,A(b)", 0, (int) M_LWU_AB, INSN_MACRO, 0, I3 }, +{"lwxc1", "D,t(b)", 0x4c000000, 0xfc00f83f, LDD|WR_D|RD_t|RD_b|FP_D, 0, I4|I33 }, +{"lwxs", "d,t(b)", 0x70000088, 0xfc0007ff, LDD|RD_b|RD_t|WR_d, 0, SMT }, +{"macc", "d,s,t", 0x00000028, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 }, +{"macc", "d,s,t", 0x00000158, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"maccs", "d,s,t", 0x00000428, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 }, +{"macchi", "d,s,t", 0x00000228, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 }, +{"macchi", "d,s,t", 0x00000358, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"macchis", "d,s,t", 0x00000628, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 }, +{"macchiu", "d,s,t", 0x00000268, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 }, +{"macchiu", "d,s,t", 0x00000359, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"macchius","d,s,t", 0x00000668, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 }, +{"maccu", "d,s,t", 0x00000068, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 }, +{"maccu", "d,s,t", 0x00000159, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"maccus", "d,s,t", 0x00000468, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 }, +{"mad", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, P3 }, +{"madu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, P3 }, +{"madd.d", "D,R,S,T", 0x4c000021, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I4|I33 }, +{"madd.s", "D,R,S,T", 0x4c000020, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, 0, I4|I33 }, +{"madd.ps", "D,R,S,T", 0x4c000026, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I5|I33 }, +{"madd", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, L1 }, +{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, I32|N55 }, +{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, 0, G1 }, +{"madd", "7,s,t", 0x70000000, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"madd", "d,s,t", 0x70000000, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, 0, G1 }, +{"maddp", "s,t", 0x70000441, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, SMT }, +{"maddu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, L1 }, +{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, I32|N55 }, +{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, 0, G1 }, +{"maddu", "7,s,t", 0x70000001, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"maddu", "d,s,t", 0x70000001, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, 0, G1 }, +{"madd16", "s,t", 0x00000028, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, N411 }, +{"max.ob", "X,Y,Q", 0x78000007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"max.ob", "D,S,T", 0x4ac00007, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"max.ob", "D,S,T[e]", 0x48000007, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"max.ob", "D,S,k", 0x4bc00007, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"max.qh", "X,Y,Q", 0x78200007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"mfpc", "t,P", 0x4000c801, 0xffe0ffc1, LCD|WR_t|RD_C0, 0, M1|N5 }, +{"mfps", "t,P", 0x4000c800, 0xffe0ffc1, LCD|WR_t|RD_C0, 0, M1|N5 }, +{"mftacx", "d", 0x41020021, 0xffff07ff, TRAP|WR_d|RD_a, 0, MT32 }, +{"mftacx", "d,*", 0x41020021, 0xfff307ff, TRAP|WR_d|RD_a, 0, MT32 }, +{"mftc0", "d,+t", 0x41000000, 0xffe007ff, TRAP|LCD|WR_d|RD_C0, 0, MT32 }, +{"mftc0", "d,+T", 0x41000000, 0xffe007f8, TRAP|LCD|WR_d|RD_C0, 0, MT32 }, +{"mftc0", "d,E,H", 0x41000000, 0xffe007f8, TRAP|LCD|WR_d|RD_C0, 0, MT32 }, +{"mftc1", "d,T", 0x41000022, 0xffe007ff, TRAP|LCD|WR_d|RD_T|FP_S, 0, MT32 }, +{"mftc1", "d,E", 0x41000022, 0xffe007ff, TRAP|LCD|WR_d|RD_T|FP_S, 0, MT32 }, +{"mftc2", "d,E", 0x41000024, 0xffe007ff, TRAP|LCD|WR_d|RD_C2, 0, MT32 }, +{"mftdsp", "d", 0x41100021, 0xffff07ff, TRAP|WR_d, 0, MT32 }, +{"mftgpr", "d,t", 0x41000020, 0xffe007ff, TRAP|WR_d|RD_t, 0, MT32 }, +{"mfthc1", "d,T", 0x41000032, 0xffe007ff, TRAP|LCD|WR_d|RD_T|FP_D, 0, MT32 }, +{"mfthc1", "d,E", 0x41000032, 0xffe007ff, TRAP|LCD|WR_d|RD_T|FP_D, 0, MT32 }, +{"mfthc2", "d,E", 0x41000034, 0xffe007ff, TRAP|LCD|WR_d|RD_C2, 0, MT32 }, +{"mfthi", "d", 0x41010021, 0xffff07ff, TRAP|WR_d|RD_a, 0, MT32 }, +{"mfthi", "d,*", 0x41010021, 0xfff307ff, TRAP|WR_d|RD_a, 0, MT32 }, +{"mftlo", "d", 0x41000021, 0xffff07ff, TRAP|WR_d|RD_a, 0, MT32 }, +{"mftlo", "d,*", 0x41000021, 0xfff307ff, TRAP|WR_d|RD_a, 0, MT32 }, +{"mftr", "d,t,!,H,$", 0x41000000, 0xffe007c8, TRAP|WR_d, 0, MT32 }, +{"mfc0", "t,G", 0x40000000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I1 }, +{"mfc0", "t,+D", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I32 }, +{"mfc0", "t,G,H", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I32 }, +{"mfc1", "t,S", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, 0, I1 }, +{"mfc1", "t,G", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, 0, I1 }, +{"mfhc1", "t,S", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_D, 0, I33 }, +{"mfhc1", "t,G", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_D, 0, I33 }, +/* mfc2 is at the bottom of the table. */ +/* mfhc2 is at the bottom of the table. */ +/* mfc3 is at the bottom of the table. */ +{"mfdr", "t,G", 0x7000003d, 0xffe007ff, LCD|WR_t|RD_C0, 0, N5 }, +{"mfhi", "d", 0x00000010, 0xffff07ff, WR_d|RD_HI, 0, I1 }, +{"mfhi", "d,9", 0x00000010, 0xff9f07ff, WR_d|RD_HI, 0, D32 }, +{"mflo", "d", 0x00000012, 0xffff07ff, WR_d|RD_LO, 0, I1 }, +{"mflo", "d,9", 0x00000012, 0xff9f07ff, WR_d|RD_LO, 0, D32 }, +{"mflhxu", "d", 0x00000052, 0xffff07ff, WR_d|MOD_HILO, 0, SMT }, +{"min.ob", "X,Y,Q", 0x78000006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"min.ob", "D,S,T", 0x4ac00006, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"min.ob", "D,S,T[e]", 0x48000006, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"min.ob", "D,S,k", 0x4bc00006, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"min.qh", "X,Y,Q", 0x78200006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"mov.d", "D,S", 0x46200006, 0xffff003f, WR_D|RD_S|FP_D, 0, I1 }, +{"mov.s", "D,S", 0x46000006, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 }, +{"mov.ps", "D,S", 0x46c00006, 0xffff003f, WR_D|RD_S|FP_D, 0, I5|I33 }, +{"movf", "d,s,N", 0x00000001, 0xfc0307ff, WR_d|RD_s|RD_CC|FP_S|FP_D, 0, I4|I32 }, +{"movf.d", "D,S,N", 0x46200011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, I4|I32 }, +{"movf.l", "D,S,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, MX|SB1 }, +{"movf.l", "X,Y,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, MX|SB1 }, +{"movf.s", "D,S,N", 0x46000011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, 0, I4|I32 }, +{"movf.ps", "D,S,N", 0x46c00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, I5|I33 }, +{"movn", "d,v,t", 0x0000000b, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I4|I32 }, +{"ffc", "d,v", 0x0000000b, 0xfc1f07ff, WR_d|RD_s, 0, L1 }, +{"movn.d", "D,S,t", 0x46200013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, I4|I32 }, +{"movn.l", "D,S,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, MX|SB1 }, +{"movn.l", "X,Y,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, MX|SB1 }, +{"movn.s", "D,S,t", 0x46000013, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, 0, I4|I32 }, +{"movn.ps", "D,S,t", 0x46c00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, I5|I33 }, +{"movt", "d,s,N", 0x00010001, 0xfc0307ff, WR_d|RD_s|RD_CC|FP_S|FP_D, 0, I4|I32 }, +{"movt.d", "D,S,N", 0x46210011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, I4|I32 }, +{"movt.l", "D,S,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, MX|SB1 }, +{"movt.l", "X,Y,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, MX|SB1 }, +{"movt.s", "D,S,N", 0x46010011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, 0, I4|I32 }, +{"movt.ps", "D,S,N", 0x46c10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, I5|I33 }, +{"movz", "d,v,t", 0x0000000a, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I4|I32 }, +{"ffs", "d,v", 0x0000000a, 0xfc1f07ff, WR_d|RD_s, 0, L1 }, +{"movz.d", "D,S,t", 0x46200012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, I4|I32 }, +{"movz.l", "D,S,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, MX|SB1 }, +{"movz.l", "X,Y,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, MX|SB1 }, +{"movz.s", "D,S,t", 0x46000012, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, 0, I4|I32 }, +{"movz.ps", "D,S,t", 0x46c00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, I5|I33 }, +{"msac", "d,s,t", 0x000001d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"msacu", "d,s,t", 0x000001d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"msachi", "d,s,t", 0x000003d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"msachiu", "d,s,t", 0x000003d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +/* move is at the top of the table. */ +{"msgn.qh", "X,Y,Q", 0x78200000, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"msub.d", "D,R,S,T", 0x4c000029, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I4|I33 }, +{"msub.s", "D,R,S,T", 0x4c000028, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, 0, I4|I33 }, +{"msub.ps", "D,R,S,T", 0x4c00002e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I5|I33 }, +{"msub", "s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, L1 }, +{"msub", "s,t", 0x70000004, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, I32|N55 }, +{"msub", "7,s,t", 0x70000004, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"msubu", "s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, L1 }, +{"msubu", "s,t", 0x70000005, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, I32|N55 }, +{"msubu", "7,s,t", 0x70000005, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"mtpc", "t,P", 0x4080c801, 0xffe0ffc1, COD|RD_t|WR_C0, 0, M1|N5 }, +{"mtps", "t,P", 0x4080c800, 0xffe0ffc1, COD|RD_t|WR_C0, 0, M1|N5 }, +{"mtc0", "t,G", 0x40800000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, 0, I1 }, +{"mtc0", "t,+D", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I32 }, +{"mtc0", "t,G,H", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I32 }, +{"mtc1", "t,S", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, 0, I1 }, +{"mtc1", "t,G", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, 0, I1 }, +{"mthc1", "t,S", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_D, 0, I33 }, +{"mthc1", "t,G", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_D, 0, I33 }, +/* mtc2 is at the bottom of the table. */ +/* mthc2 is at the bottom of the table. */ +/* mtc3 is at the bottom of the table. */ +{"mtdr", "t,G", 0x7080003d, 0xffe007ff, COD|RD_t|WR_C0, 0, N5 }, +{"mthi", "s", 0x00000011, 0xfc1fffff, RD_s|WR_HI, 0, I1 }, +{"mthi", "s,7", 0x00000011, 0xfc1fe7ff, RD_s|WR_HI, 0, D32 }, +{"mtlo", "s", 0x00000013, 0xfc1fffff, RD_s|WR_LO, 0, I1 }, +{"mtlo", "s,7", 0x00000013, 0xfc1fe7ff, RD_s|WR_LO, 0, D32 }, +{"mtlhx", "s", 0x00000053, 0xfc1fffff, RD_s|MOD_HILO, 0, SMT }, +{"mttc0", "t,G", 0x41800000, 0xffe007ff, TRAP|COD|RD_t|WR_C0|WR_CC, 0, MT32 }, +{"mttc0", "t,+D", 0x41800000, 0xffe007f8, TRAP|COD|RD_t|WR_C0|WR_CC, 0, MT32 }, +{"mttc0", "t,G,H", 0x41800000, 0xffe007f8, TRAP|COD|RD_t|WR_C0|WR_CC, 0, MT32 }, +{"mttc1", "t,S", 0x41800022, 0xffe007ff, TRAP|COD|RD_t|WR_S|FP_S, 0, MT32 }, +{"mttc1", "t,G", 0x41800022, 0xffe007ff, TRAP|COD|RD_t|WR_S|FP_S, 0, MT32 }, +{"mttc2", "t,g", 0x41800024, 0xffe007ff, TRAP|COD|RD_t|WR_C2|WR_CC, 0, MT32 }, +{"mttacx", "t", 0x41801021, 0xffe0ffff, TRAP|WR_a|RD_t, 0, MT32 }, +{"mttacx", "t,&", 0x41801021, 0xffe09fff, TRAP|WR_a|RD_t, 0, MT32 }, +{"mttdsp", "t", 0x41808021, 0xffe0ffff, TRAP|RD_t, 0, MT32 }, +{"mttgpr", "t,d", 0x41800020, 0xffe007ff, TRAP|WR_d|RD_t, 0, MT32 }, +{"mtthc1", "t,S", 0x41800032, 0xffe007ff, TRAP|COD|RD_t|WR_S|FP_D, 0, MT32 }, +{"mtthc1", "t,G", 0x41800032, 0xffe007ff, TRAP|COD|RD_t|WR_S|FP_D, 0, MT32 }, +{"mtthc2", "t,g", 0x41800034, 0xffe007ff, TRAP|COD|RD_t|WR_C2|WR_CC, 0, MT32 }, +{"mtthi", "t", 0x41800821, 0xffe0ffff, TRAP|WR_a|RD_t, 0, MT32 }, +{"mtthi", "t,&", 0x41800821, 0xffe09fff, TRAP|WR_a|RD_t, 0, MT32 }, +{"mttlo", "t", 0x41800021, 0xffe0ffff, TRAP|WR_a|RD_t, 0, MT32 }, +{"mttlo", "t,&", 0x41800021, 0xffe09fff, TRAP|WR_a|RD_t, 0, MT32 }, +{"mttr", "t,d,!,H,$", 0x41800000, 0xffe007c8, TRAP|RD_t, 0, MT32 }, +{"mul.d", "D,V,T", 0x46200002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I1 }, +{"mul.s", "D,V,T", 0x46000002, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I1 }, +{"mul.ob", "X,Y,Q", 0x78000030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"mul.ob", "D,S,T", 0x4ac00030, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"mul.ob", "D,S,T[e]", 0x48000030, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"mul.ob", "D,S,k", 0x4bc00030, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"mul.ps", "D,V,T", 0x46c00002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 }, +{"mul.qh", "X,Y,Q", 0x78200030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"mul", "d,v,t", 0x70000002, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, I32|P3|N55}, +{"mul", "d,s,t", 0x00000058, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N54 }, +{"mul", "d,v,t", 0, (int) M_MUL, INSN_MACRO, 0, I1 }, +{"mul", "d,v,I", 0, (int) M_MUL_I, INSN_MACRO, 0, I1 }, +{"mula.ob", "Y,Q", 0x78000033, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"mula.ob", "S,T", 0x4ac00033, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mula.ob", "S,T[e]", 0x48000033, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mula.ob", "S,k", 0x4bc00033, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mula.qh", "Y,Q", 0x78200033, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"mulhi", "d,s,t", 0x00000258, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"mulhiu", "d,s,t", 0x00000259, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"mull.ob", "Y,Q", 0x78000433, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"mull.ob", "S,T", 0x4ac00433, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mull.ob", "S,T[e]", 0x48000433, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mull.ob", "S,k", 0x4bc00433, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mull.qh", "Y,Q", 0x78200433, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"mulo", "d,v,t", 0, (int) M_MULO, INSN_MACRO, 0, I1 }, +{"mulo", "d,v,I", 0, (int) M_MULO_I, INSN_MACRO, 0, I1 }, +{"mulou", "d,v,t", 0, (int) M_MULOU, INSN_MACRO, 0, I1 }, +{"mulou", "d,v,I", 0, (int) M_MULOU_I, INSN_MACRO, 0, I1 }, +{"mulr.ps", "D,S,T", 0x46c0001a, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, M3D }, +{"muls", "d,s,t", 0x000000d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"mulsu", "d,s,t", 0x000000d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"mulshi", "d,s,t", 0x000002d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"mulshiu", "d,s,t", 0x000002d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"muls.ob", "Y,Q", 0x78000032, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"muls.ob", "S,T", 0x4ac00032, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"muls.ob", "S,T[e]", 0x48000032, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"muls.ob", "S,k", 0x4bc00032, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"muls.qh", "Y,Q", 0x78200032, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"mulsl.ob", "Y,Q", 0x78000432, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"mulsl.ob", "S,T", 0x4ac00432, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mulsl.ob", "S,T[e]", 0x48000432, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mulsl.ob", "S,k", 0x4bc00432, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 }, +{"mulsl.qh", "Y,Q", 0x78200432, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"mult", "s,t", 0x00000018, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, 0, I1 }, +{"mult", "7,s,t", 0x00000018, 0xfc00e7ff, WR_a|RD_s|RD_t, 0, D33 }, +{"mult", "d,s,t", 0x00000018, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, 0, G1 }, +{"multp", "s,t", 0x00000459, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, SMT }, +{"multu", "s,t", 0x00000019, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, 0, I1 }, +{"multu", "7,s,t", 0x00000019, 0xfc00e7ff, WR_a|RD_s|RD_t, 0, D33 }, +{"multu", "d,s,t", 0x00000019, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, 0, G1 }, +{"mulu", "d,s,t", 0x00000059, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 }, +{"neg", "d,w", 0x00000022, 0xffe007ff, WR_d|RD_t, 0, I1 }, /* sub 0 */ +{"negu", "d,w", 0x00000023, 0xffe007ff, WR_d|RD_t, 0, I1 }, /* subu 0 */ +{"neg.d", "D,V", 0x46200007, 0xffff003f, WR_D|RD_S|FP_D, 0, I1 }, +{"neg.s", "D,V", 0x46000007, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 }, +{"neg.ps", "D,V", 0x46c00007, 0xffff003f, WR_D|RD_S|FP_D, 0, I5|I33 }, +{"nmadd.d", "D,R,S,T", 0x4c000031, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I4|I33 }, +{"nmadd.s", "D,R,S,T", 0x4c000030, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, 0, I4|I33 }, +{"nmadd.ps","D,R,S,T", 0x4c000036, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I5|I33 }, +{"nmsub.d", "D,R,S,T", 0x4c000039, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I4|I33 }, +{"nmsub.s", "D,R,S,T", 0x4c000038, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, 0, I4|I33 }, +{"nmsub.ps","D,R,S,T", 0x4c00003e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I5|I33 }, +/* nop is at the start of the table. */ +{"nor", "d,v,t", 0x00000027, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"nor", "t,r,I", 0, (int) M_NOR_I, INSN_MACRO, 0, I1 }, +{"nor.ob", "X,Y,Q", 0x7800000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"nor.ob", "D,S,T", 0x4ac0000f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"nor.ob", "D,S,T[e]", 0x4800000f, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"nor.ob", "D,S,k", 0x4bc0000f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"nor.qh", "X,Y,Q", 0x7820000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"not", "d,v", 0x00000027, 0xfc1f07ff, WR_d|RD_s|RD_t, 0, I1 },/*nor d,s,0*/ +{"or", "d,v,t", 0x00000025, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"or", "t,r,I", 0, (int) M_OR_I, INSN_MACRO, 0, I1 }, +{"or.ob", "X,Y,Q", 0x7800000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"or.ob", "D,S,T", 0x4ac0000e, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"or.ob", "D,S,T[e]", 0x4800000e, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"or.ob", "D,S,k", 0x4bc0000e, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"or.qh", "X,Y,Q", 0x7820000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"ori", "t,r,i", 0x34000000, 0xfc000000, WR_t|RD_s, 0, I1 }, +{"pabsdiff.ob", "X,Y,Q",0x78000009, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, SB1 }, +{"pabsdiffc.ob", "Y,Q", 0x78000035, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, SB1 }, +{"pavg.ob", "X,Y,Q", 0x78000008, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, SB1 }, +{"pickf.ob", "X,Y,Q", 0x78000002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"pickf.ob", "D,S,T", 0x4ac00002, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"pickf.ob", "D,S,T[e]",0x48000002, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"pickf.ob", "D,S,k", 0x4bc00002, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"pickf.qh", "X,Y,Q", 0x78200002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"pickt.ob", "X,Y,Q", 0x78000003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"pickt.ob", "D,S,T", 0x4ac00003, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"pickt.ob", "D,S,T[e]",0x48000003, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"pickt.ob", "D,S,k", 0x4bc00003, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"pickt.qh", "X,Y,Q", 0x78200003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"pll.ps", "D,V,T", 0x46c0002c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 }, +{"plu.ps", "D,V,T", 0x46c0002d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 }, + /* pref and prefx are at the start of the table. */ +{"pul.ps", "D,V,T", 0x46c0002e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 }, +{"puu.ps", "D,V,T", 0x46c0002f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 }, +{"pperm", "s,t", 0x70000481, 0xfc00ffff, MOD_HILO|RD_s|RD_t, 0, SMT }, +{"rach.ob", "X", 0x7a00003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX|SB1 }, +{"rach.ob", "D", 0x4a00003f, 0xfffff83f, WR_D, 0, N54 }, +{"rach.qh", "X", 0x7a20003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX }, +{"racl.ob", "X", 0x7800003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX|SB1 }, +{"racl.ob", "D", 0x4800003f, 0xfffff83f, WR_D, 0, N54 }, +{"racl.qh", "X", 0x7820003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX }, +{"racm.ob", "X", 0x7900003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX|SB1 }, +{"racm.ob", "D", 0x4900003f, 0xfffff83f, WR_D, 0, N54 }, +{"racm.qh", "X", 0x7920003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX }, +{"recip.d", "D,S", 0x46200015, 0xffff003f, WR_D|RD_S|FP_D, 0, I4|I33 }, +{"recip.ps","D,S", 0x46c00015, 0xffff003f, WR_D|RD_S|FP_D, 0, SB1 }, +{"recip.s", "D,S", 0x46000015, 0xffff003f, WR_D|RD_S|FP_S, 0, I4|I33 }, +{"recip1.d", "D,S", 0x4620001d, 0xffff003f, WR_D|RD_S|FP_D, 0, M3D }, +{"recip1.ps", "D,S", 0x46c0001d, 0xffff003f, WR_D|RD_S|FP_S, 0, M3D }, +{"recip1.s", "D,S", 0x4600001d, 0xffff003f, WR_D|RD_S|FP_S, 0, M3D }, +{"recip2.d", "D,S,T", 0x4620001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, M3D }, +{"recip2.ps", "D,S,T", 0x46c0001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, M3D }, +{"recip2.s", "D,S,T", 0x4600001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, M3D }, +{"rem", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I1 }, +{"rem", "d,v,t", 0, (int) M_REM_3, INSN_MACRO, 0, I1 }, +{"rem", "d,v,I", 0, (int) M_REM_3I, INSN_MACRO, 0, I1 }, +{"remu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I1 }, +{"remu", "d,v,t", 0, (int) M_REMU_3, INSN_MACRO, 0, I1 }, +{"remu", "d,v,I", 0, (int) M_REMU_3I, INSN_MACRO, 0, I1 }, +{"rdhwr", "t,K", 0x7c00003b, 0xffe007ff, WR_t, 0, I33 }, +{"rdpgpr", "d,w", 0x41400000, 0xffe007ff, WR_d, 0, I33 }, +{"rfe", "", 0x42000010, 0xffffffff, 0, 0, I1|T3 }, +{"rnas.qh", "X,Q", 0x78200025, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX }, +{"rnau.ob", "X,Q", 0x78000021, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX|SB1 }, +{"rnau.qh", "X,Q", 0x78200021, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX }, +{"rnes.qh", "X,Q", 0x78200026, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX }, +{"rneu.ob", "X,Q", 0x78000022, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX|SB1 }, +{"rneu.qh", "X,Q", 0x78200022, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX }, +{"rol", "d,v,t", 0, (int) M_ROL, INSN_MACRO, 0, I1 }, +{"rol", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, 0, I1 }, +{"ror", "d,v,t", 0, (int) M_ROR, INSN_MACRO, 0, I1 }, +{"ror", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, 0, I1 }, +{"ror", "d,w,<", 0x00200002, 0xffe0003f, WR_d|RD_t, 0, N5|I33|SMT }, +{"rorv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, 0, N5|I33|SMT }, +{"rotl", "d,v,t", 0, (int) M_ROL, INSN_MACRO, 0, I33|SMT }, +{"rotl", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, 0, I33|SMT }, +{"rotr", "d,v,t", 0, (int) M_ROR, INSN_MACRO, 0, I33|SMT }, +{"rotr", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, 0, I33|SMT }, +{"rotrv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, 0, I33|SMT }, +{"round.l.d", "D,S", 0x46200008, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 }, +{"round.l.s", "D,S", 0x46000008, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 }, +{"round.w.d", "D,S", 0x4620000c, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 }, +{"round.w.s", "D,S", 0x4600000c, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 }, +{"rsqrt.d", "D,S", 0x46200016, 0xffff003f, WR_D|RD_S|FP_D, 0, I4|I33 }, +{"rsqrt.ps","D,S", 0x46c00016, 0xffff003f, WR_D|RD_S|FP_D, 0, SB1 }, +{"rsqrt.s", "D,S", 0x46000016, 0xffff003f, WR_D|RD_S|FP_S, 0, I4|I33 }, +{"rsqrt1.d", "D,S", 0x4620001e, 0xffff003f, WR_D|RD_S|FP_D, 0, M3D }, +{"rsqrt1.ps", "D,S", 0x46c0001e, 0xffff003f, WR_D|RD_S|FP_S, 0, M3D }, +{"rsqrt1.s", "D,S", 0x4600001e, 0xffff003f, WR_D|RD_S|FP_S, 0, M3D }, +{"rsqrt2.d", "D,S,T", 0x4620001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, M3D }, +{"rsqrt2.ps", "D,S,T", 0x46c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, M3D }, +{"rsqrt2.s", "D,S,T", 0x4600001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, M3D }, +{"rzs.qh", "X,Q", 0x78200024, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX }, +{"rzu.ob", "X,Q", 0x78000020, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX|SB1 }, +{"rzu.ob", "D,k", 0x4bc00020, 0xffe0f83f, WR_D|RD_S|RD_T, 0, N54 }, +{"rzu.qh", "X,Q", 0x78200020, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX }, +{"sb", "t,o(b)", 0xa0000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 }, +{"sb", "t,A(b)", 0, (int) M_SB_AB, INSN_MACRO, 0, I1 }, +{"sc", "t,o(b)", 0xe0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, 0, I2 }, +{"sc", "t,A(b)", 0, (int) M_SC_AB, INSN_MACRO, 0, I2 }, +{"scd", "t,o(b)", 0xf0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, 0, I3 }, +{"scd", "t,A(b)", 0, (int) M_SCD_AB, INSN_MACRO, 0, I3 }, +{"sd", "t,o(b)", 0xfc000000, 0xfc000000, SM|RD_t|RD_b, 0, I3 }, +{"sd", "t,o(b)", 0, (int) M_SD_OB, INSN_MACRO, 0, I1 }, +{"sd", "t,A(b)", 0, (int) M_SD_AB, INSN_MACRO, 0, I1 }, +{"sdbbp", "", 0x0000000e, 0xffffffff, TRAP, 0, G2 }, +{"sdbbp", "c", 0x0000000e, 0xfc00ffff, TRAP, 0, G2 }, +{"sdbbp", "c,q", 0x0000000e, 0xfc00003f, TRAP, 0, G2 }, +{"sdbbp", "", 0x7000003f, 0xffffffff, TRAP, 0, I32 }, +{"sdbbp", "B", 0x7000003f, 0xfc00003f, TRAP, 0, I32 }, +{"sdc1", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, 0, I2 }, +{"sdc1", "E,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, 0, I2 }, +{"sdc1", "T,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, 0, I2 }, +{"sdc1", "E,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, 0, I2 }, +{"sdc2", "E,o(b)", 0xf8000000, 0xfc000000, SM|RD_C2|RD_b, 0, I2 }, +{"sdc2", "E,A(b)", 0, (int) M_SDC2_AB, INSN_MACRO, 0, I2 }, +{"sdc3", "E,o(b)", 0xfc000000, 0xfc000000, SM|RD_C3|RD_b, 0, I2 }, +{"sdc3", "E,A(b)", 0, (int) M_SDC3_AB, INSN_MACRO, 0, I2 }, +{"s.d", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, 0, I2 }, +{"s.d", "T,o(b)", 0, (int) M_S_DOB, INSN_MACRO, 0, I1 }, +{"s.d", "T,A(b)", 0, (int) M_S_DAB, INSN_MACRO, 0, I1 }, +{"sdl", "t,o(b)", 0xb0000000, 0xfc000000, SM|RD_t|RD_b, 0, I3 }, +{"sdl", "t,A(b)", 0, (int) M_SDL_AB, INSN_MACRO, 0, I3 }, +{"sdr", "t,o(b)", 0xb4000000, 0xfc000000, SM|RD_t|RD_b, 0, I3 }, +{"sdr", "t,A(b)", 0, (int) M_SDR_AB, INSN_MACRO, 0, I3 }, +{"sdxc1", "S,t(b)", 0x4c000009, 0xfc0007ff, SM|RD_S|RD_t|RD_b|FP_D, 0, I4|I33 }, +{"seb", "d,w", 0x7c000420, 0xffe007ff, WR_d|RD_t, 0, I33 }, +{"seh", "d,w", 0x7c000620, 0xffe007ff, WR_d|RD_t, 0, I33 }, +{"selsl", "d,v,t", 0x00000005, 0xfc0007ff, WR_d|RD_s|RD_t, 0, L1 }, +{"selsr", "d,v,t", 0x00000001, 0xfc0007ff, WR_d|RD_s|RD_t, 0, L1 }, +{"seq", "d,v,t", 0, (int) M_SEQ, INSN_MACRO, 0, I1 }, +{"seq", "d,v,I", 0, (int) M_SEQ_I, INSN_MACRO, 0, I1 }, +{"sge", "d,v,t", 0, (int) M_SGE, INSN_MACRO, 0, I1 }, +{"sge", "d,v,I", 0, (int) M_SGE_I, INSN_MACRO, 0, I1 }, +{"sgeu", "d,v,t", 0, (int) M_SGEU, INSN_MACRO, 0, I1 }, +{"sgeu", "d,v,I", 0, (int) M_SGEU_I, INSN_MACRO, 0, I1 }, +{"sgt", "d,v,t", 0, (int) M_SGT, INSN_MACRO, 0, I1 }, +{"sgt", "d,v,I", 0, (int) M_SGT_I, INSN_MACRO, 0, I1 }, +{"sgtu", "d,v,t", 0, (int) M_SGTU, INSN_MACRO, 0, I1 }, +{"sgtu", "d,v,I", 0, (int) M_SGTU_I, INSN_MACRO, 0, I1 }, +{"sh", "t,o(b)", 0xa4000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 }, +{"sh", "t,A(b)", 0, (int) M_SH_AB, INSN_MACRO, 0, I1 }, +{"shfl.bfla.qh", "X,Y,Z", 0x7a20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"shfl.mixh.ob", "X,Y,Z", 0x7980001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"shfl.mixh.ob", "D,S,T", 0x4980001f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"shfl.mixh.qh", "X,Y,Z", 0x7820001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"shfl.mixl.ob", "X,Y,Z", 0x79c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"shfl.mixl.ob", "D,S,T", 0x49c0001f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"shfl.mixl.qh", "X,Y,Z", 0x78a0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"shfl.pach.ob", "X,Y,Z", 0x7900001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"shfl.pach.ob", "D,S,T", 0x4900001f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"shfl.pach.qh", "X,Y,Z", 0x7920001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"shfl.pacl.ob", "D,S,T", 0x4940001f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"shfl.repa.qh", "X,Y,Z", 0x7b20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"shfl.repb.qh", "X,Y,Z", 0x7ba0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"shfl.upsl.ob", "X,Y,Z", 0x78c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"sle", "d,v,t", 0, (int) M_SLE, INSN_MACRO, 0, I1 }, +{"sle", "d,v,I", 0, (int) M_SLE_I, INSN_MACRO, 0, I1 }, +{"sleu", "d,v,t", 0, (int) M_SLEU, INSN_MACRO, 0, I1 }, +{"sleu", "d,v,I", 0, (int) M_SLEU_I, INSN_MACRO, 0, I1 }, +{"sllv", "d,t,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, +{"sll", "d,w,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, /* sllv */ +{"sll", "d,w,<", 0x00000000, 0xffe0003f, WR_d|RD_t, 0, I1 }, +{"sll.ob", "X,Y,Q", 0x78000010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"sll.ob", "D,S,T[e]", 0x48000010, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"sll.ob", "D,S,k", 0x4bc00010, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"sll.qh", "X,Y,Q", 0x78200010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"slt", "d,v,t", 0x0000002a, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"slt", "d,v,I", 0, (int) M_SLT_I, INSN_MACRO, 0, I1 }, +{"slti", "t,r,j", 0x28000000, 0xfc000000, WR_t|RD_s, 0, I1 }, +{"sltiu", "t,r,j", 0x2c000000, 0xfc000000, WR_t|RD_s, 0, I1 }, +{"sltu", "d,v,t", 0x0000002b, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"sltu", "d,v,I", 0, (int) M_SLTU_I, INSN_MACRO, 0, I1 }, +{"sne", "d,v,t", 0, (int) M_SNE, INSN_MACRO, 0, I1 }, +{"sne", "d,v,I", 0, (int) M_SNE_I, INSN_MACRO, 0, I1 }, +{"sqrt.d", "D,S", 0x46200004, 0xffff003f, WR_D|RD_S|FP_D, 0, I2 }, +{"sqrt.s", "D,S", 0x46000004, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 }, +{"sqrt.ps", "D,S", 0x46c00004, 0xffff003f, WR_D|RD_S|FP_D, 0, SB1 }, +{"srav", "d,t,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, +{"sra", "d,w,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, /* srav */ +{"sra", "d,w,<", 0x00000003, 0xffe0003f, WR_d|RD_t, 0, I1 }, +{"sra.qh", "X,Y,Q", 0x78200013, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"srlv", "d,t,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, +{"srl", "d,w,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, /* srlv */ +{"srl", "d,w,<", 0x00000002, 0xffe0003f, WR_d|RD_t, 0, I1 }, +{"srl.ob", "X,Y,Q", 0x78000012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"srl.ob", "D,S,T[e]", 0x48000012, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"srl.ob", "D,S,k", 0x4bc00012, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"srl.qh", "X,Y,Q", 0x78200012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +/* ssnop is at the start of the table. */ +{"standby", "", 0x42000021, 0xffffffff, 0, 0, V1 }, +{"sub", "d,v,t", 0x00000022, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"sub", "d,v,I", 0, (int) M_SUB_I, INSN_MACRO, 0, I1 }, +{"sub.d", "D,V,T", 0x46200001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I1 }, +{"sub.s", "D,V,T", 0x46000001, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I1 }, +{"sub.ob", "X,Y,Q", 0x7800000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"sub.ob", "D,S,T", 0x4ac0000a, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"sub.ob", "D,S,T[e]", 0x4800000a, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"sub.ob", "D,S,k", 0x4bc0000a, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"sub.ps", "D,V,T", 0x46c00001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 }, +{"sub.qh", "X,Y,Q", 0x7820000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"suba.ob", "Y,Q", 0x78000036, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"suba.qh", "Y,Q", 0x78200036, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"subl.ob", "Y,Q", 0x78000436, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"subl.qh", "Y,Q", 0x78200436, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"subu", "d,v,t", 0x00000023, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"subu", "d,v,I", 0, (int) M_SUBU_I, INSN_MACRO, 0, I1 }, +{"suspend", "", 0x42000022, 0xffffffff, 0, 0, V1 }, +{"suxc1", "S,t(b)", 0x4c00000d, 0xfc0007ff, SM|RD_S|RD_t|RD_b, 0, I5|I33|N55}, +{"sw", "t,o(b)", 0xac000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 }, +{"sw", "t,A(b)", 0, (int) M_SW_AB, INSN_MACRO, 0, I1 }, +{"swc0", "E,o(b)", 0xe0000000, 0xfc000000, SM|RD_C0|RD_b, 0, I1 }, +{"swc0", "E,A(b)", 0, (int) M_SWC0_AB, INSN_MACRO, 0, I1 }, +{"swc1", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, 0, I1 }, +{"swc1", "E,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, 0, I1 }, +{"swc1", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, 0, I1 }, +{"swc1", "E,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, 0, I1 }, +{"s.s", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, 0, I1 }, /* swc1 */ +{"s.s", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, 0, I1 }, +{"swc2", "E,o(b)", 0xe8000000, 0xfc000000, SM|RD_C2|RD_b, 0, I1 }, +{"swc2", "E,A(b)", 0, (int) M_SWC2_AB, INSN_MACRO, 0, I1 }, +{"swc3", "E,o(b)", 0xec000000, 0xfc000000, SM|RD_C3|RD_b, 0, I1 }, +{"swc3", "E,A(b)", 0, (int) M_SWC3_AB, INSN_MACRO, 0, I1 }, +{"swl", "t,o(b)", 0xa8000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 }, +{"swl", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, 0, I1 }, +{"scache", "t,o(b)", 0xa8000000, 0xfc000000, RD_t|RD_b, 0, I2 }, /* same */ +{"scache", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, 0, I2 }, /* as swl */ +{"swr", "t,o(b)", 0xb8000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 }, +{"swr", "t,A(b)", 0, (int) M_SWR_AB, INSN_MACRO, 0, I1 }, +{"invalidate", "t,o(b)",0xb8000000, 0xfc000000, RD_t|RD_b, 0, I2 }, /* same */ +{"invalidate", "t,A(b)",0, (int) M_SWR_AB, INSN_MACRO, 0, I2 }, /* as swr */ +{"swxc1", "S,t(b)", 0x4c000008, 0xfc0007ff, SM|RD_S|RD_t|RD_b|FP_S, 0, I4|I33 }, +{"sync", "", 0x0000000f, 0xffffffff, INSN_SYNC, 0, I2|G1 }, +{"sync.p", "", 0x0000040f, 0xffffffff, INSN_SYNC, 0, I2 }, +{"sync.l", "", 0x0000000f, 0xffffffff, INSN_SYNC, 0, I2 }, +{"synci", "o(b)", 0x041f0000, 0xfc1f0000, SM|RD_b, 0, I33 }, +{"syscall", "", 0x0000000c, 0xffffffff, TRAP, 0, I1 }, +{"syscall", "B", 0x0000000c, 0xfc00003f, TRAP, 0, I1 }, +{"teqi", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, +{"teq", "s,t", 0x00000034, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 }, +{"teq", "s,t,q", 0x00000034, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 }, +{"teq", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* teqi */ +{"teq", "s,I", 0, (int) M_TEQ_I, INSN_MACRO, 0, I2 }, +{"tgei", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, +{"tge", "s,t", 0x00000030, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 }, +{"tge", "s,t,q", 0x00000030, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 }, +{"tge", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tgei */ +{"tge", "s,I", 0, (int) M_TGE_I, INSN_MACRO, 0, I2 }, +{"tgeiu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, +{"tgeu", "s,t", 0x00000031, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 }, +{"tgeu", "s,t,q", 0x00000031, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 }, +{"tgeu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tgeiu */ +{"tgeu", "s,I", 0, (int) M_TGEU_I, INSN_MACRO, 0, I2 }, +{"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, 0, I1 }, +{"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, 0, I1 }, +{"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, 0, I1 }, +{"tlbinv", "", 0x42000003, 0xffffffff, INSN_TLB, 0, I32 }, +{"tlbinvf", "", 0x42000004, 0xffffffff, INSN_TLB, 0, I32 }, +{"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, 0, I1 }, +{"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, +{"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 }, +{"tlt", "s,t,q", 0x00000032, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 }, +{"tlt", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tlti */ +{"tlt", "s,I", 0, (int) M_TLT_I, INSN_MACRO, 0, I2 }, +{"tltiu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, +{"tltu", "s,t", 0x00000033, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 }, +{"tltu", "s,t,q", 0x00000033, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 }, +{"tltu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tltiu */ +{"tltu", "s,I", 0, (int) M_TLTU_I, INSN_MACRO, 0, I2 }, +{"tnei", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, +{"tne", "s,t", 0x00000036, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 }, +{"tne", "s,t,q", 0x00000036, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 }, +{"tne", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tnei */ +{"tne", "s,I", 0, (int) M_TNE_I, INSN_MACRO, 0, I2 }, +{"trunc.l.d", "D,S", 0x46200009, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 }, +{"trunc.l.s", "D,S", 0x46000009, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 }, +{"trunc.w.d", "D,S", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 }, +{"trunc.w.d", "D,S,x", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 }, +{"trunc.w.d", "D,S,t", 0, (int) M_TRUNCWD, INSN_MACRO, 0, I1 }, +{"trunc.w.s", "D,S", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 }, +{"trunc.w.s", "D,S,x", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 }, +{"trunc.w.s", "D,S,t", 0, (int) M_TRUNCWS, INSN_MACRO, 0, I1 }, +{"uld", "t,o(b)", 0, (int) M_ULD, INSN_MACRO, 0, I3 }, +{"uld", "t,A(b)", 0, (int) M_ULD_A, INSN_MACRO, 0, I3 }, +{"ulh", "t,o(b)", 0, (int) M_ULH, INSN_MACRO, 0, I1 }, +{"ulh", "t,A(b)", 0, (int) M_ULH_A, INSN_MACRO, 0, I1 }, +{"ulhu", "t,o(b)", 0, (int) M_ULHU, INSN_MACRO, 0, I1 }, +{"ulhu", "t,A(b)", 0, (int) M_ULHU_A, INSN_MACRO, 0, I1 }, +{"ulw", "t,o(b)", 0, (int) M_ULW, INSN_MACRO, 0, I1 }, +{"ulw", "t,A(b)", 0, (int) M_ULW_A, INSN_MACRO, 0, I1 }, +{"usd", "t,o(b)", 0, (int) M_USD, INSN_MACRO, 0, I3 }, +{"usd", "t,A(b)", 0, (int) M_USD_A, INSN_MACRO, 0, I3 }, +{"ush", "t,o(b)", 0, (int) M_USH, INSN_MACRO, 0, I1 }, +{"ush", "t,A(b)", 0, (int) M_USH_A, INSN_MACRO, 0, I1 }, +{"usw", "t,o(b)", 0, (int) M_USW, INSN_MACRO, 0, I1 }, +{"usw", "t,A(b)", 0, (int) M_USW_A, INSN_MACRO, 0, I1 }, +{"wach.ob", "Y", 0x7a00003e, 0xffff07ff, RD_S|FP_D, WR_MACC, MX|SB1 }, +{"wach.ob", "S", 0x4a00003e, 0xffff07ff, RD_S, 0, N54 }, +{"wach.qh", "Y", 0x7a20003e, 0xffff07ff, RD_S|FP_D, WR_MACC, MX }, +{"wacl.ob", "Y,Z", 0x7800003e, 0xffe007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 }, +{"wacl.ob", "S,T", 0x4800003e, 0xffe007ff, RD_S|RD_T, 0, N54 }, +{"wacl.qh", "Y,Z", 0x7820003e, 0xffe007ff, RD_S|RD_T|FP_D, WR_MACC, MX }, +{"wait", "", 0x42000020, 0xffffffff, TRAP, 0, I3|I32 }, +{"wait", "J", 0x42000020, 0xfe00003f, TRAP, 0, I32|N55 }, +{"waiti", "", 0x42000020, 0xffffffff, TRAP, 0, L1 }, +{"wrpgpr", "d,w", 0x41c00000, 0xffe007ff, RD_t, 0, I33 }, +{"wsbh", "d,w", 0x7c0000a0, 0xffe007ff, WR_d|RD_t, 0, I33 }, +{"xor", "d,v,t", 0x00000026, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 }, +{"xor", "t,r,I", 0, (int) M_XOR_I, INSN_MACRO, 0, I1 }, +{"xor.ob", "X,Y,Q", 0x7800000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 }, +{"xor.ob", "D,S,T", 0x4ac0000d, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"xor.ob", "D,S,T[e]", 0x4800000d, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 }, +{"xor.ob", "D,S,k", 0x4bc0000d, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 }, +{"xor.qh", "X,Y,Q", 0x7820000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX }, +{"xori", "t,r,i", 0x38000000, 0xfc000000, WR_t|RD_s, 0, I1 }, +{"yield", "s", 0x7c000009, 0xfc1fffff, TRAP|RD_s, 0, MT32 }, +{"yield", "d,s", 0x7c000009, 0xfc1f07ff, TRAP|WR_d|RD_s, 0, MT32 }, + +/* User Defined Instruction. */ +{"udi0", "s,t,d,+1",0x70000010, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi0", "s,t,+2", 0x70000010, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi0", "s,+3", 0x70000010, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi0", "+4", 0x70000010, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi1", "s,t,d,+1",0x70000011, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi1", "s,t,+2", 0x70000011, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi1", "s,+3", 0x70000011, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi1", "+4", 0x70000011, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi2", "s,t,d,+1",0x70000012, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi2", "s,t,+2", 0x70000012, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi2", "s,+3", 0x70000012, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi2", "+4", 0x70000012, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi3", "s,t,d,+1",0x70000013, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi3", "s,t,+2", 0x70000013, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi3", "s,+3", 0x70000013, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi3", "+4", 0x70000013, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi4", "s,t,d,+1",0x70000014, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi4", "s,t,+2", 0x70000014, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi4", "s,+3", 0x70000014, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi4", "+4", 0x70000014, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi5", "s,t,d,+1",0x70000015, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi5", "s,t,+2", 0x70000015, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi5", "s,+3", 0x70000015, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi5", "+4", 0x70000015, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi6", "s,t,d,+1",0x70000016, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi6", "s,t,+2", 0x70000016, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi6", "s,+3", 0x70000016, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi6", "+4", 0x70000016, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi7", "s,t,d,+1",0x70000017, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi7", "s,t,+2", 0x70000017, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi7", "s,+3", 0x70000017, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi7", "+4", 0x70000017, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi8", "s,t,d,+1",0x70000018, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi8", "s,t,+2", 0x70000018, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi8", "s,+3", 0x70000018, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi8", "+4", 0x70000018, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi9", "s,t,d,+1",0x70000019, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi9", "s,t,+2", 0x70000019, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi9", "s,+3", 0x70000019, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi9", "+4", 0x70000019, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi10", "s,t,d,+1",0x7000001a, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi10", "s,t,+2", 0x7000001a, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi10", "s,+3", 0x7000001a, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi10", "+4", 0x7000001a, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi11", "s,t,d,+1",0x7000001b, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi11", "s,t,+2", 0x7000001b, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi11", "s,+3", 0x7000001b, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi11", "+4", 0x7000001b, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi12", "s,t,d,+1",0x7000001c, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi12", "s,t,+2", 0x7000001c, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi12", "s,+3", 0x7000001c, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi12", "+4", 0x7000001c, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi13", "s,t,d,+1",0x7000001d, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi13", "s,t,+2", 0x7000001d, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi13", "s,+3", 0x7000001d, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi13", "+4", 0x7000001d, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi14", "s,t,d,+1",0x7000001e, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi14", "s,t,+2", 0x7000001e, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi14", "s,+3", 0x7000001e, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi14", "+4", 0x7000001e, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi15", "s,t,d,+1",0x7000001f, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi15", "s,t,+2", 0x7000001f, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi15", "s,+3", 0x7000001f, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, +{"udi15", "+4", 0x7000001f, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 }, + +/* Coprocessor 2 move/branch operations overlap with VR5400 .ob format + instructions so they are here for the latters to take precedence. */ +{"bc2f", "p", 0x49000000, 0xffff0000, CBD|RD_CC, 0, I1 }, +{"bc2f", "N,p", 0x49000000, 0xffe30000, CBD|RD_CC, 0, I32 }, +{"bc2fl", "p", 0x49020000, 0xffff0000, CBL|RD_CC, 0, I2|T3 }, +{"bc2fl", "N,p", 0x49020000, 0xffe30000, CBL|RD_CC, 0, I32 }, +{"bc2t", "p", 0x49010000, 0xffff0000, CBD|RD_CC, 0, I1 }, +{"bc2t", "N,p", 0x49010000, 0xffe30000, CBD|RD_CC, 0, I32 }, +{"bc2tl", "p", 0x49030000, 0xffff0000, CBL|RD_CC, 0, I2|T3 }, +{"bc2tl", "N,p", 0x49030000, 0xffe30000, CBL|RD_CC, 0, I32 }, +{"cfc2", "t,G", 0x48400000, 0xffe007ff, LCD|WR_t|RD_C2, 0, I1 }, +{"ctc2", "t,G", 0x48c00000, 0xffe007ff, COD|RD_t|WR_CC, 0, I1 }, +{"dmfc2", "t,G", 0x48200000, 0xffe007ff, LCD|WR_t|RD_C2, 0, I3 }, +{"dmfc2", "t,G,H", 0x48200000, 0xffe007f8, LCD|WR_t|RD_C2, 0, I64 }, +{"dmtc2", "t,G", 0x48a00000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, 0, I3 }, +{"dmtc2", "t,G,H", 0x48a00000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, 0, I64 }, +{"mfc2", "t,G", 0x48000000, 0xffe007ff, LCD|WR_t|RD_C2, 0, I1 }, +{"mfc2", "t,G,H", 0x48000000, 0xffe007f8, LCD|WR_t|RD_C2, 0, I32 }, +{"mfhc2", "t,G", 0x48600000, 0xffe007ff, LCD|WR_t|RD_C2, 0, I33 }, +{"mfhc2", "t,G,H", 0x48600000, 0xffe007f8, LCD|WR_t|RD_C2, 0, I33 }, +{"mfhc2", "t,i", 0x48600000, 0xffe00000, LCD|WR_t|RD_C2, 0, I33 }, +{"mtc2", "t,G", 0x48800000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, 0, I1 }, +{"mtc2", "t,G,H", 0x48800000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, 0, I32 }, +{"mthc2", "t,G", 0x48e00000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, 0, I33 }, +{"mthc2", "t,G,H", 0x48e00000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, 0, I33 }, +{"mthc2", "t,i", 0x48e00000, 0xffe00000, COD|RD_t|WR_C2|WR_CC, 0, I33 }, + +/* Coprocessor 3 move/branch operations overlap with MIPS IV COP1X + instructions, so they are here for the latters to take precedence. */ +{"bc3f", "p", 0x4d000000, 0xffff0000, CBD|RD_CC, 0, I1 }, +{"bc3fl", "p", 0x4d020000, 0xffff0000, CBL|RD_CC, 0, I2|T3 }, +{"bc3t", "p", 0x4d010000, 0xffff0000, CBD|RD_CC, 0, I1 }, +{"bc3tl", "p", 0x4d030000, 0xffff0000, CBL|RD_CC, 0, I2|T3 }, +{"cfc3", "t,G", 0x4c400000, 0xffe007ff, LCD|WR_t|RD_C3, 0, I1 }, +{"ctc3", "t,G", 0x4cc00000, 0xffe007ff, COD|RD_t|WR_CC, 0, I1 }, +{"dmfc3", "t,G", 0x4c200000, 0xffe007ff, LCD|WR_t|RD_C3, 0, I3 }, +{"dmtc3", "t,G", 0x4ca00000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, 0, I3 }, +{"mfc3", "t,G", 0x4c000000, 0xffe007ff, LCD|WR_t|RD_C3, 0, I1 }, +{"mfc3", "t,G,H", 0x4c000000, 0xffe007f8, LCD|WR_t|RD_C3, 0, I32 }, +{"mtc3", "t,G", 0x4c800000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, 0, I1 }, +{"mtc3", "t,G,H", 0x4c800000, 0xffe007f8, COD|RD_t|WR_C3|WR_CC, 0, I32 }, + +/* No hazard protection on coprocessor instructions--they shouldn't + change the state of the processor and if they do it's up to the + user to put in nops as necessary. These are at the end so that the + disassembler recognizes more specific versions first. */ +{"c0", "C", 0x42000000, 0xfe000000, 0, 0, I1 }, +{"c1", "C", 0x46000000, 0xfe000000, 0, 0, I1 }, +{"c2", "C", 0x4a000000, 0xfe000000, 0, 0, I1 }, +{"c3", "C", 0x4e000000, 0xfe000000, 0, 0, I1 }, +{"cop0", "C", 0, (int) M_COP0, INSN_MACRO, 0, I1 }, +{"cop1", "C", 0, (int) M_COP1, INSN_MACRO, 0, I1 }, +{"cop2", "C", 0, (int) M_COP2, INSN_MACRO, 0, I1 }, +{"cop3", "C", 0, (int) M_COP3, INSN_MACRO, 0, I1 }, + /* Conflicts with the 4650's "mul" instruction. Nobody's using the + 4010 any more, so move this insn out of the way. If the object + format gave us more info, we could do this right. */ +{"addciu", "t,r,j", 0x70000000, 0xfc000000, WR_t|RD_s, 0, L1 }, +/* MIPS DSP ASE */ +{"absq_s.ph", "d,t", 0x7c000252, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"absq_s.pw", "d,t", 0x7c000456, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"absq_s.qh", "d,t", 0x7c000256, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"absq_s.w", "d,t", 0x7c000452, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"addq.ph", "d,s,t", 0x7c000290, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"addq.pw", "d,s,t", 0x7c000494, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"addq.qh", "d,s,t", 0x7c000294, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"addq_s.ph", "d,s,t", 0x7c000390, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"addq_s.pw", "d,s,t", 0x7c000594, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"addq_s.qh", "d,s,t", 0x7c000394, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"addq_s.w", "d,s,t", 0x7c000590, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"addsc", "d,s,t", 0x7c000410, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"addu.ob", "d,s,t", 0x7c000014, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"addu.qb", "d,s,t", 0x7c000010, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"addu_s.ob", "d,s,t", 0x7c000114, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"addu_s.qb", "d,s,t", 0x7c000110, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"addwc", "d,s,t", 0x7c000450, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"bitrev", "d,t", 0x7c0006d2, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"bposge32", "p", 0x041c0000, 0xffff0000, CBD, 0, D32 }, +{"bposge64", "p", 0x041d0000, 0xffff0000, CBD, 0, D64 }, +{"cmp.eq.ph", "s,t", 0x7c000211, 0xfc00ffff, RD_s|RD_t, 0, D32 }, +{"cmp.eq.pw", "s,t", 0x7c000415, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmp.eq.qh", "s,t", 0x7c000215, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmpgu.eq.ob", "d,s,t", 0x7c000115, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"cmpgu.eq.qb", "d,s,t", 0x7c000111, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"cmpgu.le.ob", "d,s,t", 0x7c000195, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"cmpgu.le.qb", "d,s,t", 0x7c000191, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"cmpgu.lt.ob", "d,s,t", 0x7c000155, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"cmpgu.lt.qb", "d,s,t", 0x7c000151, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"cmp.le.ph", "s,t", 0x7c000291, 0xfc00ffff, RD_s|RD_t, 0, D32 }, +{"cmp.le.pw", "s,t", 0x7c000495, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmp.le.qh", "s,t", 0x7c000295, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmp.lt.ph", "s,t", 0x7c000251, 0xfc00ffff, RD_s|RD_t, 0, D32 }, +{"cmp.lt.pw", "s,t", 0x7c000455, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmp.lt.qh", "s,t", 0x7c000255, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmpu.eq.ob", "s,t", 0x7c000015, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmpu.eq.qb", "s,t", 0x7c000011, 0xfc00ffff, RD_s|RD_t, 0, D32 }, +{"cmpu.le.ob", "s,t", 0x7c000095, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmpu.le.qb", "s,t", 0x7c000091, 0xfc00ffff, RD_s|RD_t, 0, D32 }, +{"cmpu.lt.ob", "s,t", 0x7c000055, 0xfc00ffff, RD_s|RD_t, 0, D64 }, +{"cmpu.lt.qb", "s,t", 0x7c000051, 0xfc00ffff, RD_s|RD_t, 0, D32 }, +{"dextpdp", "t,7,6", 0x7c0002bc, 0xfc00e7ff, WR_t|RD_a|DSP_VOLA, 0, D64 }, +{"dextpdpv", "t,7,s", 0x7c0002fc, 0xfc00e7ff, WR_t|RD_a|RD_s|DSP_VOLA, 0, D64 }, +{"dextp", "t,7,6", 0x7c0000bc, 0xfc00e7ff, WR_t|RD_a, 0, D64 }, +{"dextpv", "t,7,s", 0x7c0000fc, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 }, +{"dextr.l", "t,7,6", 0x7c00043c, 0xfc00e7ff, WR_t|RD_a, 0, D64 }, +{"dextr_r.l", "t,7,6", 0x7c00053c, 0xfc00e7ff, WR_t|RD_a, 0, D64 }, +{"dextr_rs.l", "t,7,6", 0x7c0005bc, 0xfc00e7ff, WR_t|RD_a, 0, D64 }, +{"dextr_rs.w", "t,7,6", 0x7c0001bc, 0xfc00e7ff, WR_t|RD_a, 0, D64 }, +{"dextr_r.w", "t,7,6", 0x7c00013c, 0xfc00e7ff, WR_t|RD_a, 0, D64 }, +{"dextr_s.h", "t,7,6", 0x7c0003bc, 0xfc00e7ff, WR_t|RD_a, 0, D64 }, +{"dextrv.l", "t,7,s", 0x7c00047c, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 }, +{"dextrv_r.l", "t,7,s", 0x7c00057c, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 }, +{"dextrv_rs.l", "t,7,s", 0x7c0005fc, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 }, +{"dextrv_rs.w", "t,7,s", 0x7c0001fc, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 }, +{"dextrv_r.w", "t,7,s", 0x7c00017c, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 }, +{"dextrv_s.h", "t,7,s", 0x7c0003fc, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 }, +{"dextrv.w", "t,7,s", 0x7c00007c, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 }, +{"dextr.w", "t,7,6", 0x7c00003c, 0xfc00e7ff, WR_t|RD_a, 0, D64 }, +{"dinsv", "t,s", 0x7c00000d, 0xfc00ffff, WR_t|RD_s, 0, D64 }, +{"dmadd", "7,s,t", 0x7c000674, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dmaddu", "7,s,t", 0x7c000774, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dmsub", "7,s,t", 0x7c0006f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dmsubu", "7,s,t", 0x7c0007f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dmthlip", "s,7", 0x7c0007fc, 0xfc1fe7ff, RD_s|MOD_a|DSP_VOLA, 0, D64 }, +{"dpaq_sa.l.pw", "7,s,t", 0x7c000334, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dpaq_sa.l.w", "7,s,t", 0x7c000330, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"dpaq_s.w.ph", "7,s,t", 0x7c000130, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"dpaq_s.w.qh", "7,s,t", 0x7c000134, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dpau.h.obl", "7,s,t", 0x7c0000f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dpau.h.obr", "7,s,t", 0x7c0001f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dpau.h.qbl", "7,s,t", 0x7c0000f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"dpau.h.qbr", "7,s,t", 0x7c0001f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"dpsq_sa.l.pw", "7,s,t", 0x7c000374, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dpsq_sa.l.w", "7,s,t", 0x7c000370, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"dpsq_s.w.ph", "7,s,t", 0x7c000170, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"dpsq_s.w.qh", "7,s,t", 0x7c000174, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dpsu.h.obl", "7,s,t", 0x7c0002f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dpsu.h.obr", "7,s,t", 0x7c0003f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"dpsu.h.qbl", "7,s,t", 0x7c0002f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"dpsu.h.qbr", "7,s,t", 0x7c0003f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"dshilo", "7,:", 0x7c0006bc, 0xfc07e7ff, MOD_a, 0, D64 }, +{"dshilov", "7,s", 0x7c0006fc, 0xfc1fe7ff, MOD_a|RD_s, 0, D64 }, +{"extpdp", "t,7,6", 0x7c0002b8, 0xfc00e7ff, WR_t|RD_a|DSP_VOLA, 0, D32 }, +{"extpdpv", "t,7,s", 0x7c0002f8, 0xfc00e7ff, WR_t|RD_a|RD_s|DSP_VOLA, 0, D32 }, +{"extp", "t,7,6", 0x7c0000b8, 0xfc00e7ff, WR_t|RD_a, 0, D32 }, +{"extpv", "t,7,s", 0x7c0000f8, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 }, +{"extr_rs.w", "t,7,6", 0x7c0001b8, 0xfc00e7ff, WR_t|RD_a, 0, D32 }, +{"extr_r.w", "t,7,6", 0x7c000138, 0xfc00e7ff, WR_t|RD_a, 0, D32 }, +{"extr_s.h", "t,7,6", 0x7c0003b8, 0xfc00e7ff, WR_t|RD_a, 0, D32 }, +{"extrv_rs.w", "t,7,s", 0x7c0001f8, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 }, +{"extrv_r.w", "t,7,s", 0x7c000178, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 }, +{"extrv_s.h", "t,7,s", 0x7c0003f8, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 }, +{"extrv.w", "t,7,s", 0x7c000078, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 }, +{"extr.w", "t,7,6", 0x7c000038, 0xfc00e7ff, WR_t|RD_a, 0, D32 }, +{"insv", "t,s", 0x7c00000c, 0xfc00ffff, WR_t|RD_s, 0, D32 }, +{"lbux", "d,t(b)", 0x7c00018a, 0xfc0007ff, LDD|WR_d|RD_t|RD_b, 0, D32 }, +{"ldx", "d,t(b)", 0x7c00020a, 0xfc0007ff, LDD|WR_d|RD_t|RD_b, 0, D64 }, +{"lhx", "d,t(b)", 0x7c00010a, 0xfc0007ff, LDD|WR_d|RD_t|RD_b, 0, D32 }, +{"lwx", "d,t(b)", 0x7c00000a, 0xfc0007ff, LDD|WR_d|RD_t|RD_b, 0, D32 }, +{"maq_sa.w.phl", "7,s,t", 0x7c000430, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"maq_sa.w.phr", "7,s,t", 0x7c0004b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"maq_sa.w.qhll", "7,s,t", 0x7c000434, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_sa.w.qhlr", "7,s,t", 0x7c000474, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_sa.w.qhrl", "7,s,t", 0x7c0004b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_sa.w.qhrr", "7,s,t", 0x7c0004f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_s.l.pwl", "7,s,t", 0x7c000734, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_s.l.pwr", "7,s,t", 0x7c0007b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_s.w.phl", "7,s,t", 0x7c000530, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"maq_s.w.phr", "7,s,t", 0x7c0005b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"maq_s.w.qhll", "7,s,t", 0x7c000534, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_s.w.qhlr", "7,s,t", 0x7c000574, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_s.w.qhrl", "7,s,t", 0x7c0005b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"maq_s.w.qhrr", "7,s,t", 0x7c0005f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"modsub", "d,s,t", 0x7c000490, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"mthlip", "s,7", 0x7c0007f8, 0xfc1fe7ff, RD_s|MOD_a|DSP_VOLA, 0, D32 }, +{"muleq_s.pw.qhl", "d,s,t", 0x7c000714, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 }, +{"muleq_s.pw.qhr", "d,s,t", 0x7c000754, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 }, +{"muleq_s.w.phl", "d,s,t", 0x7c000710, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 }, +{"muleq_s.w.phr", "d,s,t", 0x7c000750, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 }, +{"muleu_s.ph.qbl", "d,s,t", 0x7c000190, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 }, +{"muleu_s.ph.qbr", "d,s,t", 0x7c0001d0, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 }, +{"muleu_s.qh.obl", "d,s,t", 0x7c000194, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 }, +{"muleu_s.qh.obr", "d,s,t", 0x7c0001d4, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 }, +{"mulq_rs.ph", "d,s,t", 0x7c0007d0, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 }, +{"mulq_rs.qh", "d,s,t", 0x7c0007d4, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 }, +{"mulsaq_s.l.pw", "7,s,t", 0x7c0003b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"mulsaq_s.w.ph", "7,s,t", 0x7c0001b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 }, +{"mulsaq_s.w.qh", "7,s,t", 0x7c0001b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 }, +{"packrl.ph", "d,s,t", 0x7c000391, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"packrl.pw", "d,s,t", 0x7c000395, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"pick.ob", "d,s,t", 0x7c0000d5, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"pick.ph", "d,s,t", 0x7c0002d1, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"pick.pw", "d,s,t", 0x7c0004d5, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"pick.qb", "d,s,t", 0x7c0000d1, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"pick.qh", "d,s,t", 0x7c0002d5, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"preceq.pw.qhla", "d,t", 0x7c000396, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceq.pw.qhl", "d,t", 0x7c000316, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceq.pw.qhra", "d,t", 0x7c0003d6, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceq.pw.qhr", "d,t", 0x7c000356, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceq.s.l.pwl", "d,t", 0x7c000516, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceq.s.l.pwr", "d,t", 0x7c000556, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"precequ.ph.qbla", "d,t", 0x7c000192, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"precequ.ph.qbl", "d,t", 0x7c000112, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"precequ.ph.qbra", "d,t", 0x7c0001d2, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"precequ.ph.qbr", "d,t", 0x7c000152, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"precequ.pw.qhla", "d,t", 0x7c000196, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"precequ.pw.qhl", "d,t", 0x7c000116, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"precequ.pw.qhra", "d,t", 0x7c0001d6, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"precequ.pw.qhr", "d,t", 0x7c000156, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceq.w.phl", "d,t", 0x7c000312, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"preceq.w.phr", "d,t", 0x7c000352, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"preceu.ph.qbla", "d,t", 0x7c000792, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"preceu.ph.qbl", "d,t", 0x7c000712, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"preceu.ph.qbra", "d,t", 0x7c0007d2, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"preceu.ph.qbr", "d,t", 0x7c000752, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"preceu.qh.obla", "d,t", 0x7c000796, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceu.qh.obl", "d,t", 0x7c000716, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceu.qh.obra", "d,t", 0x7c0007d6, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"preceu.qh.obr", "d,t", 0x7c000756, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"precrq.ob.qh", "d,s,t", 0x7c000315, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"precrq.ph.w", "d,s,t", 0x7c000511, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"precrq.pw.l", "d,s,t", 0x7c000715, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"precrq.qb.ph", "d,s,t", 0x7c000311, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"precrq.qh.pw", "d,s,t", 0x7c000515, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"precrq_rs.ph.w", "d,s,t", 0x7c000551, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"precrq_rs.qh.pw", "d,s,t", 0x7c000555, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"precrqu_s.ob.qh", "d,s,t", 0x7c0003d5, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"precrqu_s.qb.ph", "d,s,t", 0x7c0003d1, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"raddu.l.ob", "d,s", 0x7c000514, 0xfc1f07ff, WR_d|RD_s, 0, D64 }, +{"raddu.w.qb", "d,s", 0x7c000510, 0xfc1f07ff, WR_d|RD_s, 0, D32 }, +{"rddsp", "d", 0x7fff04b8, 0xffff07ff, WR_d, 0, D32 }, +{"rddsp", "d,'", 0x7c0004b8, 0xffc007ff, WR_d, 0, D32 }, +{"repl.ob", "d,5", 0x7c000096, 0xff0007ff, WR_d, 0, D64 }, +{"repl.ph", "d,@", 0x7c000292, 0xfc0007ff, WR_d, 0, D32 }, +{"repl.pw", "d,@", 0x7c000496, 0xfc0007ff, WR_d, 0, D64 }, +{"repl.qb", "d,5", 0x7c000092, 0xff0007ff, WR_d, 0, D32 }, +{"repl.qh", "d,@", 0x7c000296, 0xfc0007ff, WR_d, 0, D64 }, +{"replv.ob", "d,t", 0x7c0000d6, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"replv.ph", "d,t", 0x7c0002d2, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"replv.pw", "d,t", 0x7c0004d6, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"replv.qb", "d,t", 0x7c0000d2, 0xffe007ff, WR_d|RD_t, 0, D32 }, +{"replv.qh", "d,t", 0x7c0002d6, 0xffe007ff, WR_d|RD_t, 0, D64 }, +{"shilo", "7,0", 0x7c0006b8, 0xfc0fe7ff, MOD_a, 0, D32 }, +{"shilov", "7,s", 0x7c0006f8, 0xfc1fe7ff, MOD_a|RD_s, 0, D32 }, +{"shll.ob", "d,t,3", 0x7c000017, 0xff0007ff, WR_d|RD_t, 0, D64 }, +{"shll.ph", "d,t,4", 0x7c000213, 0xfe0007ff, WR_d|RD_t, 0, D32 }, +{"shll.pw", "d,t,6", 0x7c000417, 0xfc0007ff, WR_d|RD_t, 0, D64 }, +{"shll.qb", "d,t,3", 0x7c000013, 0xff0007ff, WR_d|RD_t, 0, D32 }, +{"shll.qh", "d,t,4", 0x7c000217, 0xfe0007ff, WR_d|RD_t, 0, D64 }, +{"shll_s.ph", "d,t,4", 0x7c000313, 0xfe0007ff, WR_d|RD_t, 0, D32 }, +{"shll_s.pw", "d,t,6", 0x7c000517, 0xfc0007ff, WR_d|RD_t, 0, D64 }, +{"shll_s.qh", "d,t,4", 0x7c000317, 0xfe0007ff, WR_d|RD_t, 0, D64 }, +{"shll_s.w", "d,t,6", 0x7c000513, 0xfc0007ff, WR_d|RD_t, 0, D32 }, +{"shllv.ob", "d,t,s", 0x7c000097, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shllv.ph", "d,t,s", 0x7c000293, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"shllv.pw", "d,t,s", 0x7c000497, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shllv.qb", "d,t,s", 0x7c000093, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"shllv.qh", "d,t,s", 0x7c000297, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shllv_s.ph", "d,t,s", 0x7c000393, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"shllv_s.pw", "d,t,s", 0x7c000597, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shllv_s.qh", "d,t,s", 0x7c000397, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shllv_s.w", "d,t,s", 0x7c000593, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"shra.ph", "d,t,4", 0x7c000253, 0xfe0007ff, WR_d|RD_t, 0, D32 }, +{"shra.pw", "d,t,6", 0x7c000457, 0xfc0007ff, WR_d|RD_t, 0, D64 }, +{"shra.qh", "d,t,4", 0x7c000257, 0xfe0007ff, WR_d|RD_t, 0, D64 }, +{"shra_r.ph", "d,t,4", 0x7c000353, 0xfe0007ff, WR_d|RD_t, 0, D32 }, +{"shra_r.pw", "d,t,6", 0x7c000557, 0xfc0007ff, WR_d|RD_t, 0, D64 }, +{"shra_r.qh", "d,t,4", 0x7c000357, 0xfe0007ff, WR_d|RD_t, 0, D64 }, +{"shra_r.w", "d,t,6", 0x7c000553, 0xfc0007ff, WR_d|RD_t, 0, D32 }, +{"shrav.ph", "d,t,s", 0x7c0002d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"shrav.pw", "d,t,s", 0x7c0004d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shrav.qh", "d,t,s", 0x7c0002d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shrav_r.ph", "d,t,s", 0x7c0003d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"shrav_r.pw", "d,t,s", 0x7c0005d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shrav_r.qh", "d,t,s", 0x7c0003d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shrav_r.w", "d,t,s", 0x7c0005d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"shrl.ob", "d,t,3", 0x7c000057, 0xff0007ff, WR_d|RD_t, 0, D64 }, +{"shrl.qb", "d,t,3", 0x7c000053, 0xff0007ff, WR_d|RD_t, 0, D32 }, +{"shrlv.ob", "d,t,s", 0x7c0000d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"shrlv.qb", "d,t,s", 0x7c0000d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"subq.ph", "d,s,t", 0x7c0002d0, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"subq.pw", "d,s,t", 0x7c0004d4, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"subq.qh", "d,s,t", 0x7c0002d4, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"subq_s.ph", "d,s,t", 0x7c0003d0, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"subq_s.pw", "d,s,t", 0x7c0005d4, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"subq_s.qh", "d,s,t", 0x7c0003d4, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"subq_s.w", "d,s,t", 0x7c0005d0, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"subu.ob", "d,s,t", 0x7c000054, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"subu.qb", "d,s,t", 0x7c000050, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"subu_s.ob", "d,s,t", 0x7c000154, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 }, +{"subu_s.qb", "d,s,t", 0x7c000150, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 }, +{"wrdsp", "s", 0x7c1ffcf8, 0xfc1fffff, RD_s|DSP_VOLA, 0, D32 }, +{"wrdsp", "s,8", 0x7c0004f8, 0xfc1e07ff, RD_s|DSP_VOLA, 0, D32 }, +/* MIPS DSP ASE Rev2 */ +{"absq_s.qb", "d,t", 0x7c000052, 0xffe007ff, WR_d|RD_t, 0, D33 }, +{"addu.ph", "d,s,t", 0x7c000210, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"addu_s.ph", "d,s,t", 0x7c000310, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"adduh.qb", "d,s,t", 0x7c000018, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"adduh_r.qb", "d,s,t", 0x7c000098, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"append", "t,s,h", 0x7c000031, 0xfc0007ff, WR_t|RD_t|RD_s, 0, D33 }, +{"balign", "t,s,I", 0, (int) M_BALIGN, INSN_MACRO, 0, D33 }, +{"balign", "t,s,2", 0x7c000431, 0xfc00e7ff, WR_t|RD_t|RD_s, 0, D33 }, +{"cmpgdu.eq.qb", "d,s,t", 0x7c000611, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"cmpgdu.lt.qb", "d,s,t", 0x7c000651, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"cmpgdu.le.qb", "d,s,t", 0x7c000691, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"dpa.w.ph", "7,s,t", 0x7c000030, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"dps.w.ph", "7,s,t", 0x7c000070, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"mul.ph", "d,s,t", 0x7c000318, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 }, +{"mul_s.ph", "d,s,t", 0x7c000398, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 }, +{"mulq_rs.w", "d,s,t", 0x7c0005d8, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 }, +{"mulq_s.ph", "d,s,t", 0x7c000790, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 }, +{"mulq_s.w", "d,s,t", 0x7c000598, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 }, +{"mulsa.w.ph", "7,s,t", 0x7c0000b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"precr.qb.ph", "d,s,t", 0x7c000351, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"precr_sra.ph.w", "t,s,h", 0x7c000791, 0xfc0007ff, WR_t|RD_t|RD_s, 0, D33 }, +{"precr_sra_r.ph.w", "t,s,h", 0x7c0007d1, 0xfc0007ff, WR_t|RD_t|RD_s, 0, D33 }, +{"prepend", "t,s,h", 0x7c000071, 0xfc0007ff, WR_t|RD_t|RD_s, 0, D33 }, +{"shra.qb", "d,t,3", 0x7c000113, 0xff0007ff, WR_d|RD_t, 0, D33 }, +{"shra_r.qb", "d,t,3", 0x7c000153, 0xff0007ff, WR_d|RD_t, 0, D33 }, +{"shrav.qb", "d,t,s", 0x7c000193, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"shrav_r.qb", "d,t,s", 0x7c0001d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"shrl.ph", "d,t,4", 0x7c000653, 0xfe0007ff, WR_d|RD_t, 0, D33 }, +{"shrlv.ph", "d,t,s", 0x7c0006d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"subu.ph", "d,s,t", 0x7c000250, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"subu_s.ph", "d,s,t", 0x7c000350, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"subuh.qb", "d,s,t", 0x7c000058, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"subuh_r.qb", "d,s,t", 0x7c0000d8, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"addqh.ph", "d,s,t", 0x7c000218, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"addqh_r.ph", "d,s,t", 0x7c000298, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"addqh.w", "d,s,t", 0x7c000418, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"addqh_r.w", "d,s,t", 0x7c000498, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"subqh.ph", "d,s,t", 0x7c000258, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"subqh_r.ph", "d,s,t", 0x7c0002d8, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"subqh.w", "d,s,t", 0x7c000458, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"subqh_r.w", "d,s,t", 0x7c0004d8, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 }, +{"dpax.w.ph", "7,s,t", 0x7c000230, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"dpsx.w.ph", "7,s,t", 0x7c000270, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"dpaqx_s.w.ph", "7,s,t", 0x7c000630, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"dpaqx_sa.w.ph", "7,s,t", 0x7c0006b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"dpsqx_s.w.ph", "7,s,t", 0x7c000670, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +{"dpsqx_sa.w.ph", "7,s,t", 0x7c0006f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 }, +/* Move bc0* after mftr and mttr to avoid opcode collision. */ +{"bc0f", "p", 0x41000000, 0xffff0000, CBD|RD_CC, 0, I1 }, +{"bc0fl", "p", 0x41020000, 0xffff0000, CBL|RD_CC, 0, I2|T3 }, +{"bc0t", "p", 0x41010000, 0xffff0000, CBD|RD_CC, 0, I1 }, +{"bc0tl", "p", 0x41030000, 0xffff0000, CBL|RD_CC, 0, I2|T3 }, +/* ST Microelectronics Loongson-2E and -2F. */ +{"mult.g", "d,s,t", 0x7c000018, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"mult.g", "d,s,t", 0x70000010, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"multu.g", "d,s,t", 0x7c000019, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"multu.g", "d,s,t", 0x70000012, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"dmult.g", "d,s,t", 0x7c00001c, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"dmult.g", "d,s,t", 0x70000011, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"dmultu.g", "d,s,t", 0x7c00001d, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"dmultu.g", "d,s,t", 0x70000013, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"div.g", "d,s,t", 0x7c00001a, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"div.g", "d,s,t", 0x70000014, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"divu.g", "d,s,t", 0x7c00001b, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"divu.g", "d,s,t", 0x70000016, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"ddiv.g", "d,s,t", 0x7c00001e, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"ddiv.g", "d,s,t", 0x70000015, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"ddivu.g", "d,s,t", 0x7c00001f, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"ddivu.g", "d,s,t", 0x70000017, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"mod.g", "d,s,t", 0x7c000022, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"mod.g", "d,s,t", 0x7000001c, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"modu.g", "d,s,t", 0x7c000023, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"modu.g", "d,s,t", 0x7000001e, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"dmod.g", "d,s,t", 0x7c000026, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"dmod.g", "d,s,t", 0x7000001d, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"dmodu.g", "d,s,t", 0x7c000027, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"dmodu.g", "d,s,t", 0x7000001f, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +}; + +#define MIPS_NUM_OPCODES \ + ((sizeof mips_builtin_opcodes) / (sizeof (mips_builtin_opcodes[0]))) +const int bfd_mips_num_builtin_opcodes = MIPS_NUM_OPCODES; + +/* const removed from the following to allow for dynamic extensions to the + * built-in instruction set. */ +struct mips_opcode *mips_opcodes = + (struct mips_opcode *) mips_builtin_opcodes; +int bfd_mips_num_opcodes = MIPS_NUM_OPCODES; +#undef MIPS_NUM_OPCODES + +/* Mips instructions are at maximum this many bytes long. */ +#define INSNLEN 4 + + +/* FIXME: These should be shared with gdb somehow. */ + +struct mips_cp0sel_name +{ + unsigned int cp0reg; + unsigned int sel; + const char * const name; +}; + +#if 0 +/* The mips16 registers. */ +static const unsigned int mips16_to_32_reg_map[] = +{ + 16, 17, 2, 3, 4, 5, 6, 7 +}; + +#define mips16_reg_names(rn) mips_gpr_names[mips16_to_32_reg_map[rn]] +#endif + +static const char * const mips_gpr_names_numeric[32] = +{ + "$0", "$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" +}; + +static const char * const mips_gpr_names_oldabi[32] = +{ + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static const char * const mips_gpr_names_newabi[32] = +{ + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static const char * const mips_fpr_names_numeric[32] = +{ + "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", + "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", + "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", + "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31" +}; + +static const char * const mips_fpr_names_32[32] = +{ + "fv0", "fv0f", "fv1", "fv1f", "ft0", "ft0f", "ft1", "ft1f", + "ft2", "ft2f", "ft3", "ft3f", "fa0", "fa0f", "fa1", "fa1f", + "ft4", "ft4f", "ft5", "ft5f", "fs0", "fs0f", "fs1", "fs1f", + "fs2", "fs2f", "fs3", "fs3f", "fs4", "fs4f", "fs5", "fs5f" +}; + +static const char * const mips_fpr_names_n32[32] = +{ + "fv0", "ft14", "fv1", "ft15", "ft0", "ft1", "ft2", "ft3", + "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3", + "fa4", "fa5", "fa6", "fa7", "fs0", "ft8", "fs1", "ft9", + "fs2", "ft10", "fs3", "ft11", "fs4", "ft12", "fs5", "ft13" +}; + +static const char * const mips_fpr_names_64[32] = +{ + "fv0", "ft12", "fv1", "ft13", "ft0", "ft1", "ft2", "ft3", + "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3", + "fa4", "fa5", "fa6", "fa7", "ft8", "ft9", "ft10", "ft11", + "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7" +}; + +static const char * const mips_wr_names[32] = { + "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", + "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", + "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", + "w24", "w25", "w26", "w27", "w28", "w29", "w30", "w31" +}; + +static const char * const mips_cp0_names_numeric[32] = +{ + "$0", "$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" +}; + +static const char * const mips_cp0_names_mips3264[32] = +{ + "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1", + "c0_context", "c0_pagemask", "c0_wired", "$7", + "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare", + "c0_status", "c0_cause", "c0_epc", "c0_prid", + "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi", + "c0_xcontext", "$21", "$22", "c0_debug", + "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr", + "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave", +}; + +static const struct mips_cp0sel_name mips_cp0sel_names_mips3264[] = +{ + { 4, 1, "c0_contextconfig" }, + { 0, 1, "c0_mvpcontrol" }, + { 0, 2, "c0_mvpconf0" }, + { 0, 3, "c0_mvpconf1" }, + { 1, 1, "c0_vpecontrol" }, + { 1, 2, "c0_vpeconf0" }, + { 1, 3, "c0_vpeconf1" }, + { 1, 4, "c0_yqmask" }, + { 1, 5, "c0_vpeschedule" }, + { 1, 6, "c0_vpeschefback" }, + { 2, 1, "c0_tcstatus" }, + { 2, 2, "c0_tcbind" }, + { 2, 3, "c0_tcrestart" }, + { 2, 4, "c0_tchalt" }, + { 2, 5, "c0_tccontext" }, + { 2, 6, "c0_tcschedule" }, + { 2, 7, "c0_tcschefback" }, + { 5, 1, "c0_pagegrain" }, + { 6, 1, "c0_srsconf0" }, + { 6, 2, "c0_srsconf1" }, + { 6, 3, "c0_srsconf2" }, + { 6, 4, "c0_srsconf3" }, + { 6, 5, "c0_srsconf4" }, + { 12, 1, "c0_intctl" }, + { 12, 2, "c0_srsctl" }, + { 12, 3, "c0_srsmap" }, + { 15, 1, "c0_ebase" }, + { 16, 1, "c0_config1" }, + { 16, 2, "c0_config2" }, + { 16, 3, "c0_config3" }, + { 18, 1, "c0_watchlo,1" }, + { 18, 2, "c0_watchlo,2" }, + { 18, 3, "c0_watchlo,3" }, + { 18, 4, "c0_watchlo,4" }, + { 18, 5, "c0_watchlo,5" }, + { 18, 6, "c0_watchlo,6" }, + { 18, 7, "c0_watchlo,7" }, + { 19, 1, "c0_watchhi,1" }, + { 19, 2, "c0_watchhi,2" }, + { 19, 3, "c0_watchhi,3" }, + { 19, 4, "c0_watchhi,4" }, + { 19, 5, "c0_watchhi,5" }, + { 19, 6, "c0_watchhi,6" }, + { 19, 7, "c0_watchhi,7" }, + { 23, 1, "c0_tracecontrol" }, + { 23, 2, "c0_tracecontrol2" }, + { 23, 3, "c0_usertracedata" }, + { 23, 4, "c0_tracebpc" }, + { 25, 1, "c0_perfcnt,1" }, + { 25, 2, "c0_perfcnt,2" }, + { 25, 3, "c0_perfcnt,3" }, + { 25, 4, "c0_perfcnt,4" }, + { 25, 5, "c0_perfcnt,5" }, + { 25, 6, "c0_perfcnt,6" }, + { 25, 7, "c0_perfcnt,7" }, + { 27, 1, "c0_cacheerr,1" }, + { 27, 2, "c0_cacheerr,2" }, + { 27, 3, "c0_cacheerr,3" }, + { 28, 1, "c0_datalo" }, + { 28, 2, "c0_taglo1" }, + { 28, 3, "c0_datalo1" }, + { 28, 4, "c0_taglo2" }, + { 28, 5, "c0_datalo2" }, + { 28, 6, "c0_taglo3" }, + { 28, 7, "c0_datalo3" }, + { 29, 1, "c0_datahi" }, + { 29, 2, "c0_taghi1" }, + { 29, 3, "c0_datahi1" }, + { 29, 4, "c0_taghi2" }, + { 29, 5, "c0_datahi2" }, + { 29, 6, "c0_taghi3" }, + { 29, 7, "c0_datahi3" }, +}; + +static const char * const mips_cp0_names_mips3264r2[32] = +{ + "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1", + "c0_context", "c0_pagemask", "c0_wired", "c0_hwrena", + "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare", + "c0_status", "c0_cause", "c0_epc", "c0_prid", + "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi", + "c0_xcontext", "$21", "$22", "c0_debug", + "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr", + "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave", +}; + +static const struct mips_cp0sel_name mips_cp0sel_names_mips3264r2[] = +{ + { 4, 1, "c0_contextconfig" }, + { 5, 1, "c0_pagegrain" }, + { 12, 1, "c0_intctl" }, + { 12, 2, "c0_srsctl" }, + { 12, 3, "c0_srsmap" }, + { 15, 1, "c0_ebase" }, + { 16, 1, "c0_config1" }, + { 16, 2, "c0_config2" }, + { 16, 3, "c0_config3" }, + { 18, 1, "c0_watchlo,1" }, + { 18, 2, "c0_watchlo,2" }, + { 18, 3, "c0_watchlo,3" }, + { 18, 4, "c0_watchlo,4" }, + { 18, 5, "c0_watchlo,5" }, + { 18, 6, "c0_watchlo,6" }, + { 18, 7, "c0_watchlo,7" }, + { 19, 1, "c0_watchhi,1" }, + { 19, 2, "c0_watchhi,2" }, + { 19, 3, "c0_watchhi,3" }, + { 19, 4, "c0_watchhi,4" }, + { 19, 5, "c0_watchhi,5" }, + { 19, 6, "c0_watchhi,6" }, + { 19, 7, "c0_watchhi,7" }, + { 23, 1, "c0_tracecontrol" }, + { 23, 2, "c0_tracecontrol2" }, + { 23, 3, "c0_usertracedata" }, + { 23, 4, "c0_tracebpc" }, + { 25, 1, "c0_perfcnt,1" }, + { 25, 2, "c0_perfcnt,2" }, + { 25, 3, "c0_perfcnt,3" }, + { 25, 4, "c0_perfcnt,4" }, + { 25, 5, "c0_perfcnt,5" }, + { 25, 6, "c0_perfcnt,6" }, + { 25, 7, "c0_perfcnt,7" }, + { 27, 1, "c0_cacheerr,1" }, + { 27, 2, "c0_cacheerr,2" }, + { 27, 3, "c0_cacheerr,3" }, + { 28, 1, "c0_datalo" }, + { 28, 2, "c0_taglo1" }, + { 28, 3, "c0_datalo1" }, + { 28, 4, "c0_taglo2" }, + { 28, 5, "c0_datalo2" }, + { 28, 6, "c0_taglo3" }, + { 28, 7, "c0_datalo3" }, + { 29, 1, "c0_datahi" }, + { 29, 2, "c0_taghi1" }, + { 29, 3, "c0_datahi1" }, + { 29, 4, "c0_taghi2" }, + { 29, 5, "c0_datahi2" }, + { 29, 6, "c0_taghi3" }, + { 29, 7, "c0_datahi3" }, +}; + +/* SB-1: MIPS64 (mips_cp0_names_mips3264) with minor mods. */ +static const char * const mips_cp0_names_sb1[32] = +{ + "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1", + "c0_context", "c0_pagemask", "c0_wired", "$7", + "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare", + "c0_status", "c0_cause", "c0_epc", "c0_prid", + "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi", + "c0_xcontext", "$21", "$22", "c0_debug", + "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr_i", + "c0_taglo_i", "c0_taghi_i", "c0_errorepc", "c0_desave", +}; + +static const struct mips_cp0sel_name mips_cp0sel_names_sb1[] = +{ + { 16, 1, "c0_config1" }, + { 18, 1, "c0_watchlo,1" }, + { 19, 1, "c0_watchhi,1" }, + { 22, 0, "c0_perftrace" }, + { 23, 3, "c0_edebug" }, + { 25, 1, "c0_perfcnt,1" }, + { 25, 2, "c0_perfcnt,2" }, + { 25, 3, "c0_perfcnt,3" }, + { 25, 4, "c0_perfcnt,4" }, + { 25, 5, "c0_perfcnt,5" }, + { 25, 6, "c0_perfcnt,6" }, + { 25, 7, "c0_perfcnt,7" }, + { 26, 1, "c0_buserr_pa" }, + { 27, 1, "c0_cacheerr_d" }, + { 27, 3, "c0_cacheerr_d_pa" }, + { 28, 1, "c0_datalo_i" }, + { 28, 2, "c0_taglo_d" }, + { 28, 3, "c0_datalo_d" }, + { 29, 1, "c0_datahi_i" }, + { 29, 2, "c0_taghi_d" }, + { 29, 3, "c0_datahi_d" }, +}; + +static const char * const mips_hwr_names_numeric[32] = +{ + "$0", "$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" +}; + +static const char * const mips_hwr_names_mips3264r2[32] = +{ + "hwr_cpunum", "hwr_synci_step", "hwr_cc", "hwr_ccres", + "$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" +}; + +static const char * const mips_msa_control_names_mips3264r2[32] = { + "MSAIR", "MSACSR", "$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" +}; + +struct mips_abi_choice +{ + const char *name; + const char * const *gpr_names; + const char * const *fpr_names; +}; + +static struct mips_abi_choice mips_abi_choices[] = +{ + { "numeric", mips_gpr_names_numeric, mips_fpr_names_numeric }, + { "32", mips_gpr_names_oldabi, mips_fpr_names_32 }, + { "n32", mips_gpr_names_newabi, mips_fpr_names_n32 }, + { "64", mips_gpr_names_newabi, mips_fpr_names_64 }, +}; + +struct mips_arch_choice +{ + const char *name; + int bfd_mach_valid; + unsigned long bfd_mach; + int processor; + int isa; + const char * const *cp0_names; + const struct mips_cp0sel_name *cp0sel_names; + unsigned int cp0sel_names_len; + const char * const *hwr_names; +}; + +#define bfd_mach_mips3000 3000 +#define bfd_mach_mips3900 3900 +#define bfd_mach_mips4000 4000 +#define bfd_mach_mips4010 4010 +#define bfd_mach_mips4100 4100 +#define bfd_mach_mips4111 4111 +#define bfd_mach_mips4120 4120 +#define bfd_mach_mips4300 4300 +#define bfd_mach_mips4400 4400 +#define bfd_mach_mips4600 4600 +#define bfd_mach_mips4650 4650 +#define bfd_mach_mips5000 5000 +#define bfd_mach_mips5400 5400 +#define bfd_mach_mips5500 5500 +#define bfd_mach_mips6000 6000 +#define bfd_mach_mips7000 7000 +#define bfd_mach_mips8000 8000 +#define bfd_mach_mips9000 9000 +#define bfd_mach_mips10000 10000 +#define bfd_mach_mips12000 12000 +#define bfd_mach_mips16 16 +#define bfd_mach_mips5 5 +#define bfd_mach_mips_sb1 12310201 /* octal 'SB', 01 */ +#define bfd_mach_mipsisa32 32 +#define bfd_mach_mipsisa32r2 33 +#define bfd_mach_mipsisa64 64 +#define bfd_mach_mipsisa64r2 65 + +static const struct mips_arch_choice mips_arch_choices[] = +{ + { "numeric", 0, 0, 0, 0, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + + { "r3000", 1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r3900", 1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4000", 1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4010", 1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr4100", 1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr4111", 1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr4120", 1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4300", 1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4400", 1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4600", 1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4650", 1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r5000", 1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr5400", 1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr5500", 1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r6000", 1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "rm7000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "rm9000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r8000", 1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r10000", 1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r12000", 1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "mips5", 1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + + /* For stock MIPS32, disassemble all applicable MIPS-specified ASEs. + Note that MIPS-3D and MDMX are not applicable to MIPS32. (See + _MIPS32 Architecture For Programmers Volume I: Introduction to the + MIPS32 Architecture_ (MIPS Document Number MD00082, Revision 0.95), + page 1. */ + { "mips32", 1, bfd_mach_mipsisa32, CPU_MIPS32, + ISA_MIPS32 | INSN_MIPS16 | INSN_SMARTMIPS, + mips_cp0_names_mips3264, + mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264), + mips_hwr_names_numeric }, + + { "mips32r2", 1, bfd_mach_mipsisa32r2, CPU_MIPS32R2, + (ISA_MIPS32R2 | INSN_MIPS16 | INSN_SMARTMIPS | INSN_DSP | INSN_DSPR2 + | INSN_MIPS3D | INSN_MT | INSN_MSA), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_hwr_names_mips3264r2 }, + + /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs. */ + { "mips64", 1, bfd_mach_mipsisa64, CPU_MIPS64, + ISA_MIPS64 | INSN_MIPS16 | INSN_MIPS3D | INSN_MDMX, + mips_cp0_names_mips3264, + mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264), + mips_hwr_names_numeric }, + + { "mips64r2", 1, bfd_mach_mipsisa64r2, CPU_MIPS64R2, + (ISA_MIPS64R2 | INSN_MIPS16 | INSN_MIPS3D | INSN_DSP | INSN_DSPR2 + | INSN_DSP64 | INSN_MT | INSN_MDMX), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_hwr_names_mips3264r2 }, + + { "sb1", 1, bfd_mach_mips_sb1, CPU_SB1, + ISA_MIPS64 | INSN_MIPS3D | INSN_SB1, + mips_cp0_names_sb1, + mips_cp0sel_names_sb1, ARRAY_SIZE (mips_cp0sel_names_sb1), + mips_hwr_names_numeric }, + + /* This entry, mips16, is here only for ISA/processor selection; do + not print its name. */ + { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3 | INSN_MIPS16, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, +}; + +/* ISA and processor type to disassemble for, and register names to use. + set_default_mips_dis_options and parse_mips_dis_options fill in these + values. */ +static int mips_processor; +static int mips_isa; +static const char * const *mips_gpr_names; +static const char * const *mips_fpr_names; +static const char * const *mips_cp0_names; +static const struct mips_cp0sel_name *mips_cp0sel_names; +static int mips_cp0sel_names_len; +static const char * const *mips_hwr_names; + +/* Other options */ +static int no_aliases; /* If set disassemble as most general inst. */ + +static const struct mips_abi_choice * +choose_abi_by_name (const char *name, unsigned int namelen) +{ + const struct mips_abi_choice *c; + unsigned int i; + + for (i = 0, c = NULL; i < ARRAY_SIZE (mips_abi_choices) && c == NULL; i++) + if (strncmp (mips_abi_choices[i].name, name, namelen) == 0 + && strlen (mips_abi_choices[i].name) == namelen) + c = &mips_abi_choices[i]; + + return c; +} + +static const struct mips_arch_choice * +choose_arch_by_name (const char *name, unsigned int namelen) +{ + const struct mips_arch_choice *c = NULL; + unsigned int i; + + for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++) + if (strncmp (mips_arch_choices[i].name, name, namelen) == 0 + && strlen (mips_arch_choices[i].name) == namelen) + c = &mips_arch_choices[i]; + + return c; +} + +static const struct mips_arch_choice * +choose_arch_by_number (unsigned long mach) +{ + static unsigned long hint_bfd_mach; + static const struct mips_arch_choice *hint_arch_choice; + const struct mips_arch_choice *c; + unsigned int i; + + /* We optimize this because even if the user specifies no + flags, this will be done for every instruction! */ + if (hint_bfd_mach == mach + && hint_arch_choice != NULL + && hint_arch_choice->bfd_mach == hint_bfd_mach) + return hint_arch_choice; + + for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++) + { + if (mips_arch_choices[i].bfd_mach_valid + && mips_arch_choices[i].bfd_mach == mach) + { + c = &mips_arch_choices[i]; + hint_bfd_mach = mach; + hint_arch_choice = c; + } + } + return c; +} + +static void +set_default_mips_dis_options (struct disassemble_info *info) +{ + const struct mips_arch_choice *chosen_arch; + + /* Defaults: mipsIII/r3000 (?!), (o)32-style ("oldabi") GPR names, + and numeric FPR, CP0 register, and HWR names. */ + mips_isa = ISA_MIPS3; + mips_processor = CPU_R3000; + mips_gpr_names = mips_gpr_names_oldabi; + mips_fpr_names = mips_fpr_names_numeric; + mips_cp0_names = mips_cp0_names_numeric; + mips_cp0sel_names = NULL; + mips_cp0sel_names_len = 0; + mips_hwr_names = mips_hwr_names_numeric; + no_aliases = 0; + + /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */ +#if 0 + if (info->flavour == bfd_target_elf_flavour && info->section != NULL) + { + Elf_Internal_Ehdr *header; + + header = elf_elfheader (info->section->owner); + if (is_newabi (header)) + mips_gpr_names = mips_gpr_names_newabi; + } +#endif + + /* Set ISA, architecture, and cp0 register names as best we can. */ +#if !defined(SYMTAB_AVAILABLE) && 0 + /* This is running out on a target machine, not in a host tool. + FIXME: Where does mips_target_info come from? */ + target_processor = mips_target_info.processor; + mips_isa = mips_target_info.isa; +#else + chosen_arch = choose_arch_by_number (info->mach); + if (chosen_arch != NULL) + { + mips_processor = chosen_arch->processor; + mips_isa = chosen_arch->isa; + mips_cp0_names = chosen_arch->cp0_names; + mips_cp0sel_names = chosen_arch->cp0sel_names; + mips_cp0sel_names_len = chosen_arch->cp0sel_names_len; + mips_hwr_names = chosen_arch->hwr_names; + } +#endif +} + +static void +parse_mips_dis_option (const char *option, unsigned int len) +{ + unsigned int i, optionlen, vallen; + const char *val; + const struct mips_abi_choice *chosen_abi; + const struct mips_arch_choice *chosen_arch; + + /* Look for the = that delimits the end of the option name. */ + for (i = 0; i < len; i++) + { + if (option[i] == '=') + break; + } + if (i == 0) /* Invalid option: no name before '='. */ + return; + if (i == len) /* Invalid option: no '='. */ + return; + if (i == (len - 1)) /* Invalid option: no value after '='. */ + return; + + optionlen = i; + val = option + (optionlen + 1); + vallen = len - (optionlen + 1); + + if (strncmp("gpr-names", option, optionlen) == 0 + && strlen("gpr-names") == optionlen) + { + chosen_abi = choose_abi_by_name (val, vallen); + if (chosen_abi != NULL) + mips_gpr_names = chosen_abi->gpr_names; + return; + } + + if (strncmp("fpr-names", option, optionlen) == 0 + && strlen("fpr-names") == optionlen) + { + chosen_abi = choose_abi_by_name (val, vallen); + if (chosen_abi != NULL) + mips_fpr_names = chosen_abi->fpr_names; + return; + } + + if (strncmp("cp0-names", option, optionlen) == 0 + && strlen("cp0-names") == optionlen) + { + chosen_arch = choose_arch_by_name (val, vallen); + if (chosen_arch != NULL) + { + mips_cp0_names = chosen_arch->cp0_names; + mips_cp0sel_names = chosen_arch->cp0sel_names; + mips_cp0sel_names_len = chosen_arch->cp0sel_names_len; + } + return; + } + + if (strncmp("hwr-names", option, optionlen) == 0 + && strlen("hwr-names") == optionlen) + { + chosen_arch = choose_arch_by_name (val, vallen); + if (chosen_arch != NULL) + mips_hwr_names = chosen_arch->hwr_names; + return; + } + + if (strncmp("reg-names", option, optionlen) == 0 + && strlen("reg-names") == optionlen) + { + /* We check both ABI and ARCH here unconditionally, so + that "numeric" will do the desirable thing: select + numeric register names for all registers. Other than + that, a given name probably won't match both. */ + chosen_abi = choose_abi_by_name (val, vallen); + if (chosen_abi != NULL) + { + mips_gpr_names = chosen_abi->gpr_names; + mips_fpr_names = chosen_abi->fpr_names; + } + chosen_arch = choose_arch_by_name (val, vallen); + if (chosen_arch != NULL) + { + mips_cp0_names = chosen_arch->cp0_names; + mips_cp0sel_names = chosen_arch->cp0sel_names; + mips_cp0sel_names_len = chosen_arch->cp0sel_names_len; + mips_hwr_names = chosen_arch->hwr_names; + } + return; + } + + /* Invalid option. */ +} + +static void +parse_mips_dis_options (const char *options) +{ + const char *option_end; + + if (options == NULL) + return; + + while (*options != '\0') + { + /* Skip empty options. */ + if (*options == ',') + { + options++; + continue; + } + + /* We know that *options is neither NUL or a comma. */ + option_end = options + 1; + while (*option_end != ',' && *option_end != '\0') + option_end++; + + parse_mips_dis_option (options, option_end - options); + + /* Go on to the next one. If option_end points to a comma, it + will be skipped above. */ + options = option_end; + } +} + +static const struct mips_cp0sel_name * +lookup_mips_cp0sel_name (const struct mips_cp0sel_name *names, + unsigned int len, + unsigned int cp0reg, + unsigned int sel) +{ + unsigned int i; + + for (i = 0; i < len; i++) + if (names[i].cp0reg == cp0reg && names[i].sel == sel) + return &names[i]; + return NULL; +} + +/* Print insn arguments for 32/64-bit code. */ + +static void +print_insn_args (const char *d, + register unsigned long int l, + bfd_vma pc, + struct disassemble_info *info, + const struct mips_opcode *opp) +{ + int op, delta; + unsigned int lsb, msb, msbd; + + lsb = 0; + + for (; *d != '\0'; d++) + { + switch (*d) + { + case ',': + case '(': + case ')': + case '[': + case ']': + (*info->fprintf_func) (info->stream, "%c", *d); + break; + + case '+': + /* Extension character; switch for second char. */ + d++; + switch (*d) + { + case '\0': + /* xgettext:c-format */ + (*info->fprintf_func) (info->stream, + _("# internal error, incomplete extension sequence (+)")); + return; + + case 'A': + lsb = (l >> OP_SH_SHAMT) & OP_MASK_SHAMT; + (*info->fprintf_func) (info->stream, "0x%x", lsb); + break; + + case 'B': + msb = (l >> OP_SH_INSMSB) & OP_MASK_INSMSB; + (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1); + break; + + case '1': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_UDI1) & OP_MASK_UDI1); + break; + + case '2': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_UDI2) & OP_MASK_UDI2); + break; + + case '3': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_UDI3) & OP_MASK_UDI3); + break; + + case '4': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_UDI4) & OP_MASK_UDI4); + break; + + case '5': /* 5-bit signed immediate in bit 16 */ + delta = ((l >> OP_SH_RT) & OP_MASK_RT); + if (delta & 0x10) { /* test sign bit */ + delta |= ~OP_MASK_RT; + } + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case '6': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_2BIT) & OP_MASK_2BIT); + break; + + case '7': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_3BIT) & OP_MASK_3BIT); + break; + + case '8': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_4BIT) & OP_MASK_4BIT); + break; + + case '9': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_5BIT) & OP_MASK_5BIT); + break; + + case ':': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_1BIT) & OP_MASK_1BIT); + break; + + case '!': /* 10-bit pc-relative target in bit 11 */ + delta = ((l >> OP_SH_10BIT) & OP_MASK_10BIT); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_10BIT; + } + info->target = (delta << 2) + pc + INSNLEN; + (*info->print_address_func) (info->target, info); + break; + + case '~': + (*info->fprintf_func) (info->stream, "0"); + break; + + case '@': + (*info->fprintf_func) (info->stream, "0x%lx", + ((l >> OP_SH_1_TO_4) & OP_MASK_1_TO_4)+1); + break; + + case '^': /* 10-bit signed immediate << 0 in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_IMM10; + } + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case '#': /* 10-bit signed immediate << 1 in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_IMM10; + } + (*info->fprintf_func) (info->stream, "%d", delta << 1); + break; + + case '$': /* 10-bit signed immediate << 2 in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_IMM10; + } + (*info->fprintf_func) (info->stream, "%d", delta << 2); + break; + + case '%': /* 10-bit signed immediate << 3 in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_IMM10; + } + (*info->fprintf_func) (info->stream, "%d", delta << 3); + break; + + case 'C': + case 'H': + msbd = (l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD; + (*info->fprintf_func) (info->stream, "0x%x", msbd + 1); + break; + + case 'D': + { + const struct mips_cp0sel_name *n; + unsigned int cp0reg, sel; + + cp0reg = (l >> OP_SH_RD) & OP_MASK_RD; + sel = (l >> OP_SH_SEL) & OP_MASK_SEL; + + /* CP0 register including 'sel' code for mtcN (et al.), to be + printed textually if known. If not known, print both + CP0 register name and sel numerically since CP0 register + with sel 0 may have a name unrelated to register being + printed. */ + n = lookup_mips_cp0sel_name(mips_cp0sel_names, + mips_cp0sel_names_len, cp0reg, sel); + if (n != NULL) + (*info->fprintf_func) (info->stream, "%s", n->name); + else + (*info->fprintf_func) (info->stream, "$%d,%d", cp0reg, sel); + break; + } + + case 'E': + lsb = ((l >> OP_SH_SHAMT) & OP_MASK_SHAMT) + 32; + (*info->fprintf_func) (info->stream, "0x%x", lsb); + break; + + case 'F': + msb = ((l >> OP_SH_INSMSB) & OP_MASK_INSMSB) + 32; + (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1); + break; + + case 'G': + msbd = ((l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD) + 32; + (*info->fprintf_func) (info->stream, "0x%x", msbd + 1); + break; + + case 'o': + switch (*(d+1)) { + case '1': + d++; + delta = l & ((1 << 18) - 1); + if (delta & 0x20000) { + delta |= ~0x1ffff; + } + break; + case '2': + d++; + delta = l & ((1 << 19) - 1); + if (delta & 0x40000) { + delta |= ~0x3ffff; + } + break; + default: + delta = (l >> OP_SH_DELTA_R6) & OP_MASK_DELTA_R6; + if (delta & 0x8000) { + delta |= ~0xffff; + } + } + + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case 'p': + /* Sign extend the displacement with 26 bits. */ + delta = (l >> OP_SH_DELTA) & OP_MASK_TARGET; + if (delta & 0x2000000) { + delta |= ~0x3FFFFFF; + } + info->target = (delta << 2) + pc + INSNLEN; + (*info->print_address_func) (info->target, info); + break; + + case 't': /* Coprocessor 0 reg name */ + (*info->fprintf_func) (info->stream, "%s", + mips_cp0_names[(l >> OP_SH_RT) & + OP_MASK_RT]); + break; + + case 'T': /* Coprocessor 0 reg name */ + { + const struct mips_cp0sel_name *n; + unsigned int cp0reg, sel; + + cp0reg = (l >> OP_SH_RT) & OP_MASK_RT; + sel = (l >> OP_SH_SEL) & OP_MASK_SEL; + + /* CP0 register including 'sel' code for mftc0, to be + printed textually if known. If not known, print both + CP0 register name and sel numerically since CP0 register + with sel 0 may have a name unrelated to register being + printed. */ + n = lookup_mips_cp0sel_name(mips_cp0sel_names, + mips_cp0sel_names_len, cp0reg, sel); + if (n != NULL) + (*info->fprintf_func) (info->stream, "%s", n->name); + else + (*info->fprintf_func) (info->stream, "$%d,%d", cp0reg, sel); + break; + } + + case 'd': + (*info->fprintf_func) (info->stream, "%s", + mips_wr_names[(l >> OP_SH_FD) & OP_MASK_FD]); + break; + + case 'e': + (*info->fprintf_func) (info->stream, "%s", + mips_wr_names[(l >> OP_SH_FS) & OP_MASK_FS]); + break; + + case 'f': + (*info->fprintf_func) (info->stream, "%s", + mips_wr_names[(l >> OP_SH_FT) & OP_MASK_FT]); + break; + + case 'g': + (*info->fprintf_func) (info->stream, "%s", + mips_msa_control_names_mips3264r2[(l >> OP_SH_MSACR11) + & OP_MASK_MSACR11]); + break; + + case 'h': + (*info->fprintf_func) (info->stream, "%s", + mips_msa_control_names_mips3264r2[(l >> OP_SH_MSACR6) + & OP_MASK_MSACR6]); + break; + + case 'i': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_GPR) & OP_MASK_GPR]); + break; + + default: + /* xgettext:c-format */ + (*info->fprintf_func) (info->stream, + _("# internal error, undefined extension sequence (+%c)"), + *d); + return; + } + break; + + case '2': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_BP) & OP_MASK_BP); + break; + + case '3': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_SA3) & OP_MASK_SA3); + break; + + case '4': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_SA4) & OP_MASK_SA4); + break; + + case '5': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_IMM8) & OP_MASK_IMM8); + break; + + case '6': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_RS) & OP_MASK_RS); + break; + + case '7': + (*info->fprintf_func) (info->stream, "$ac%ld", + (l >> OP_SH_DSPACC) & OP_MASK_DSPACC); + break; + + case '8': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_WRDSP) & OP_MASK_WRDSP); + break; + + case '9': + (*info->fprintf_func) (info->stream, "$ac%ld", + (l >> OP_SH_DSPACC_S) & OP_MASK_DSPACC_S); + break; + + case '0': /* dsp 6-bit signed immediate in bit 20 */ + delta = ((l >> OP_SH_DSPSFT) & OP_MASK_DSPSFT); + if (delta & 0x20) /* test sign bit */ + delta |= ~OP_MASK_DSPSFT; + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case ':': /* dsp 7-bit signed immediate in bit 19 */ + delta = ((l >> OP_SH_DSPSFT_7) & OP_MASK_DSPSFT_7); + if (delta & 0x40) /* test sign bit */ + delta |= ~OP_MASK_DSPSFT_7; + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case '\'': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_RDDSP) & OP_MASK_RDDSP); + break; + + case '@': /* dsp 10-bit signed immediate in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) /* test sign bit */ + delta |= ~OP_MASK_IMM10; + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case '!': + (*info->fprintf_func) (info->stream, "%ld", + (l >> OP_SH_MT_U) & OP_MASK_MT_U); + break; + + case '$': + (*info->fprintf_func) (info->stream, "%ld", + (l >> OP_SH_MT_H) & OP_MASK_MT_H); + break; + + case '*': + (*info->fprintf_func) (info->stream, "$ac%ld", + (l >> OP_SH_MTACC_T) & OP_MASK_MTACC_T); + break; + + case '&': + (*info->fprintf_func) (info->stream, "$ac%ld", + (l >> OP_SH_MTACC_D) & OP_MASK_MTACC_D); + break; + + case 'g': + /* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2. */ + (*info->fprintf_func) (info->stream, "$%ld", + (l >> OP_SH_RD) & OP_MASK_RD); + break; + + case 's': + case 'b': + case 'r': + case 'v': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_RS) & OP_MASK_RS]); + break; + + case 't': + case 'w': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]); + break; + + case 'i': + case 'u': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE); + break; + + case 'j': /* Same as i, but sign-extended. */ + case 'o': + delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; + + if (delta & 0x8000) + delta |= ~0xffff; + (*info->fprintf_func) (info->stream, "%d", + delta); + break; + + case 'h': + (*info->fprintf_func) (info->stream, "0x%x", + (unsigned int) ((l >> OP_SH_PREFX) + & OP_MASK_PREFX)); + break; + + case 'k': + (*info->fprintf_func) (info->stream, "0x%x", + (unsigned int) ((l >> OP_SH_CACHE) + & OP_MASK_CACHE)); + break; + + case 'a': + info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff) + | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2)); + /* For gdb disassembler, force odd address on jalx. */ + if (info->flavour == bfd_target_unknown_flavour + && strcmp (opp->name, "jalx") == 0) + info->target |= 1; + (*info->print_address_func) (info->target, info); + break; + + case 'p': + /* Sign extend the displacement. */ + delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; + if (delta & 0x8000) + delta |= ~0xffff; + info->target = (delta << 2) + pc + INSNLEN; + (*info->print_address_func) (info->target, info); + break; + + case 'd': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_RD) & OP_MASK_RD]); + break; + + case 'U': + { + /* First check for both rd and rt being equal. */ + unsigned int reg = (l >> OP_SH_RD) & OP_MASK_RD; + if (reg == ((l >> OP_SH_RT) & OP_MASK_RT)) + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[reg]); + else + { + /* If one is zero use the other. */ + if (reg == 0) + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]); + else if (((l >> OP_SH_RT) & OP_MASK_RT) == 0) + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[reg]); + else /* Bogus, result depends on processor. */ + (*info->fprintf_func) (info->stream, "%s or %s", + mips_gpr_names[reg], + mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]); + } + } + break; + + case 'z': + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]); + break; + + case '<': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_SHAMT) & OP_MASK_SHAMT); + break; + + case 'c': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_CODE) & OP_MASK_CODE); + break; + + case 'q': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_CODE2) & OP_MASK_CODE2); + break; + + case 'C': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_COPZ) & OP_MASK_COPZ); + break; + + case 'B': + (*info->fprintf_func) (info->stream, "0x%lx", + + (l >> OP_SH_CODE20) & OP_MASK_CODE20); + break; + + case 'J': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_CODE19) & OP_MASK_CODE19); + break; + + case 'S': + case 'V': + (*info->fprintf_func) (info->stream, "%s", + mips_fpr_names[(l >> OP_SH_FS) & OP_MASK_FS]); + break; + + case 'T': + case 'W': + (*info->fprintf_func) (info->stream, "%s", + mips_fpr_names[(l >> OP_SH_FT) & OP_MASK_FT]); + break; + + case 'D': + (*info->fprintf_func) (info->stream, "%s", + mips_fpr_names[(l >> OP_SH_FD) & OP_MASK_FD]); + break; + + case 'R': + (*info->fprintf_func) (info->stream, "%s", + mips_fpr_names[(l >> OP_SH_FR) & OP_MASK_FR]); + break; + + case 'E': + /* Coprocessor register for lwcN instructions, et al. + + Note that there is no load/store cp0 instructions, and + that FPU (cp1) instructions disassemble this field using + 'T' format. Therefore, until we gain understanding of + cp2 register names, we can simply print the register + numbers. */ + (*info->fprintf_func) (info->stream, "$%ld", + (l >> OP_SH_RT) & OP_MASK_RT); + break; + + case 'G': + /* Coprocessor register for mtcN instructions, et al. Note + that FPU (cp1) instructions disassemble this field using + 'S' format. Therefore, we only need to worry about cp0, + cp2, and cp3. */ + op = (l >> OP_SH_OP) & OP_MASK_OP; + if (op == OP_OP_COP0) + (*info->fprintf_func) (info->stream, "%s", + mips_cp0_names[(l >> OP_SH_RD) & OP_MASK_RD]); + else + (*info->fprintf_func) (info->stream, "$%ld", + (l >> OP_SH_RD) & OP_MASK_RD); + break; + + case 'K': + (*info->fprintf_func) (info->stream, "%s", + mips_hwr_names[(l >> OP_SH_RD) & OP_MASK_RD]); + break; + + case 'N': + (*info->fprintf_func) (info->stream, + ((opp->pinfo & (FP_D | FP_S)) != 0 + ? "$fcc%ld" : "$cc%ld"), + (l >> OP_SH_BCC) & OP_MASK_BCC); + break; + + case 'M': + (*info->fprintf_func) (info->stream, "$fcc%ld", + (l >> OP_SH_CCC) & OP_MASK_CCC); + break; + + case 'P': + (*info->fprintf_func) (info->stream, "%ld", + (l >> OP_SH_PERFREG) & OP_MASK_PERFREG); + break; + + case 'e': + (*info->fprintf_func) (info->stream, "%ld", + (l >> OP_SH_VECBYTE) & OP_MASK_VECBYTE); + break; + + case '%': + (*info->fprintf_func) (info->stream, "%ld", + (l >> OP_SH_VECALIGN) & OP_MASK_VECALIGN); + break; + + case 'H': + (*info->fprintf_func) (info->stream, "%ld", + (l >> OP_SH_SEL) & OP_MASK_SEL); + break; + + case 'O': + (*info->fprintf_func) (info->stream, "%ld", + (l >> OP_SH_ALN) & OP_MASK_ALN); + break; + + case 'Q': + { + unsigned int vsel = (l >> OP_SH_VSEL) & OP_MASK_VSEL; + + if ((vsel & 0x10) == 0) + { + int fmt; + + vsel &= 0x0f; + for (fmt = 0; fmt < 3; fmt++, vsel >>= 1) + if ((vsel & 1) == 0) + break; + (*info->fprintf_func) (info->stream, "$v%ld[%d]", + (l >> OP_SH_FT) & OP_MASK_FT, + vsel >> 1); + } + else if ((vsel & 0x08) == 0) + { + (*info->fprintf_func) (info->stream, "$v%ld", + (l >> OP_SH_FT) & OP_MASK_FT); + } + else + { + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_FT) & OP_MASK_FT); + } + } + break; + + case 'X': + (*info->fprintf_func) (info->stream, "$v%ld", + (l >> OP_SH_FD) & OP_MASK_FD); + break; + + case 'Y': + (*info->fprintf_func) (info->stream, "$v%ld", + (l >> OP_SH_FS) & OP_MASK_FS); + break; + + case 'Z': + (*info->fprintf_func) (info->stream, "$v%ld", + (l >> OP_SH_FT) & OP_MASK_FT); + break; + + default: + /* xgettext:c-format */ + (*info->fprintf_func) (info->stream, + _("# internal error, undefined modifier(%c)"), + *d); + return; + } + } +} + +/* Check if the object uses NewABI conventions. */ +#if 0 +static int +is_newabi (header) + Elf_Internal_Ehdr *header; +{ + /* There are no old-style ABIs which use 64-bit ELF. */ + if (header->e_ident[EI_CLASS] == ELFCLASS64) + return 1; + + /* If a 32-bit ELF file, n32 is a new-style ABI. */ + if ((header->e_flags & EF_MIPS_ABI2) != 0) + return 1; + + return 0; +} +#endif + +/* Print the mips instruction at address MEMADDR in debugged memory, + on using INFO. Returns length of the instruction, in bytes, which is + always INSNLEN. BIGENDIAN must be 1 if this is big-endian code, 0 if + this is little-endian code. */ + +static int +print_insn_mips (bfd_vma memaddr, + unsigned long int word, + struct disassemble_info *info) +{ + const struct mips_opcode *op; + static bfd_boolean init = 0; + static const struct mips_opcode *mips_hash[OP_MASK_OP + 1]; + + /* Build a hash table to shorten the search time. */ + if (! init) + { + unsigned int i; + + for (i = 0; i <= OP_MASK_OP; i++) + { + for (op = mips_opcodes; op < &mips_opcodes[NUMOPCODES]; op++) + { + if (op->pinfo == INSN_MACRO + || (no_aliases && (op->pinfo2 & INSN2_ALIAS))) + continue; + if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)) + { + mips_hash[i] = op; + break; + } + } + } + + init = 1; + } + + info->bytes_per_chunk = INSNLEN; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP]; + if (op != NULL) + { + for (; op < &mips_opcodes[NUMOPCODES]; op++) + { + if (op->pinfo != INSN_MACRO + && !(no_aliases && (op->pinfo2 & INSN2_ALIAS)) + && (word & op->mask) == op->match) + { + const char *d; + + /* We always allow to disassemble the jalx instruction. */ + if (! OPCODE_IS_MEMBER (op, mips_isa, mips_processor) + && strcmp (op->name, "jalx")) + continue; + + if (strcmp(op->name, "bovc") == 0 + || strcmp(op->name, "bnvc") == 0) { + if (((word >> OP_SH_RS) & OP_MASK_RS) < + ((word >> OP_SH_RT) & OP_MASK_RT)) { + continue; + } + } + if (strcmp(op->name, "bgezc") == 0 + || strcmp(op->name, "bltzc") == 0 + || strcmp(op->name, "bgezalc") == 0 + || strcmp(op->name, "bltzalc") == 0) { + if (((word >> OP_SH_RS) & OP_MASK_RS) != + ((word >> OP_SH_RT) & OP_MASK_RT)) { + continue; + } + } + + /* Figure out instruction type and branch delay information. */ + if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) + { + if ((info->insn_type & INSN_WRITE_GPR_31) != 0) + info->insn_type = dis_jsr; + else + info->insn_type = dis_branch; + info->branch_delay_insns = 1; + } + else if ((op->pinfo & (INSN_COND_BRANCH_DELAY + | INSN_COND_BRANCH_LIKELY)) != 0) + { + if ((info->insn_type & INSN_WRITE_GPR_31) != 0) + info->insn_type = dis_condjsr; + else + info->insn_type = dis_condbranch; + info->branch_delay_insns = 1; + } + else if ((op->pinfo & (INSN_STORE_MEMORY + | INSN_LOAD_MEMORY_DELAY)) != 0) + info->insn_type = dis_dref; + + (*info->fprintf_func) (info->stream, "%s", op->name); + + d = op->args; + if (d != NULL && *d != '\0') + { + (*info->fprintf_func) (info->stream, "\t"); + print_insn_args (d, word, memaddr, info, op); + } + + return INSNLEN; + } + } + } + + /* Handle undefined instructions. */ + info->insn_type = dis_noninsn; + (*info->fprintf_func) (info->stream, "0x%lx", word); + return INSNLEN; +} + +/* In an environment where we do not know the symbol type of the + instruction we are forced to assume that the low order bit of the + instructions' address may mark it as a mips16 instruction. If we + are single stepping, or the pc is within the disassembled function, + this works. Otherwise, we need a clue. Sometimes. */ + +static int +_print_insn_mips (bfd_vma memaddr, + struct disassemble_info *info, + enum bfd_endian endianness) +{ + bfd_byte buffer[INSNLEN]; + int status; + + set_default_mips_dis_options (info); + parse_mips_dis_options (info->disassembler_options); + +#if 0 +#if 1 + /* FIXME: If odd address, this is CLEARLY a mips 16 instruction. */ + /* Only a few tools will work this way. */ + if (memaddr & 0x01) + return print_insn_mips16 (memaddr, info); +#endif + +#if SYMTAB_AVAILABLE + if (info->mach == bfd_mach_mips16 + || (info->flavour == bfd_target_elf_flavour + && info->symbols != NULL + && ((*(elf_symbol_type **) info->symbols)->internal_elf_sym.st_other + == STO_MIPS16))) + return print_insn_mips16 (memaddr, info); +#endif +#endif + + status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info); + if (status == 0) + { + unsigned long insn; + + if (endianness == BFD_ENDIAN_BIG) + insn = (unsigned long) bfd_getb32 (buffer); + else + insn = (unsigned long) bfd_getl32 (buffer); + + return print_insn_mips (memaddr, insn, info); + } + else + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } +} + +int +print_insn_big_mips (bfd_vma memaddr, struct disassemble_info *info) +{ + return _print_insn_mips (memaddr, info, BFD_ENDIAN_BIG); +} + +int +print_insn_little_mips (bfd_vma memaddr, struct disassemble_info *info) +{ + return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE); +} + +/* Disassemble mips16 instructions. */ +#if 0 +static int +print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info) +{ + int status; + bfd_byte buffer[2]; + int length; + int insn; + bfd_boolean use_extend; + int extend = 0; + const struct mips_opcode *op, *opend; + + info->bytes_per_chunk = 2; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + status = (*info->read_memory_func) (memaddr, buffer, 2, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + length = 2; + + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb16 (buffer); + else + insn = bfd_getl16 (buffer); + + /* Handle the extend opcode specially. */ + use_extend = FALSE; + if ((insn & 0xf800) == 0xf000) + { + use_extend = TRUE; + extend = insn & 0x7ff; + + memaddr += 2; + + status = (*info->read_memory_func) (memaddr, buffer, 2, info); + if (status != 0) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb16 (buffer); + else + insn = bfd_getl16 (buffer); + + /* Check for an extend opcode followed by an extend opcode. */ + if ((insn & 0xf800) == 0xf000) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + info->insn_type = dis_noninsn; + return length; + } + + length += 2; + } + + /* FIXME: Should probably use a hash table on the major opcode here. */ + + opend = mips16_opcodes + bfd_mips16_num_opcodes; + for (op = mips16_opcodes; op < opend; op++) + { + if (op->pinfo != INSN_MACRO + && !(no_aliases && (op->pinfo2 & INSN2_ALIAS)) + && (insn & op->mask) == op->match) + { + const char *s; + + if (strchr (op->args, 'a') != NULL) + { + if (use_extend) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + info->insn_type = dis_noninsn; + return length - 2; + } + + use_extend = FALSE; + + memaddr += 2; + + status = (*info->read_memory_func) (memaddr, buffer, 2, + info); + if (status == 0) + { + use_extend = TRUE; + if (info->endian == BFD_ENDIAN_BIG) + extend = bfd_getb16 (buffer); + else + extend = bfd_getl16 (buffer); + length += 2; + } + } + + (*info->fprintf_func) (info->stream, "%s", op->name); + if (op->args[0] != '\0') + (*info->fprintf_func) (info->stream, "\t"); + + for (s = op->args; *s != '\0'; s++) + { + if (*s == ',' + && s[1] == 'w' + && (((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX) + == ((insn >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY))) + { + /* Skip the register and the comma. */ + ++s; + continue; + } + if (*s == ',' + && s[1] == 'v' + && (((insn >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ) + == ((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX))) + { + /* Skip the register and the comma. */ + ++s; + continue; + } + print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr, + info); + } + + if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) + { + info->branch_delay_insns = 1; + if (info->insn_type != dis_jsr) + info->insn_type = dis_branch; + } + + return length; + } + } + + if (use_extend) + (*info->fprintf_func) (info->stream, "0x%x", extend | 0xf000); + (*info->fprintf_func) (info->stream, "0x%x", insn); + info->insn_type = dis_noninsn; + + return length; +} + +/* Disassemble an operand for a mips16 instruction. */ + +static void +print_mips16_insn_arg (char type, + const struct mips_opcode *op, + int l, + bfd_boolean use_extend, + int extend, + bfd_vma memaddr, + struct disassemble_info *info) +{ + switch (type) + { + case ',': + case '(': + case ')': + (*info->fprintf_func) (info->stream, "%c", type); + break; + + case 'y': + case 'w': + (*info->fprintf_func) (info->stream, "%s", + mips16_reg_names(((l >> MIPS16OP_SH_RY) + & MIPS16OP_MASK_RY))); + break; + + case 'x': + case 'v': + (*info->fprintf_func) (info->stream, "%s", + mips16_reg_names(((l >> MIPS16OP_SH_RX) + & MIPS16OP_MASK_RX))); + break; + + case 'z': + (*info->fprintf_func) (info->stream, "%s", + mips16_reg_names(((l >> MIPS16OP_SH_RZ) + & MIPS16OP_MASK_RZ))); + break; + + case 'Z': + (*info->fprintf_func) (info->stream, "%s", + mips16_reg_names(((l >> MIPS16OP_SH_MOVE32Z) + & MIPS16OP_MASK_MOVE32Z))); + break; + + case '0': + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]); + break; + + case 'S': + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[29]); + break; + + case 'P': + (*info->fprintf_func) (info->stream, "$pc"); + break; + + case 'R': + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[31]); + break; + + case 'X': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[((l >> MIPS16OP_SH_REGR32) + & MIPS16OP_MASK_REGR32)]); + break; + + case 'Y': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]); + break; + + case '<': + case '>': + case '[': + case ']': + case '4': + case '5': + case 'H': + case 'W': + case 'D': + case 'j': + case '6': + case '8': + case 'V': + case 'C': + case 'U': + case 'k': + case 'K': + case 'p': + case 'q': + case 'A': + case 'B': + case 'E': + { + int immed, nbits, shift, signedp, extbits, pcrel, extu, branch; + + shift = 0; + signedp = 0; + extbits = 16; + pcrel = 0; + extu = 0; + branch = 0; + switch (type) + { + case '<': + nbits = 3; + immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ; + extbits = 5; + extu = 1; + break; + case '>': + nbits = 3; + immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX; + extbits = 5; + extu = 1; + break; + case '[': + nbits = 3; + immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ; + extbits = 6; + extu = 1; + break; + case ']': + nbits = 3; + immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX; + extbits = 6; + extu = 1; + break; + case '4': + nbits = 4; + immed = (l >> MIPS16OP_SH_IMM4) & MIPS16OP_MASK_IMM4; + signedp = 1; + extbits = 15; + break; + case '5': + nbits = 5; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 1; + break; + case 'H': + nbits = 5; + shift = 1; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 2; + break; + case 'W': + nbits = 5; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + if ((op->pinfo & MIPS16_INSN_READ_PC) == 0 + && (op->pinfo & MIPS16_INSN_READ_SP) == 0) + { + info->insn_type = dis_dref; + info->data_size = 4; + } + break; + case 'D': + nbits = 5; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'j': + nbits = 5; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + signedp = 1; + break; + case '6': + nbits = 6; + immed = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6; + break; + case '8': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + break; + case 'V': + nbits = 8; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + /* FIXME: This might be lw, or it might be addiu to $sp or + $pc. We assume it's load. */ + info->insn_type = dis_dref; + info->data_size = 4; + break; + case 'C': + nbits = 8; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'U': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + extu = 1; + break; + case 'k': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + break; + case 'K': + nbits = 8; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + break; + case 'p': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + pcrel = 1; + branch = 1; + info->insn_type = dis_condbranch; + break; + case 'q': + nbits = 11; + immed = (l >> MIPS16OP_SH_IMM11) & MIPS16OP_MASK_IMM11; + signedp = 1; + pcrel = 1; + branch = 1; + info->insn_type = dis_branch; + break; + case 'A': + nbits = 8; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + pcrel = 1; + /* FIXME: This can be lw or la. We assume it is lw. */ + info->insn_type = dis_dref; + info->data_size = 4; + break; + case 'B': + nbits = 5; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + pcrel = 1; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'E': + nbits = 5; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + pcrel = 1; + break; + default: + abort (); + } + + if (! use_extend) + { + if (signedp && immed >= (1 << (nbits - 1))) + immed -= 1 << nbits; + immed <<= shift; + if ((type == '<' || type == '>' || type == '[' || type == ']') + && immed == 0) + immed = 8; + } + else + { + if (extbits == 16) + immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0); + else if (extbits == 15) + immed |= ((extend & 0xf) << 11) | (extend & 0x7f0); + else + immed = ((extend >> 6) & 0x1f) | (extend & 0x20); + immed &= (1 << extbits) - 1; + if (! extu && immed >= (1 << (extbits - 1))) + immed -= 1 << extbits; + } + + if (! pcrel) + (*info->fprintf_func) (info->stream, "%d", immed); + else + { + bfd_vma baseaddr; + + if (branch) + { + immed *= 2; + baseaddr = memaddr + 2; + } + else if (use_extend) + baseaddr = memaddr - 2; + else + { + int status; + bfd_byte buffer[2]; + + baseaddr = memaddr; + + /* If this instruction is in the delay slot of a jr + instruction, the base address is the address of the + jr instruction. If it is in the delay slot of jalr + instruction, the base address is the address of the + jalr instruction. This test is unreliable: we have + no way of knowing whether the previous word is + instruction or data. */ + status = (*info->read_memory_func) (memaddr - 4, buffer, 2, + info); + if (status == 0 + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0xf800) == 0x1800)) + baseaddr = memaddr - 4; + else + { + status = (*info->read_memory_func) (memaddr - 2, buffer, + 2, info); + if (status == 0 + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0xf81f) == 0xe800)) + baseaddr = memaddr - 2; + } + } + info->target = (baseaddr & ~((1 << shift) - 1)) + immed; + if (pcrel && branch + && info->flavour == bfd_target_unknown_flavour) + /* For gdb disassembler, maintain odd address. */ + info->target |= 1; + (*info->print_address_func) (info->target, info); + } + } + break; + + case 'a': + { + int jalx = l & 0x400; + + if (! use_extend) + extend = 0; + l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2); + if (!jalx && info->flavour == bfd_target_unknown_flavour) + /* For gdb disassembler, maintain odd address. */ + l |= 1; + } + info->target = ((memaddr + 4) & ~(bfd_vma) 0x0fffffff) | l; + (*info->print_address_func) (info->target, info); + info->insn_type = dis_jsr; + info->branch_delay_insns = 1; + break; + + case 'l': + case 'L': + { + int need_comma, amask, smask; + + need_comma = 0; + + l = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6; + + amask = (l >> 3) & 7; + + if (amask > 0 && amask < 5) + { + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[4]); + if (amask > 1) + (*info->fprintf_func) (info->stream, "-%s", + mips_gpr_names[amask + 3]); + need_comma = 1; + } + + smask = (l >> 1) & 3; + if (smask == 3) + { + (*info->fprintf_func) (info->stream, "%s??", + need_comma ? "," : ""); + need_comma = 1; + } + else if (smask > 0) + { + (*info->fprintf_func) (info->stream, "%s%s", + need_comma ? "," : "", + mips_gpr_names[16]); + if (smask > 1) + (*info->fprintf_func) (info->stream, "-%s", + mips_gpr_names[smask + 15]); + need_comma = 1; + } + + if (l & 1) + { + (*info->fprintf_func) (info->stream, "%s%s", + need_comma ? "," : "", + mips_gpr_names[31]); + need_comma = 1; + } + + if (amask == 5 || amask == 6) + { + (*info->fprintf_func) (info->stream, "%s$f0", + need_comma ? "," : ""); + if (amask == 6) + (*info->fprintf_func) (info->stream, "-$f1"); + } + } + break; + + case 'm': + case 'M': + /* MIPS16e save/restore. */ + { + int need_comma = 0; + int amask, args, statics; + int nsreg, smask; + int framesz; + int i, j; + + l = l & 0x7f; + if (use_extend) + l |= extend << 16; + + amask = (l >> 16) & 0xf; + if (amask == MIPS16_ALL_ARGS) + { + args = 4; + statics = 0; + } + else if (amask == MIPS16_ALL_STATICS) + { + args = 0; + statics = 4; + } + else + { + args = amask >> 2; + statics = amask & 3; + } + + if (args > 0) { + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[4]); + if (args > 1) + (*info->fprintf_func) (info->stream, "-%s", + mips_gpr_names[4 + args - 1]); + need_comma = 1; + } + + framesz = (((l >> 16) & 0xf0) | (l & 0x0f)) * 8; + if (framesz == 0 && !use_extend) + framesz = 128; + + (*info->fprintf_func) (info->stream, "%s%d", + need_comma ? "," : "", + framesz); + + if (l & 0x40) /* $ra */ + (*info->fprintf_func) (info->stream, ",%s", mips_gpr_names[31]); + + nsreg = (l >> 24) & 0x7; + smask = 0; + if (l & 0x20) /* $s0 */ + smask |= 1 << 0; + if (l & 0x10) /* $s1 */ + smask |= 1 << 1; + if (nsreg > 0) /* $s2-$s8 */ + smask |= ((1 << nsreg) - 1) << 2; + + /* Find first set static reg bit. */ + for (i = 0; i < 9; i++) + { + if (smask & (1 << i)) + { + (*info->fprintf_func) (info->stream, ",%s", + mips_gpr_names[i == 8 ? 30 : (16 + i)]); + /* Skip over string of set bits. */ + for (j = i; smask & (2 << j); j++) + continue; + if (j > i) + (*info->fprintf_func) (info->stream, "-%s", + mips_gpr_names[j == 8 ? 30 : (16 + j)]); + i = j + 1; + } + } + + /* Statics $ax - $a3. */ + if (statics == 1) + (*info->fprintf_func) (info->stream, ",%s", mips_gpr_names[7]); + else if (statics > 0) + (*info->fprintf_func) (info->stream, ",%s-%s", + mips_gpr_names[7 - statics + 1], + mips_gpr_names[7]); + } + break; + + default: + /* xgettext:c-format */ + (*info->fprintf_func) + (info->stream, + _("# internal disassembler error, unrecognised modifier (%c)"), + type); + abort (); + } +} + +void +print_mips_disassembler_options (FILE *stream) +{ + unsigned int i; + + fprintf (stream, _("\n\ +The following MIPS specific disassembler options are supported for use\n\ +with the -M switch (multiple options should be separated by commas):\n")); + + fprintf (stream, _("\n\ + gpr-names=ABI Print GPR names according to specified ABI.\n\ + Default: based on binary being disassembled.\n")); + + fprintf (stream, _("\n\ + fpr-names=ABI Print FPR names according to specified ABI.\n\ + Default: numeric.\n")); + + fprintf (stream, _("\n\ + cp0-names=ARCH Print CP0 register names according to\n\ + specified architecture.\n\ + Default: based on binary being disassembled.\n")); + + fprintf (stream, _("\n\ + hwr-names=ARCH Print HWR names according to specified\n\ + architecture.\n\ + Default: based on binary being disassembled.\n")); + + fprintf (stream, _("\n\ + reg-names=ABI Print GPR and FPR names according to\n\ + specified ABI.\n")); + + fprintf (stream, _("\n\ + reg-names=ARCH Print CP0 register and HWR names according to\n\ + specified architecture.\n")); + + fprintf (stream, _("\n\ + For the options above, the following values are supported for \"ABI\":\n\ + ")); + for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++) + fprintf (stream, " %s", mips_abi_choices[i].name); + fprintf (stream, _("\n")); + + fprintf (stream, _("\n\ + For the options above, The following values are supported for \"ARCH\":\n\ + ")); + for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++) + if (*mips_arch_choices[i].name != '\0') + fprintf (stream, " %s", mips_arch_choices[i].name); + fprintf (stream, _("\n")); + + fprintf (stream, _("\n")); +} +#endif diff --git a/disas/moxie.c b/disas/moxie.c new file mode 100644 index 0000000..4c5f180 --- /dev/null +++ b/disas/moxie.c @@ -0,0 +1,360 @@ +/* Disassemble moxie instructions. + Copyright (c) 2009 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include +#define STATIC_TABLE +#define DEFINE_TABLE + +#include "disas/bfd.h" + +static void *stream; + +/* Form 1 instructions come in different flavors: + + Some have no arguments (MOXIE_F1_NARG) + Some only use the A operand (MOXIE_F1_A) + Some use A and B registers (MOXIE_F1_AB) + Some use A and consume a 4 byte immediate value (MOXIE_F1_A4) + Some use just a 4 byte immediate value (MOXIE_F1_4) + Some use just a 4 byte memory address (MOXIE_F1_M) + Some use B and an indirect A (MOXIE_F1_AiB) + Some use A and an indirect B (MOXIE_F1_ABi) + Some consume a 4 byte immediate value and use X (MOXIE_F1_4A) + Some use B and an indirect A plus 4 bytes (MOXIE_F1_AiB4) + Some use A and an indirect B plus 4 bytes (MOXIE_F1_ABi4) + + Form 2 instructions also come in different flavors: + + Some have no arguments (MOXIE_F2_NARG) + Some use the A register and an 8-bit value (MOXIE_F2_A8V) + + Form 3 instructions also come in different flavors: + + Some have no arguments (MOXIE_F3_NARG) + Some have a 10-bit PC relative operand (MOXIE_F3_PCREL). */ + +#define MOXIE_F1_NARG 0x100 +#define MOXIE_F1_A 0x101 +#define MOXIE_F1_AB 0x102 +/* #define MOXIE_F1_ABC 0x103 */ +#define MOXIE_F1_A4 0x104 +#define MOXIE_F1_4 0x105 +#define MOXIE_F1_AiB 0x106 +#define MOXIE_F1_ABi 0x107 +#define MOXIE_F1_4A 0x108 +#define MOXIE_F1_AiB4 0x109 +#define MOXIE_F1_ABi4 0x10a +#define MOXIE_F1_M 0x10b + +#define MOXIE_F2_NARG 0x200 +#define MOXIE_F2_A8V 0x201 + +#define MOXIE_F3_NARG 0x300 +#define MOXIE_F3_PCREL 0x301 + +typedef struct moxie_opc_info_t { + short opcode; + unsigned itype; + const char * name; +} moxie_opc_info_t; + +extern const moxie_opc_info_t moxie_form1_opc_info[64]; +extern const moxie_opc_info_t moxie_form2_opc_info[4]; +extern const moxie_opc_info_t moxie_form3_opc_info[16]; + +/* The moxie processor's 16-bit instructions come in two forms: + + FORM 1 instructions start with a 0 bit... + + 0oooooooaaaabbbb + 0 F + + ooooooo - form 1 opcode number + aaaa - operand A + bbbb - operand B + + FORM 2 instructions start with bits "10"... + + 10ooaaaavvvvvvvv + 0 F + + oo - form 2 opcode number + aaaa - operand A + vvvvvvvv - 8-bit immediate value + + FORM 3 instructions start with a bits "11"... + + 11oooovvvvvvvvvv + 0 F + + oooo - form 3 opcode number + vvvvvvvvvv - 10-bit immediate value. */ + +const moxie_opc_info_t moxie_form1_opc_info[64] = + { + { 0x00, MOXIE_F1_NARG, "nop" }, + { 0x01, MOXIE_F1_A4, "ldi.l" }, + { 0x02, MOXIE_F1_AB, "mov" }, + { 0x03, MOXIE_F1_M, "jsra" }, + { 0x04, MOXIE_F1_NARG, "ret" }, + { 0x05, MOXIE_F1_AB, "add.l" }, + { 0x06, MOXIE_F1_AB, "push" }, + { 0x07, MOXIE_F1_AB, "pop" }, + { 0x08, MOXIE_F1_A4, "lda.l" }, + { 0x09, MOXIE_F1_4A, "sta.l" }, + { 0x0a, MOXIE_F1_ABi, "ld.l" }, + { 0x0b, MOXIE_F1_AiB, "st.l" }, + { 0x0c, MOXIE_F1_ABi4, "ldo.l" }, + { 0x0d, MOXIE_F1_AiB4, "sto.l" }, + { 0x0e, MOXIE_F1_AB, "cmp" }, + { 0x0f, MOXIE_F1_NARG, "bad" }, + { 0x10, MOXIE_F1_NARG, "bad" }, + { 0x11, MOXIE_F1_NARG, "bad" }, + { 0x12, MOXIE_F1_NARG, "bad" }, + { 0x13, MOXIE_F1_NARG, "bad" }, + { 0x14, MOXIE_F1_NARG, "bad" }, + { 0x15, MOXIE_F1_NARG, "bad" }, + { 0x16, MOXIE_F1_NARG, "bad" }, + { 0x17, MOXIE_F1_NARG, "bad" }, + { 0x18, MOXIE_F1_NARG, "bad" }, + { 0x19, MOXIE_F1_A, "jsr" }, + { 0x1a, MOXIE_F1_M, "jmpa" }, + { 0x1b, MOXIE_F1_A4, "ldi.b" }, + { 0x1c, MOXIE_F1_ABi, "ld.b" }, + { 0x1d, MOXIE_F1_A4, "lda.b" }, + { 0x1e, MOXIE_F1_AiB, "st.b" }, + { 0x1f, MOXIE_F1_4A, "sta.b" }, + { 0x20, MOXIE_F1_A4, "ldi.s" }, + { 0x21, MOXIE_F1_ABi, "ld.s" }, + { 0x22, MOXIE_F1_A4, "lda.s" }, + { 0x23, MOXIE_F1_AiB, "st.s" }, + { 0x24, MOXIE_F1_4A, "sta.s" }, + { 0x25, MOXIE_F1_A, "jmp" }, + { 0x26, MOXIE_F1_AB, "and" }, + { 0x27, MOXIE_F1_AB, "lshr" }, + { 0x28, MOXIE_F1_AB, "ashl" }, + { 0x29, MOXIE_F1_AB, "sub.l" }, + { 0x2a, MOXIE_F1_AB, "neg" }, + { 0x2b, MOXIE_F1_AB, "or" }, + { 0x2c, MOXIE_F1_AB, "not" }, + { 0x2d, MOXIE_F1_AB, "ashr" }, + { 0x2e, MOXIE_F1_AB, "xor" }, + { 0x2f, MOXIE_F1_AB, "mul.l" }, + { 0x30, MOXIE_F1_4, "swi" }, + { 0x31, MOXIE_F1_AB, "div.l" }, + { 0x32, MOXIE_F1_AB, "udiv.l" }, + { 0x33, MOXIE_F1_AB, "mod.l" }, + { 0x34, MOXIE_F1_AB, "umod.l" }, + { 0x35, MOXIE_F1_NARG, "brk" }, + { 0x36, MOXIE_F1_ABi4, "ldo.b" }, + { 0x37, MOXIE_F1_AiB4, "sto.b" }, + { 0x38, MOXIE_F1_ABi4, "ldo.s" }, + { 0x39, MOXIE_F1_AiB4, "sto.s" }, + { 0x3a, MOXIE_F1_NARG, "bad" }, + { 0x3b, MOXIE_F1_NARG, "bad" }, + { 0x3c, MOXIE_F1_NARG, "bad" }, + { 0x3d, MOXIE_F1_NARG, "bad" }, + { 0x3e, MOXIE_F1_NARG, "bad" }, + { 0x3f, MOXIE_F1_NARG, "bad" } + }; + +const moxie_opc_info_t moxie_form2_opc_info[4] = + { + { 0x00, MOXIE_F2_A8V, "inc" }, + { 0x01, MOXIE_F2_A8V, "dec" }, + { 0x02, MOXIE_F2_A8V, "gsr" }, + { 0x03, MOXIE_F2_A8V, "ssr" } + }; + +const moxie_opc_info_t moxie_form3_opc_info[16] = + { + { 0x00, MOXIE_F3_PCREL,"beq" }, + { 0x01, MOXIE_F3_PCREL,"bne" }, + { 0x02, MOXIE_F3_PCREL,"blt" }, + { 0x03, MOXIE_F3_PCREL,"bgt" }, + { 0x04, MOXIE_F3_PCREL,"bltu" }, + { 0x05, MOXIE_F3_PCREL,"bgtu" }, + { 0x06, MOXIE_F3_PCREL,"bge" }, + { 0x07, MOXIE_F3_PCREL,"ble" }, + { 0x08, MOXIE_F3_PCREL,"bgeu" }, + { 0x09, MOXIE_F3_PCREL,"bleu" }, + { 0x0a, MOXIE_F3_NARG, "bad" }, + { 0x0b, MOXIE_F3_NARG, "bad" }, + { 0x0c, MOXIE_F3_NARG, "bad" }, + { 0x0d, MOXIE_F3_NARG, "bad" }, + { 0x0e, MOXIE_F3_NARG, "bad" }, + { 0x0f, MOXIE_F3_NARG, "bad" } + }; + +/* Macros to extract operands from the instruction word. */ +#define OP_A(i) ((i >> 4) & 0xf) +#define OP_B(i) (i & 0xf) +#define INST2OFFSET(o) ((((signed short)((o & ((1<<10)-1))<<6))>>6)<<1) + +static const char * reg_names[16] = + { "$fp", "$sp", "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", + "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13" }; + +int +print_insn_moxie(bfd_vma addr, struct disassemble_info * info) +{ + int length = 2; + int status; + stream = info->stream; + const moxie_opc_info_t * opcode; + bfd_byte buffer[4]; + unsigned short iword; + fprintf_function fpr = info->fprintf_func; + + if ((status = info->read_memory_func(addr, buffer, 2, info))) + goto fail; + iword = (bfd_getb16(buffer) >> 16); + + /* Form 1 instructions have the high bit set to 0. */ + if ((iword & (1<<15)) == 0) { + /* Extract the Form 1 opcode. */ + opcode = &moxie_form1_opc_info[iword >> 8]; + switch (opcode->itype) { + case MOXIE_F1_NARG: + fpr(stream, "%s", opcode->name); + break; + case MOXIE_F1_A: + fpr(stream, "%s\t%s", opcode->name, + reg_names[OP_A(iword)]); + break; + case MOXIE_F1_AB: + fpr(stream, "%s\t%s, %s", opcode->name, + reg_names[OP_A(iword)], + reg_names[OP_B(iword)]); + break; + case MOXIE_F1_A4: + { + unsigned imm; + if ((status = info->read_memory_func(addr + 2, buffer, 4, info))) + goto fail; + imm = bfd_getb32(buffer); + fpr(stream, "%s\t%s, 0x%x", opcode->name, + reg_names[OP_A(iword)], imm); + length = 6; + } + break; + case MOXIE_F1_4: + { + unsigned imm; + if ((status = info->read_memory_func(addr + 2, buffer, 4, info))) + goto fail; + imm = bfd_getb32(buffer); + fpr(stream, "%s\t0x%x", opcode->name, imm); + length = 6; + } + break; + case MOXIE_F1_M: + { + unsigned imm; + if ((status = info->read_memory_func(addr + 2, buffer, 4, info))) + goto fail; + imm = bfd_getb32(buffer); + fpr(stream, "%s\t", opcode->name); + info->print_address_func((bfd_vma) imm, info); + length = 6; + } + break; + case MOXIE_F1_AiB: + fpr (stream, "%s\t(%s), %s", opcode->name, + reg_names[OP_A(iword)], reg_names[OP_B(iword)]); + break; + case MOXIE_F1_ABi: + fpr(stream, "%s\t%s, (%s)", opcode->name, + reg_names[OP_A(iword)], reg_names[OP_B(iword)]); + break; + case MOXIE_F1_4A: + { + unsigned imm; + if ((status = info->read_memory_func(addr + 2, buffer, 4, info))) + goto fail; + imm = bfd_getb32(buffer); + fpr(stream, "%s\t0x%x, %s", + opcode->name, imm, reg_names[OP_A(iword)]); + length = 6; + } + break; + case MOXIE_F1_AiB4: + { + unsigned imm; + if ((status = info->read_memory_func(addr+2, buffer, 4, info))) + goto fail; + imm = bfd_getb32(buffer); + fpr(stream, "%s\t0x%x(%s), %s", opcode->name, + imm, + reg_names[OP_A(iword)], + reg_names[OP_B(iword)]); + length = 6; + } + break; + case MOXIE_F1_ABi4: + { + unsigned imm; + if ((status = info->read_memory_func(addr+2, buffer, 4, info))) + goto fail; + imm = bfd_getb32(buffer); + fpr(stream, "%s\t%s, 0x%x(%s)", + opcode->name, + reg_names[OP_A(iword)], + imm, + reg_names[OP_B(iword)]); + length = 6; + } + break; + default: + abort(); + } + } + else if ((iword & (1<<14)) == 0) { + /* Extract the Form 2 opcode. */ + opcode = &moxie_form2_opc_info[(iword >> 12) & 3]; + switch (opcode->itype) { + case MOXIE_F2_A8V: + fpr(stream, "%s\t%s, 0x%x", + opcode->name, + reg_names[(iword >> 8) & 0xf], + iword & ((1 << 8) - 1)); + break; + case MOXIE_F2_NARG: + fpr(stream, "%s", opcode->name); + break; + default: + abort(); + } + } else { + /* Extract the Form 3 opcode. */ + opcode = &moxie_form3_opc_info[(iword >> 10) & 15]; + switch (opcode->itype) { + case MOXIE_F3_PCREL: + fpr(stream, "%s\t", opcode->name); + info->print_address_func((bfd_vma) (addr + INST2OFFSET(iword) + 2), + info); + break; + default: + abort(); + } + } + + return length; + + fail: + info->memory_error_func(status, addr, info); + return -1; +} diff --git a/disas/ppc.c b/disas/ppc.c new file mode 100644 index 0000000..99c4cbc --- /dev/null +++ b/disas/ppc.c @@ -0,0 +1,5413 @@ +/* ppc-dis.c -- Disassemble PowerPC instructions + Copyright 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, +see . */ +#include "disas/bfd.h" +#define BFD_DEFAULT_TARGET_SIZE 64 + +/* ppc.h -- Header file for PowerPC opcode table + Copyright 1994, 1995, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + 2007 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, +see . */ + +/* The opcode table is an array of struct powerpc_opcode. */ + +struct powerpc_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned long opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned long mask; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The defined values + are listed below. */ + unsigned long flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[8]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct powerpc_opcode powerpc_opcodes[]; +extern const int powerpc_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* Opcode is defined for the PowerPC architecture. */ +#define PPC_OPCODE_PPC 1 + +/* Opcode is defined for the POWER (RS/6000) architecture. */ +#define PPC_OPCODE_POWER 2 + +/* Opcode is defined for the POWER2 (Rios 2) architecture. */ +#define PPC_OPCODE_POWER2 4 + +/* Opcode is only defined on 32 bit architectures. */ +#define PPC_OPCODE_32 8 + +/* Opcode is only defined on 64 bit architectures. */ +#define PPC_OPCODE_64 0x10 + +/* Opcode is supported by the Motorola PowerPC 601 processor. The 601 + is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions, + but it also supports many additional POWER instructions. */ +#define PPC_OPCODE_601 0x20 + +/* Opcode is supported in both the Power and PowerPC architectures + (ie, compiler's -mcpu=common or assembler's -mcom). */ +#define PPC_OPCODE_COMMON 0x40 + +/* Opcode is supported for any Power or PowerPC platform (this is + for the assembler's -many option, and it eliminates duplicates). */ +#define PPC_OPCODE_ANY 0x80 + +/* Opcode is supported as part of the 64-bit bridge. */ +#define PPC_OPCODE_64_BRIDGE 0x100 + +/* Opcode is supported by Altivec Vector Unit */ +#define PPC_OPCODE_ALTIVEC 0x200 + +/* Opcode is supported by PowerPC 403 processor. */ +#define PPC_OPCODE_403 0x400 + +/* Opcode is supported by PowerPC BookE processor. */ +#define PPC_OPCODE_BOOKE 0x800 + +/* Opcode is only supported by 64-bit PowerPC BookE processor. */ +#define PPC_OPCODE_BOOKE64 0x1000 + +/* Opcode is supported by PowerPC 440 processor. */ +#define PPC_OPCODE_440 0x2000 + +/* Opcode is only supported by Power4 architecture. */ +#define PPC_OPCODE_POWER4 0x4000 + +/* Opcode isn't supported by Power4 architecture. */ +#define PPC_OPCODE_NOPOWER4 0x8000 + +/* Opcode is only supported by POWERPC Classic architecture. */ +#define PPC_OPCODE_CLASSIC 0x10000 + +/* Opcode is only supported by e500x2 Core. */ +#define PPC_OPCODE_SPE 0x20000 + +/* Opcode is supported by e500x2 Integer select APU. */ +#define PPC_OPCODE_ISEL 0x40000 + +/* Opcode is an e500 SPE floating point instruction. */ +#define PPC_OPCODE_EFS 0x80000 + +/* Opcode is supported by branch locking APU. */ +#define PPC_OPCODE_BRLOCK 0x100000 + +/* Opcode is supported by performance monitor APU. */ +#define PPC_OPCODE_PMR 0x200000 + +/* Opcode is supported by cache locking APU. */ +#define PPC_OPCODE_CACHELCK 0x400000 + +/* Opcode is supported by machine check APU. */ +#define PPC_OPCODE_RFMCI 0x800000 + +/* Opcode is only supported by Power5 architecture. */ +#define PPC_OPCODE_POWER5 0x1000000 + +/* Opcode is supported by PowerPC e300 family. */ +#define PPC_OPCODE_E300 0x2000000 + +/* Opcode is only supported by Power6 architecture. */ +#define PPC_OPCODE_POWER6 0x4000000 + +/* Opcode is only supported by PowerPC Cell family. */ +#define PPC_OPCODE_CELL 0x8000000 + +/* A macro to extract the major opcode from an instruction. */ +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +/* The operands table is an array of struct powerpc_operand. */ + +struct powerpc_operand +{ + /* A bitmask of bits in the operand. */ + unsigned int bitm; + + /* How far the operand is left shifted in the instruction. + -1 to indicate that BITM and SHIFT cannot be used to determine + where the operand goes in the insn. */ + int shift; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & o->bitm) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the operand value). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned long (*insert) + (unsigned long instruction, long op, int dialect, const char **errmsg); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = (i >> o->shift) & o->bitm; + if ((o->flags & PPC_OPERAND_SIGNED) != 0) + sign_extend (op); + (i is the instruction, o is a pointer to this structure, and op + is the result). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + long (*extract) (unsigned long instruction, int dialect, int *invalid); + + /* One bit syntax flags. */ + unsigned long flags; +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +extern const struct powerpc_operand powerpc_operands[]; +extern const unsigned int num_powerpc_operands; + +/* Values defined for the flags field of a struct powerpc_operand. */ + +/* This operand takes signed values. */ +#define PPC_OPERAND_SIGNED (0x1) + +/* This operand takes signed values, but also accepts a full positive + range of values when running in 32 bit mode. That is, if bits is + 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode, + this flag is ignored. */ +#define PPC_OPERAND_SIGNOPT (0x2) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics such as mr, for which two + operands fields are identical. The assembler should call the + insert function with any op value. The disassembler should call + the extract function, ignore the return value, and check the value + placed in the valid argument. */ +#define PPC_OPERAND_FAKE (0x4) + +/* The next operand should be wrapped in parentheses rather than + separated from this one by a comma. This is used for the load and + store instructions which want their operands to look like + reg,displacement(reg) + */ +#define PPC_OPERAND_PARENS (0x8) + +/* This operand may use the symbolic names for the CR fields, which + are + lt 0 gt 1 eq 2 so 3 un 3 + cr0 0 cr1 1 cr2 2 cr3 3 + cr4 4 cr5 5 cr6 6 cr7 7 + These may be combined arithmetically, as in cr2*4+gt. These are + only supported on the PowerPC, not the POWER. */ +#define PPC_OPERAND_CR (0x10) + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define PPC_OPERAND_GPR (0x20) + +/* Like PPC_OPERAND_GPR, but don't print a leading 'r' for r0. */ +#define PPC_OPERAND_GPR_0 (0x40) + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define PPC_OPERAND_FPR (0x80) + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_RELATIVE (0x100) + +/* This operand is an absolute branch address. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_ABSOLUTE (0x200) + +/* This operand is optional, and is zero if omitted. This is used for + example, in the optional BF field in the comparison instructions. The + assembler must count the number of operands remaining on the line, + and the number of operands remaining for the opcode, and decide + whether this operand is present or not. The disassembler should + print this operand out only if it is not zero. */ +#define PPC_OPERAND_OPTIONAL (0x400) + +/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand + is omitted, then for the next operand use this operand value plus + 1, ignoring the next operand field for the opcode. This wretched + hack is needed because the Power rotate instructions can take + either 4 or 5 operands. The disassembler should print this operand + out regardless of the PPC_OPERAND_OPTIONAL field. */ +#define PPC_OPERAND_NEXT (0x800) + +/* This operand should be regarded as a negative number for the + purposes of overflow checking (i.e., the normal most negative + number is disallowed and one more than the normal most positive + number is allowed). This flag will only be set for a signed + operand. */ +#define PPC_OPERAND_NEGATIVE (0x1000) + +/* This operand names a vector unit register. The disassembler + prints these with a leading 'v'. */ +#define PPC_OPERAND_VR (0x2000) + +/* This operand is for the DS field in a DS form instruction. */ +#define PPC_OPERAND_DS (0x4000) + +/* This operand is for the DQ field in a DQ form instruction. */ +#define PPC_OPERAND_DQ (0x8000) + +/* Valid range of operand is 0..n rather than 0..n-1. */ +#define PPC_OPERAND_PLUS1 (0x10000) + +/* The POWER and PowerPC assemblers use a few macros. We keep them + with the operands table for simplicity. The macro table is an + array of struct powerpc_macro. */ + +struct powerpc_macro +{ + /* The macro name. */ + const char *name; + + /* The number of operands the macro takes. */ + unsigned int operands; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The values are the + same as those for the struct powerpc_opcode flags field. */ + unsigned long flags; + + /* A format string to turn the macro into a normal instruction. + Each %N in the string is replaced with operand number N (zero + based). */ + const char *format; +}; + +extern const struct powerpc_macro powerpc_macros[]; +extern const int powerpc_num_macros; + +/* ppc-opc.c -- PowerPC opcode list + Copyright 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. + If not, see . */ + +/* This file holds the PowerPC opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bat (unsigned long, long, int, const char **); +static long extract_bat (unsigned long, int, int *); +static unsigned long insert_bba (unsigned long, long, int, const char **); +static long extract_bba (unsigned long, int, int *); +static unsigned long insert_bdm (unsigned long, long, int, const char **); +static long extract_bdm (unsigned long, int, int *); +static unsigned long insert_bdp (unsigned long, long, int, const char **); +static long extract_bdp (unsigned long, int, int *); +static unsigned long insert_bo (unsigned long, long, int, const char **); +static long extract_bo (unsigned long, int, int *); +static unsigned long insert_boe (unsigned long, long, int, const char **); +static long extract_boe (unsigned long, int, int *); +static unsigned long insert_fxm (unsigned long, long, int, const char **); +static long extract_fxm (unsigned long, int, int *); +static unsigned long insert_mbe (unsigned long, long, int, const char **); +static long extract_mbe (unsigned long, int, int *); +static unsigned long insert_mb6 (unsigned long, long, int, const char **); +static long extract_mb6 (unsigned long, int, int *); +static long extract_nb (unsigned long, int, int *); +static unsigned long insert_nsi (unsigned long, long, int, const char **); +static long extract_nsi (unsigned long, int, int *); +static unsigned long insert_ral (unsigned long, long, int, const char **); +static unsigned long insert_ram (unsigned long, long, int, const char **); +static unsigned long insert_raq (unsigned long, long, int, const char **); +static unsigned long insert_ras (unsigned long, long, int, const char **); +static unsigned long insert_rbs (unsigned long, long, int, const char **); +static long extract_rbs (unsigned long, int, int *); +static unsigned long insert_sh6 (unsigned long, long, int, const char **); +static long extract_sh6 (unsigned long, int, int *); +static unsigned long insert_spr (unsigned long, long, int, const char **); +static long extract_spr (unsigned long, int, int *); +static unsigned long insert_sprg (unsigned long, long, int, const char **); +static long extract_sprg (unsigned long, int, int *); +static unsigned long insert_tbr (unsigned long, long, int, const char **); +static long extract_tbr (unsigned long, int, int *); + +/* The operands table. + + The fields are bitm, shift, insert, extract, flags. + + We used to put parens around the various additions, like the one + for BA just below. However, that caused trouble with feeble + compilers with a limit on depth of a parenthesized expression, like + (reportedly) the compiler in Microsoft Developer Studio 5. So we + omit the parens, since the macros are never used in a context where + the addition will be ambiguous. */ + +const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED 0 + { 0, 0, NULL, NULL, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA UNUSED + 1 + /* The BI field in a B form or XL form instruction. */ +#define BI BA +#define BI_MASK (0x1f << 16) + { 0x1f, 16, NULL, NULL, PPC_OPERAND_CR }, + + /* The BA field in an XL form instruction when it must be the same + as the BT field in the same instruction. */ +#define BAT BA + 1 + { 0x1f, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE }, + + /* The BB field in an XL form instruction. */ +#define BB BAT + 1 +#define BB_MASK (0x1f << 11) + { 0x1f, 11, NULL, NULL, PPC_OPERAND_CR }, + + /* The BB field in an XL form instruction when it must be the same + as the BA field in the same instruction. */ +#define BBA BB + 1 + { 0x1f, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD BBA + 1 + { 0xfffc, 0, NULL, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when absolute addressing is + used. */ +#define BDA BD + 1 + { 0xfffc, 0, NULL, NULL, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM BDA + 1 + { 0xfffc, 0, insert_bdm, extract_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used + and absolute address is used. */ +#define BDMA BDM + 1 + { 0xfffc, 0, insert_bdm, extract_bdm, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDP BDMA + 1 + { 0xfffc, 0, insert_bdp, extract_bdp, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used + and absolute addressing is used. */ +#define BDPA BDP + 1 + { 0xfffc, 0, insert_bdp, extract_bdp, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF BDPA + 1 + /* The CRFD field in an X form instruction. */ +#define CRFD BF + { 0x7, 23, NULL, NULL, PPC_OPERAND_CR }, + + /* The BF field in an X or XL form instruction. */ +#define BFF BF + 1 + { 0x7, 23, NULL, NULL, 0 }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF BFF + 1 + { 0x7, 23, NULL, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BFA field in an X or XL form instruction. */ +#define BFA OBF + 1 + { 0x7, 18, NULL, NULL, PPC_OPERAND_CR }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO BFA + 1 +#define BO_MASK (0x1f << 21) + { 0x1f, 21, insert_bo, extract_bo, 0 }, + + /* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. */ +#define BOE BO + 1 + { 0x1e, 21, insert_boe, extract_boe, 0 }, + +#define BH BOE + 1 + { 0x3, 11, NULL, NULL, PPC_OPERAND_OPTIONAL }, + + /* The BT field in an X or XL form instruction. */ +#define BT BH + 1 + { 0x1f, 21, NULL, NULL, PPC_OPERAND_CR }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR BT + 1 + { 0x7, 18, NULL, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The CRB field in an X form instruction. */ +#define CRB CR + 1 + /* The MB field in an M form instruction. */ +#define MB CRB +#define MB_MASK (0x1f << 6) + { 0x1f, 6, NULL, NULL, 0 }, + + /* The CRFS field in an X form instruction. */ +#define CRFS CRB + 1 + { 0x7, 0, NULL, NULL, PPC_OPERAND_CR }, + + /* The CT field in an X form instruction. */ +#define CT CRFS + 1 + /* The MO field in an mbar instruction. */ +#define MO CT + { 0x1f, 21, NULL, NULL, PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D CT + 1 + { 0xffff, 0, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DE field in a DE form instruction. This is like D, but is 12 + bits only. */ +#define DE D + 1 + { 0xfff, 4, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DES field in a DES form instruction. This is like DS, but is 14 + bits only (12 stored.) */ +#define DES DE + 1 + { 0x3ffc, 2, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DQ field in a DQ form instruction. This is like D, but the + lower four bits are forced to zero. */ +#define DQ DES + 1 + { 0xfff0, 0, NULL, NULL, + PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED | PPC_OPERAND_DQ }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#undef DS +#define DS DQ + 1 + { 0xfffc, 0, NULL, NULL, + PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED | PPC_OPERAND_DS }, + + /* The E field in a wrteei instruction. */ +#define E DS + 1 + { 0x1, 15, NULL, NULL, 0 }, + + /* The FL1 field in a POWER SC form instruction. */ +#define FL1 E + 1 + /* The U field in an X form instruction. */ +#define U FL1 + { 0xf, 12, NULL, NULL, 0 }, + + /* The FL2 field in a POWER SC form instruction. */ +#define FL2 FL1 + 1 + { 0x7, 2, NULL, NULL, 0 }, + + /* The FLM field in an XFL form instruction. */ +#define FLM FL2 + 1 + { 0xff, 17, NULL, NULL, 0 }, + + /* The FRA field in an X or A form instruction. */ +#define FRA FLM + 1 +#define FRA_MASK (0x1f << 16) + { 0x1f, 16, NULL, NULL, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB FRA + 1 +#define FRB_MASK (0x1f << 11) + { 0x1f, 11, NULL, NULL, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC FRB + 1 +#define FRC_MASK (0x1f << 6) + { 0x1f, 6, NULL, NULL, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS FRC + 1 +#define FRT FRS + { 0x1f, 21, NULL, NULL, PPC_OPERAND_FPR }, + + /* The FXM field in an XFX instruction. */ +#define FXM FRS + 1 + { 0xff, 12, insert_fxm, extract_fxm, 0 }, + + /* Power4 version for mfcr. */ +#define FXM4 FXM + 1 + { 0xff, 12, insert_fxm, extract_fxm, PPC_OPERAND_OPTIONAL }, + + /* The L field in a D or X form instruction. */ +#define L FXM4 + 1 + { 0x1, 21, NULL, NULL, PPC_OPERAND_OPTIONAL }, + + /* The LEV field in a POWER SVC form instruction. */ +#define SVC_LEV L + 1 + { 0x7f, 5, NULL, NULL, 0 }, + + /* The LEV field in an SC form instruction. */ +#define LEV SVC_LEV + 1 + { 0x7f, 5, NULL, NULL, PPC_OPERAND_OPTIONAL }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI LEV + 1 + { 0x3fffffc, 0, NULL, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The LI field in an I form instruction when used as an absolute + address. */ +#define LIA LI + 1 + { 0x3fffffc, 0, NULL, NULL, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The LS field in an X (sync) form instruction. */ +#define LS LIA + 1 + { 0x3, 21, NULL, NULL, PPC_OPERAND_OPTIONAL }, + + /* The ME field in an M form instruction. */ +#define ME LS + 1 +#define ME_MASK (0x1f << 1) + { 0x1f, 1, NULL, NULL, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE ME + 1 + { 0x1f, 6, NULL, NULL, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + { -1, 0, insert_mbe, extract_mbe, 0 }, + + /* The MB or ME field in an MD or MDS form instruction. The high + bit is wrapped to the low end. */ +#define MB6 MBE + 2 +#define ME6 MB6 +#define MB6_MASK (0x3f << 5) + { 0x3f, 5, insert_mb6, extract_mb6, 0 }, + + /* The NB field in an X form instruction. The value 32 is stored as + 0. */ +#define NB MB6 + 1 + { 0x1f, 11, NULL, extract_nb, PPC_OPERAND_PLUS1 }, + + /* The NSI field in a D form instruction. This is the same as the + SI field, only negated. */ +#define NSI NB + 1 + { 0xffff, 0, insert_nsi, extract_nsi, + PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED }, + + /* The RA field in an D, DS, DQ, X, XO, M, or MDS form instruction. */ +#define RA NSI + 1 +#define RA_MASK (0x1f << 16) + { 0x1f, 16, NULL, NULL, PPC_OPERAND_GPR }, + + /* As above, but 0 in the RA field means zero, not r0. */ +#define RA0 RA + 1 + { 0x1f, 16, NULL, NULL, PPC_OPERAND_GPR_0 }, + + /* The RA field in the DQ form lq instruction, which has special + value restrictions. */ +#define RAQ RA0 + 1 + { 0x1f, 16, insert_raq, NULL, PPC_OPERAND_GPR_0 }, + + /* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ +#define RAL RAQ + 1 + { 0x1f, 16, insert_ral, NULL, PPC_OPERAND_GPR_0 }, + + /* The RA field in an lmw instruction, which has special value + restrictions. */ +#define RAM RAL + 1 + { 0x1f, 16, insert_ram, NULL, PPC_OPERAND_GPR_0 }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS RAM + 1 + { 0x1f, 16, insert_ras, NULL, PPC_OPERAND_GPR_0 }, + + /* The RA field of the tlbwe instruction, which is optional. */ +#define RAOPT RAS + 1 + { 0x1f, 16, NULL, NULL, PPC_OPERAND_GPR | PPC_OPERAND_OPTIONAL }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB RAOPT + 1 +#define RB_MASK (0x1f << 11) + { 0x1f, 11, NULL, NULL, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS RB + 1 + { 0x1f, 11, insert_rbs, extract_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS RBS + 1 +#define RT RS +#define RT_MASK (0x1f << 21) + { 0x1f, 21, NULL, NULL, PPC_OPERAND_GPR }, + + /* The RS and RT fields of the DS form stq instruction, which have + special value restrictions. */ +#define RSQ RS + 1 +#define RTQ RSQ + { 0x1e, 21, NULL, NULL, PPC_OPERAND_GPR_0 }, + + /* The RS field of the tlbwe instruction, which is optional. */ +#define RSO RSQ + 1 +#define RTO RSO + { 0x1f, 21, NULL, NULL, PPC_OPERAND_GPR | PPC_OPERAND_OPTIONAL }, + + /* The SH field in an X or M form instruction. */ +#define SH RSO + 1 +#define SH_MASK (0x1f << 11) + /* The other UIMM field in a EVX form instruction. */ +#define EVUIMM SH + { 0x1f, 11, NULL, NULL, 0 }, + + /* The SH field in an MD form instruction. This is split. */ +#define SH6 SH + 1 +#define SH6_MASK ((0x1f << 11) | (1 << 1)) + { 0x3f, -1, insert_sh6, extract_sh6, 0 }, + + /* The SH field of the tlbwe instruction, which is optional. */ +#define SHO SH6 + 1 + { 0x1f, 11, NULL, NULL, PPC_OPERAND_OPTIONAL }, + + /* The SI field in a D form instruction. */ +#define SI SHO + 1 + { 0xffff, 0, NULL, NULL, PPC_OPERAND_SIGNED }, + + /* The SI field in a D form instruction when we accept a wide range + of positive values. */ +#define SISIGNOPT SI + 1 + { 0xffff, 0, NULL, NULL, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT }, + + /* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ +#define SPR SISIGNOPT + 1 +#define PMR SPR +#define SPR_MASK (0x3ff << 11) + { 0x3ff, 11, insert_spr, extract_spr, 0 }, + + /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */ +#define SPRBAT SPR + 1 +#define SPRBAT_MASK (0x3 << 17) + { 0x3, 17, NULL, NULL, 0 }, + + /* The SPRG register number in an XFX form m[ft]sprg instruction. */ +#define SPRG SPRBAT + 1 + { 0x1f, 16, insert_sprg, extract_sprg, 0 }, + + /* The SR field in an X form instruction. */ +#define SR SPRG + 1 + { 0xf, 16, NULL, NULL, 0 }, + + /* The STRM field in an X AltiVec form instruction. */ +#define STRM SR + 1 + { 0x3, 21, NULL, NULL, 0 }, + + /* The SV field in a POWER SC form instruction. */ +#define SV STRM + 1 + { 0x3fff, 2, NULL, NULL, 0 }, + + /* The TBR field in an XFX form instruction. This is like the SPR + field, but it is optional. */ +#define TBR SV + 1 + { 0x3ff, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL }, + + /* The TO field in a D or X form instruction. */ +#define TO TBR + 1 +#define TO_MASK (0x1f << 21) + { 0x1f, 21, NULL, NULL, 0 }, + + /* The UI field in a D form instruction. */ +#define UI TO + 1 + { 0xffff, 0, NULL, NULL, 0 }, + + /* The VA field in a VA, VX or VXR form instruction. */ +#define VA UI + 1 + { 0x1f, 16, NULL, NULL, PPC_OPERAND_VR }, + + /* The VB field in a VA, VX or VXR form instruction. */ +#define VB VA + 1 + { 0x1f, 11, NULL, NULL, PPC_OPERAND_VR }, + + /* The VC field in a VA form instruction. */ +#define VC VB + 1 + { 0x1f, 6, NULL, NULL, PPC_OPERAND_VR }, + + /* The VD or VS field in a VA, VX, VXR or X form instruction. */ +#define VD VC + 1 +#define VS VD + { 0x1f, 21, NULL, NULL, PPC_OPERAND_VR }, + + /* The SIMM field in a VX form instruction. */ +#define SIMM VD + 1 + { 0x1f, 16, NULL, NULL, PPC_OPERAND_SIGNED}, + + /* The UIMM field in a VX form instruction, and TE in Z form. */ +#define UIMM SIMM + 1 +#define TE UIMM + { 0x1f, 16, NULL, NULL, 0 }, + + /* The SHB field in a VA form instruction. */ +#define SHB UIMM + 1 + { 0xf, 6, NULL, NULL, 0 }, + + /* The other UIMM field in a half word EVX form instruction. */ +#define EVUIMM_2 SHB + 1 + { 0x3e, 10, NULL, NULL, PPC_OPERAND_PARENS }, + + /* The other UIMM field in a word EVX form instruction. */ +#define EVUIMM_4 EVUIMM_2 + 1 + { 0x7c, 9, NULL, NULL, PPC_OPERAND_PARENS }, + + /* The other UIMM field in a double EVX form instruction. */ +#define EVUIMM_8 EVUIMM_4 + 1 + { 0xf8, 8, NULL, NULL, PPC_OPERAND_PARENS }, + + /* The WS field. */ +#define WS EVUIMM_8 + 1 + { 0x7, 11, NULL, NULL, 0 }, + + /* The L field in an mtmsrd or A form instruction or W in an X form. */ +#define A_L WS + 1 +#define W A_L + { 0x1, 16, NULL, NULL, PPC_OPERAND_OPTIONAL }, + +#define RMC A_L + 1 + { 0x3, 9, NULL, NULL, 0 }, + +#define R RMC + 1 + { 0x1, 16, NULL, NULL, 0 }, + +#define SP R + 1 + { 0x3, 19, NULL, NULL, 0 }, + +#define S SP + 1 + { 0x1, 20, NULL, NULL, 0 }, + + /* SH field starting at bit position 16. */ +#define SH16 S + 1 + /* The DCM and DGM fields in a Z form instruction. */ +#define DCM SH16 +#define DGM DCM + { 0x3f, 10, NULL, NULL, 0 }, + + /* The EH field in larx instruction. */ +#define EH SH16 + 1 + { 0x1, 0, NULL, NULL, PPC_OPERAND_OPTIONAL }, + + /* The L field in an mtfsf or XFL form instruction. */ +#define XFL_L EH + 1 + { 0x1, 25, NULL, NULL, PPC_OPERAND_OPTIONAL}, +}; + +const unsigned int num_powerpc_operands = (sizeof (powerpc_operands) + / sizeof (powerpc_operands[0])); + +/* The functions used to insert and extract complicated operands. */ + +/* The BA field in an XL form instruction when it must be the same as + the BT field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BT field into the BA field, + and the extraction function just checks that the fields are the + same. */ + +static unsigned long +insert_bat (unsigned long insn, + long value ATTRIBUTE_UNUSED, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static long +extract_bat (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid) +{ + if (((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BB field in an XL form instruction when it must be the same as + the BA field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BA field into the BB field, + and the extraction function just checks that the fields are the + same. */ + +static unsigned long +insert_bba (unsigned long insn, + long value ATTRIBUTE_UNUSED, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (((insn >> 16) & 0x1f) << 11); +} + +static long +extract_bba (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid) +{ + if (((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + For chips built to versions of the architecture prior to version 2 + (ie. not Power4 compatible), we set the y bit of the BO field to 1 + if the offset is negative. When extracting, we require that the y + bit be 1 and that the offset be positive, since if the y bit is 0 + we just want to print the normal form of the instruction. + Power4 compatible targets use two bits, "a", and "t", instead of + the "y" bit. "at" == 00 => no hint, "at" == 01 => unpredictable, + "at" == 10 => not taken, "at" == 11 => taken. The "t" bit is 00001 + in BO field, the "a" bit is 00010 for branch on CR(BI) and 01000 + for branch on CTR. We only handle the taken/not-taken hint here. + Note that we don't relax the conditions tested here when + disassembling with -Many because insns using extract_bdm and + extract_bdp always occur in pairs. One or the other will always + be valid. */ + +static unsigned long +insert_bdm (unsigned long insn, + long value, + int dialect, + const char **errmsg ATTRIBUTE_UNUSED) +{ + if ((dialect & PPC_OPCODE_POWER4) == 0) + { + if ((value & 0x8000) != 0) + insn |= 1 << 21; + } + else + { + if ((insn & (0x14 << 21)) == (0x04 << 21)) + insn |= 0x02 << 21; + else if ((insn & (0x14 << 21)) == (0x10 << 21)) + insn |= 0x08 << 21; + } + return insn | (value & 0xfffc); +} + +static long +extract_bdm (unsigned long insn, + int dialect, + int *invalid) +{ + if ((dialect & PPC_OPCODE_POWER4) == 0) + { + if (((insn & (1 << 21)) == 0) != ((insn & (1 << 15)) == 0)) + *invalid = 1; + } + else + { + if ((insn & (0x17 << 21)) != (0x06 << 21) + && (insn & (0x1d << 21)) != (0x18 << 21)) + *invalid = 1; + } + + return ((insn & 0xfffc) ^ 0x8000) - 0x8000; +} + +/* The BD field in a B form instruction when the + modifier is used. + This is like BDM, above, except that the branch is expected to be + taken. */ + +static unsigned long +insert_bdp (unsigned long insn, + long value, + int dialect, + const char **errmsg ATTRIBUTE_UNUSED) +{ + if ((dialect & PPC_OPCODE_POWER4) == 0) + { + if ((value & 0x8000) == 0) + insn |= 1 << 21; + } + else + { + if ((insn & (0x14 << 21)) == (0x04 << 21)) + insn |= 0x03 << 21; + else if ((insn & (0x14 << 21)) == (0x10 << 21)) + insn |= 0x09 << 21; + } + return insn | (value & 0xfffc); +} + +static long +extract_bdp (unsigned long insn, + int dialect, + int *invalid) +{ + if ((dialect & PPC_OPCODE_POWER4) == 0) + { + if (((insn & (1 << 21)) == 0) == ((insn & (1 << 15)) == 0)) + *invalid = 1; + } + else + { + if ((insn & (0x17 << 21)) != (0x07 << 21) + && (insn & (0x1d << 21)) != (0x19 << 21)) + *invalid = 1; + } + + return ((insn & 0xfffc) ^ 0x8000) - 0x8000; +} + +/* Check for legal values of a BO field. */ + +static int +valid_bo (long value, int dialect, int extract) +{ + if ((dialect & PPC_OPCODE_POWER4) == 0) + { + int valid; + /* Certain encodings have bits that are required to be zero. + These are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + valid = 1; + break; + case 0x4: + valid = (value & 0x2) == 0; + break; + case 0x10: + valid = (value & 0x8) == 0; + break; + case 0x14: + valid = value == 0x14; + break; + } + /* When disassembling with -Many, accept power4 encodings too. */ + if (valid + || (dialect & PPC_OPCODE_ANY) == 0 + || !extract) + return valid; + } + + /* Certain encodings have bits that are required to be zero. + These are (z must be zero, a & t may be anything): + 0000z + 0001z + 0100z + 0101z + 001at + 011at + 1a00t + 1a01t + 1z1zz + */ + if ((value & 0x14) == 0) + return (value & 0x1) == 0; + else if ((value & 0x14) == 0x14) + return value == 0x14; + else + return 1; +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo (unsigned long insn, + long value, + int dialect, + const char **errmsg) +{ + if (!valid_bo (value, dialect, 0)) + *errmsg = _("invalid conditional option"); + return insn | ((value & 0x1f) << 21); +} + +static long +extract_bo (unsigned long insn, + int dialect, + int *invalid) +{ + long value; + + value = (insn >> 21) & 0x1f; + if (!valid_bo (value, dialect, 1)) + *invalid = 1; + return value; +} + +/* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. When + extracting it, we force it to be even. */ + +static unsigned long +insert_boe (unsigned long insn, + long value, + int dialect, + const char **errmsg) +{ + if (!valid_bo (value, dialect, 0)) + *errmsg = _("invalid conditional option"); + else if ((value & 1) != 0) + *errmsg = _("attempt to set y bit when using + or - modifier"); + + return insn | ((value & 0x1f) << 21); +} + +static long +extract_boe (unsigned long insn, + int dialect, + int *invalid) +{ + long value; + + value = (insn >> 21) & 0x1f; + if (!valid_bo (value, dialect, 1)) + *invalid = 1; + return value & 0x1e; +} + +/* FXM mask in mfcr and mtcrf instructions. */ + +static unsigned long +insert_fxm (unsigned long insn, + long value, + int dialect, + const char **errmsg) +{ + /* If we're handling the mfocrf and mtocrf insns ensure that exactly + one bit of the mask field is set. */ + if ((insn & (1 << 20)) != 0) + { + if (value == 0 || (value & -value) != value) + { + *errmsg = _("invalid mask field"); + value = 0; + } + } + + /* If the optional field on mfcr is missing that means we want to use + the old form of the instruction that moves the whole cr. In that + case we'll have VALUE zero. There doesn't seem to be a way to + distinguish this from the case where someone writes mfcr %r3,0. */ + else if (value == 0) + ; + + /* If only one bit of the FXM field is set, we can use the new form + of the instruction, which is faster. Unlike the Power4 branch hint + encoding, this is not backward compatible. Do not generate the + new form unless -mpower4 has been given, or -many and the two + operand form of mfcr was used. */ + else if ((value & -value) == value + && ((dialect & PPC_OPCODE_POWER4) != 0 + || ((dialect & PPC_OPCODE_ANY) != 0 + && (insn & (0x3ff << 1)) == 19 << 1))) + insn |= 1 << 20; + + /* Any other value on mfcr is an error. */ + else if ((insn & (0x3ff << 1)) == 19 << 1) + { + *errmsg = _("ignoring invalid mfcr mask"); + value = 0; + } + + return insn | ((value & 0xff) << 12); +} + +static long +extract_fxm (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid) +{ + long mask = (insn >> 12) & 0xff; + + /* Is this a Power4 insn? */ + if ((insn & (1 << 20)) != 0) + { + /* Exactly one bit of MASK should be set. */ + if (mask == 0 || (mask & -mask) != mask) + *invalid = 1; + } + + /* Check that non-power4 form of mfcr has a zero MASK. */ + else if ((insn & (0x3ff << 1)) == 19 << 1) + { + if (mask != 0) + *invalid = 1; + } + + return mask; +} + +/* The MB and ME fields in an M form instruction expressed as a single + operand which is itself a bitmask. The extraction function always + marks it as invalid, since we never want to recognize an + instruction which uses a field of this type. */ + +static unsigned long +insert_mbe (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg) +{ + unsigned long uval, mask; + int mb, me, mx, count, last; + + uval = value; + + if (uval == 0) + { + *errmsg = _("illegal bitmask"); + return insn; + } + + mb = 0; + me = 32; + if ((uval & 1) != 0) + last = 1; + else + last = 0; + count = 0; + + /* mb: location of last 0->1 transition */ + /* me: location of last 1->0 transition */ + /* count: # transitions */ + + for (mx = 0, mask = 1L << 31; mx < 32; ++mx, mask >>= 1) + { + if ((uval & mask) && !last) + { + ++count; + mb = mx; + last = 1; + } + else if (!(uval & mask) && last) + { + ++count; + me = mx; + last = 0; + } + } + if (me == 0) + me = 32; + + if (count != 2 && (count != 0 || ! last)) + *errmsg = _("illegal bitmask"); + + return insn | (mb << 6) | ((me - 1) << 1); +} + +static long +extract_mbe (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid) +{ + long ret; + int mb, me; + int i; + + *invalid = 1; + + mb = (insn >> 6) & 0x1f; + me = (insn >> 1) & 0x1f; + if (mb < me + 1) + { + ret = 0; + for (i = mb; i <= me; i++) + ret |= 1L << (31 - i); + } + else if (mb == me + 1) + ret = ~0; + else /* (mb > me + 1) */ + { + ret = ~0; + for (i = me + 1; i < mb; i++) + ret &= ~(1L << (31 - i)); + } + return ret; +} + +/* The MB or ME field in an MD or MDS form instruction. The high bit + is wrapped to the low end. */ + +static unsigned long +insert_mb6 (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | ((value & 0x1f) << 6) | (value & 0x20); +} + +static long +extract_mb6 (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid ATTRIBUTE_UNUSED) +{ + return ((insn >> 6) & 0x1f) | (insn & 0x20); +} + +/* The NB field in an X form instruction. The value 32 is stored as + 0. */ + +static long +extract_nb (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid ATTRIBUTE_UNUSED) +{ + long ret; + + ret = (insn >> 11) & 0x1f; + if (ret == 0) + ret = 32; + return ret; +} + +/* The NSI field in a D form instruction. This is the same as the SI + field, only negated. The extraction function always marks it as + invalid, since we never want to recognize an instruction which uses + a field of this type. */ + +static unsigned long +insert_nsi (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (-value & 0xffff); +} + +static long +extract_nsi (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid) +{ + *invalid = 1; + return -(((insn & 0xffff) ^ 0x8000) - 0x8000); +} + +/* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ + +static unsigned long +insert_ral (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg) +{ + if (value == 0 + || (unsigned long) value == ((insn >> 21) & 0x1f)) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in an lmw instruction, which has special value + restrictions. */ + +static unsigned long +insert_ram (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg) +{ + if ((unsigned long) value >= ((insn >> 21) & 0x1f)) + *errmsg = _("index register in load range"); + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in the DQ form lq instruction, which has special + value restrictions. */ + +static unsigned long +insert_raq (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg) +{ + long rtvalue = (insn & RT_MASK) >> 21; + + if (value == rtvalue) + *errmsg = _("source and target register operands must be different"); + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg) +{ + if (value == 0) + *errmsg = _("invalid register operand when updating"); + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +static unsigned long +insert_rbs (unsigned long insn, + long value ATTRIBUTE_UNUSED, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + +static long +extract_rbs (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid) +{ + if (((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The SH field in an MD form instruction. This is split. */ + +static unsigned long +insert_sh6 (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4); +} + +static long +extract_sh6 (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid ATTRIBUTE_UNUSED) +{ + return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20); +} + +/* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ + +static unsigned long +insert_spr (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_spr (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid ATTRIBUTE_UNUSED) +{ + return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); +} + +/* Some dialects have 8 SPRG registers instead of the standard 4. */ + +static unsigned long +insert_sprg (unsigned long insn, + long value, + int dialect, + const char **errmsg) +{ + /* This check uses PPC_OPCODE_403 because PPC405 is later defined + as a synonym. If ever a 405 specific dialect is added this + check should use that instead. */ + if (value > 7 + || (value > 3 + && (dialect & (PPC_OPCODE_BOOKE | PPC_OPCODE_403)) == 0)) + *errmsg = _("invalid sprg number"); + + /* If this is mfsprg4..7 then use spr 260..263 which can be read in + user mode. Anything else must use spr 272..279. */ + if (value <= 3 || (insn & 0x100) != 0) + value |= 0x10; + + return insn | ((value & 0x17) << 16); +} + +static long +extract_sprg (unsigned long insn, + int dialect, + int *invalid) +{ + unsigned long val = (insn >> 16) & 0x1f; + + /* mfsprg can use 260..263 and 272..279. mtsprg only uses spr 272..279 + If not BOOKE or 405, then both use only 272..275. */ + if (val <= 3 + || (val < 0x10 && (insn & 0x100) != 0) + || (val - 0x10 > 3 + && (dialect & (PPC_OPCODE_BOOKE | PPC_OPCODE_403)) == 0)) + *invalid = 1; + return val & 7; +} + +/* The TBR field in an XFX instruction. This is just like SPR, but it + is optional. When TBR is omitted, it must be inserted as 268 (the + magic number of the TB register). These functions treat 0 + (indicating an omitted optional operand) as 268. This means that + ``mftb 4,0'' is not handled correctly. This does not matter very + much, since the architecture manual does not define mftb as + accepting any values other than 268 or 269. */ + +#define TB (268) + +static unsigned long +insert_tbr (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + if (value == 0) + value = TB; + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_tbr (unsigned long insn, + int dialect ATTRIBUTE_UNUSED, + int *invalid ATTRIBUTE_UNUSED) +{ + long ret; + + ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); + if (ret == TB) + ret = 0; + return ret; +} + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) ((((unsigned long)(x)) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | ((((unsigned long)(to)) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | ((((unsigned long)(l)) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1) | (((unsigned long)(rc)) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* An AFRAFRC_MASK, but with L bit clear. */ +#define AFRALFRC_MASK (AFRAFRC_MASK & ~((unsigned long) 1 << 16)) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | ((((unsigned long)(aa)) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. Similarly for the 'at' bits used for power4 branch hints. */ +#define Y_MASK (((unsigned long) 1) << 21) +#define AT1_MASK (((unsigned long) 3) << 21) +#define AT2_MASK (((unsigned long) 9) << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) +#define BBOAT_MASK (BBO_MASK &~ AT1_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | ((((unsigned long)(cb)) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) +#define BBOATCB_MASK (BBOCB_MASK &~ AT1_MASK) +#define BBOAT2CB_MASK (BBOCB_MASK &~ AT2_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) +#define BBOATBI_MASK (BBOAT2CB_MASK | BI_MASK) + +/* An Context form instruction. */ +#define CTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7)) +#define CTX_MASK CTX(0x3f, 0x7) + +/* An User Context form instruction. */ +#define UCTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x1f)) +#define UCTX_MASK UCTX(0x3f, 0x1f) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* A DE form instruction. */ +#define DEO(op, xop) (OP (op) | ((xop) & 0xf)) +#define DE_MASK DEO (0x3e, 0xf) + +/* An EVSEL form instruction. */ +#define EVSEL(op, xop) (OP (op) | (((unsigned long)(xop)) & 0xff) << 3) +#define EVSEL_MASK EVSEL(0x3f, 0xff) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | ((((unsigned long)(me)) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | ((((unsigned long)(sa)) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (((unsigned long)0x3ff) << 16) | (((unsigned long)1) << 1) | 1) + +/* An VX form instruction. */ +#define VX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7ff)) + +/* The mask for an VX form instruction. */ +#define VX_MASK VX(0x3f, 0x7ff) + +/* An VA form instruction. */ +#define VXA(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x03f)) + +/* The mask for an VA form instruction. */ +#define VXA_MASK VXA(0x3f, 0x3f) + +/* An VXR form instruction. */ +#define VXR(op, xop, rc) (OP (op) | (((rc) & 1) << 10) | (((unsigned long)(xop)) & 0x3ff)) + +/* The mask for a VXR form instruction. */ +#define VXR_MASK VXR(0x3f, 0x3ff, 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1)) + +/* A Z form instruction. */ +#define Z(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* A Z form instruction with the RC bit specified. */ +#define ZRC(op, xop, rc) (Z ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* The mask for a Z form instruction. */ +#define Z_MASK ZRC (0x3f, 0x1ff, 1) +#define Z2_MASK ZRC (0x3f, 0xff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An XRA_MASK with the W field clear. */ +#define XWRA_MASK (XRA_MASK & ~((unsigned long) 1 << 16)) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An XRT_MASK mask with the L bits clear. */ +#define XLRT_MASK (XRT_MASK & ~((unsigned long) 0x3 << 21)) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An XRARB_MASK, but with the L bit clear. */ +#define XRLARB_MASK (XRARB_MASK & ~((unsigned long) 1 << 16)) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An XRTRA_MASK, but with L bit clear. */ +#define XRTLRA_MASK (XRTRA_MASK & ~((unsigned long) 1 << 21)) + +/* An X form instruction with the L bit specified. */ +#define XOPL(op, xop, l) (X ((op), (xop)) | ((((unsigned long)(l)) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (((unsigned long)1) << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (((unsigned long)1) << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | ((((unsigned long)(to)) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An X form tlb instruction with the SH field specified. */ +#define XTLB(op, xop, sh) (X ((op), (xop)) | ((((unsigned long)(sh)) & 0x1f) << 11)) +#define XTLB_MASK (X_MASK | SH_MASK) + +/* An X form sync instruction. */ +#define XSYNC(op, xop, l) (X ((op), (xop)) | ((((unsigned long)(l)) & 3) << 21)) + +/* An X form sync instruction with everything filled in except the LS field. */ +#define XSYNC_MASK (0xff9fffff) + +/* An X_MASK, but with the EH bit clear. */ +#define XEH_MASK (X_MASK & ~((unsigned long )1)) + +/* An X form AltiVec dss instruction. */ +#define XDSS(op, xop, a) (X ((op), (xop)) | ((((unsigned long)(a)) & 1) << 25)) +#define XDSS_MASK XDSS(0x3f, 0x3ff, 1) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1) | (((unsigned long)(rc)) & 1)) +#define XFL_MASK XFL (0x3f, 0x3ff, 1) + +/* An X form isel instruction. */ +#define XISEL(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1)) +#define XISEL_MASK XISEL(0x3f, 0x1f) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | ((((unsigned long)(y)) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | ((((unsigned long)(cb)) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* A mask for branch instructions using the BH field. */ +#define XLBH_MASK (XL_MASK | (0x1c << 11)) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 1) | ((((unsigned long)(oe)) & 1) << 10) | (((unsigned long)(rc)) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 2) | (((unsigned long)(rc)) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (1 << 11) | (1 << 20)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm, p4) \ + (X ((op), (xop)) | ((((unsigned long)(fxm)) & 0xff) << 12) \ + | ((unsigned long)(p4) << 20)) + +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | ((((unsigned long)(spr)) & 0x1f) << 16) | ((((unsigned long)(spr)) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK & ~(0x1f << 16)) + +/* An X form instruction with everything filled in except the E field. */ +#define XE_MASK (0xffff7fff) + +/* An X form user context instruction. */ +#define XUC(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x1f)) +#define XUC_MASK XUC(0x3f, 0x1f) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) + +#define BOF (0x4) +#define BOFP (0x5) +#define BOFM4 (0x6) +#define BOFP4 (0x7) +#define BOT (0xc) +#define BOTP (0xd) +#define BOTM4 (0xe) +#define BOTP4 (0xf) + +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BODNZM4 (0x18) +#define BODNZP4 (0x19) +#define BODZM4 (0x1a) +#define BODZP4 (0x1b) + +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC +#define PPCCOM PPC_OPCODE_PPC | PPC_OPCODE_COMMON +#define NOPOWER4 PPC_OPCODE_NOPOWER4 | PPCCOM +#define POWER4 PPC_OPCODE_POWER4 +#define POWER5 PPC_OPCODE_POWER5 +#define POWER6 PPC_OPCODE_POWER6 +#define CELL PPC_OPCODE_CELL +#define PPC32 PPC_OPCODE_32 | PPC_OPCODE_PPC +#define PPC64 PPC_OPCODE_64 | PPC_OPCODE_PPC +#define PPC403 PPC_OPCODE_403 +#define PPC405 PPC403 +#define PPC440 PPC_OPCODE_440 +#define PPC750 PPC +#define PPC860 PPC +#define PPCVEC PPC_OPCODE_ALTIVEC +#define POWER PPC_OPCODE_POWER +#define POWER2 PPC_OPCODE_POWER | PPC_OPCODE_POWER2 +#define PPCPWR2 PPC_OPCODE_PPC | PPC_OPCODE_POWER | PPC_OPCODE_POWER2 +#define POWER32 PPC_OPCODE_POWER | PPC_OPCODE_32 +#define COM PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON +#define COM32 PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_32 +#define M601 PPC_OPCODE_POWER | PPC_OPCODE_601 +#define PWRCOM PPC_OPCODE_POWER | PPC_OPCODE_601 | PPC_OPCODE_COMMON +#define MFDEC1 PPC_OPCODE_POWER +#define MFDEC2 PPC_OPCODE_PPC | PPC_OPCODE_601 | PPC_OPCODE_BOOKE +#define BOOKE PPC_OPCODE_BOOKE +#define BOOKE64 PPC_OPCODE_BOOKE64 +#define CLASSIC PPC_OPCODE_CLASSIC +#define PPCE300 PPC_OPCODE_E300 +#define PPCSPE PPC_OPCODE_SPE +#define PPCISEL PPC_OPCODE_ISEL +#define PPCEFS PPC_OPCODE_EFS +#define PPCBRLK PPC_OPCODE_BRLOCK +#define PPCPMR PPC_OPCODE_PMR +#define PPCCHLK PPC_OPCODE_CACHELCK +#define PPCCHLK64 PPC_OPCODE_CACHELCK | PPC_OPCODE_BOOKE64 +#define PPCRFMCI PPC_OPCODE_RFMCI + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +const struct powerpc_opcode powerpc_opcodes[] = { +{ "attn", X(0,256), X_MASK, POWER4, { 0 } }, +{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdi", OP(2), OP_MASK, PPC64, { TO, RA, SI } }, + +{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tllti", OPTO(3,TOLLT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "teqi", OPTO(3,TOEQ), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tllei", OPTO(3,TOLLE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tgti", OPTO(3,TOGT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tgei", OPTO(3,TOGE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twnli", OPTO(3,TONL), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tnli", OPTO(3,TONL), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlti", OPTO(3,TOLT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlei", OPTO(3,TOLE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twngi", OPTO(3,TONG), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tngi", OPTO(3,TONG), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twnei", OPTO(3,TONE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tnei", OPTO(3,TONE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twi", OP(3), OP_MASK, PPCCOM, { TO, RA, SI } }, +{ "ti", OP(3), OP_MASK, PWRCOM, { TO, RA, SI } }, + +{ "macchw", XO(4,172,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchw.", XO(4,172,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwo", XO(4,172,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwo.", XO(4,172,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchws", XO(4,236,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchws.", XO(4,236,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwso", XO(4,236,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwso.", XO(4,236,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwsu", XO(4,204,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwsu.", XO(4,204,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwsuo", XO(4,204,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwsuo.", XO(4,204,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwu", XO(4,140,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwu.", XO(4,140,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwuo", XO(4,140,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "macchwuo.", XO(4,140,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhw", XO(4,44,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhw.", XO(4,44,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwo", XO(4,44,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwo.", XO(4,44,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhws", XO(4,108,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhws.", XO(4,108,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwso", XO(4,108,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwso.", XO(4,108,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwsu", XO(4,76,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwsu.", XO(4,76,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwsuo", XO(4,76,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwsuo.", XO(4,76,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwu", XO(4,12,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwu.", XO(4,12,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwuo", XO(4,12,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "machhwuo.", XO(4,12,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhw", XO(4,428,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhw.", XO(4,428,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwo", XO(4,428,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwo.", XO(4,428,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhws", XO(4,492,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhws.", XO(4,492,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwso", XO(4,492,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwso.", XO(4,492,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwsu", XO(4,460,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwsu.", XO(4,460,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwsuo", XO(4,460,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwsuo.", XO(4,460,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwu", XO(4,396,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwu.", XO(4,396,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwuo", XO(4,396,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "maclhwuo.", XO(4,396,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mulchw", XRC(4,168,0), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mulchw.", XRC(4,168,1), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mulchwu", XRC(4,136,0), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mulchwu.", XRC(4,136,1), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mulhhw", XRC(4,40,0), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mulhhw.", XRC(4,40,1), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mulhhwu", XRC(4,8,0), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mulhhwu.", XRC(4,8,1), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mullhw", XRC(4,424,0), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mullhw.", XRC(4,424,1), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mullhwu", XRC(4,392,0), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mullhwu.", XRC(4,392,1), X_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmacchw", XO(4,174,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmacchw.", XO(4,174,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmacchwo", XO(4,174,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmacchwo.", XO(4,174,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmacchws", XO(4,238,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmacchws.", XO(4,238,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmacchwso", XO(4,238,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmacchwso.", XO(4,238,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmachhw", XO(4,46,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmachhw.", XO(4,46,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmachhwo", XO(4,46,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmachhwo.", XO(4,46,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmachhws", XO(4,110,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmachhws.", XO(4,110,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmachhwso", XO(4,110,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmachhwso.", XO(4,110,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmaclhw", XO(4,430,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmaclhw.", XO(4,430,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmaclhwo", XO(4,430,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmaclhwo.", XO(4,430,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmaclhws", XO(4,494,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmaclhws.", XO(4,494,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmaclhwso", XO(4,494,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "nmaclhwso.", XO(4,494,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } }, +{ "mfvscr", VX(4, 1540), VX_MASK, PPCVEC, { VD } }, +{ "mtvscr", VX(4, 1604), VX_MASK, PPCVEC, { VB } }, + + /* Double-precision opcodes. */ + /* Some of these conflict with AltiVec, so move them before, since + PPCVEC includes the PPC_OPCODE_PPC set. */ +{ "efscfd", VX(4, 719), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdabs", VX(4, 740), VX_MASK, PPCEFS, { RS, RA } }, +{ "efdnabs", VX(4, 741), VX_MASK, PPCEFS, { RS, RA } }, +{ "efdneg", VX(4, 742), VX_MASK, PPCEFS, { RS, RA } }, +{ "efdadd", VX(4, 736), VX_MASK, PPCEFS, { RS, RA, RB } }, +{ "efdsub", VX(4, 737), VX_MASK, PPCEFS, { RS, RA, RB } }, +{ "efdmul", VX(4, 744), VX_MASK, PPCEFS, { RS, RA, RB } }, +{ "efddiv", VX(4, 745), VX_MASK, PPCEFS, { RS, RA, RB } }, +{ "efdcmpgt", VX(4, 748), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efdcmplt", VX(4, 749), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efdcmpeq", VX(4, 750), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efdtstgt", VX(4, 764), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efdtstlt", VX(4, 765), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efdtsteq", VX(4, 766), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efdcfsi", VX(4, 753), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdcfsid", VX(4, 739), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdcfui", VX(4, 752), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdcfuid", VX(4, 738), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdcfsf", VX(4, 755), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdcfuf", VX(4, 754), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdctsi", VX(4, 757), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdctsidz",VX(4, 747), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdctsiz", VX(4, 762), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdctui", VX(4, 756), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdctuidz",VX(4, 746), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdctuiz", VX(4, 760), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdctsf", VX(4, 759), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdctuf", VX(4, 758), VX_MASK, PPCEFS, { RS, RB } }, +{ "efdcfs", VX(4, 751), VX_MASK, PPCEFS, { RS, RB } }, + /* End of double-precision opcodes. */ + +{ "vaddcuw", VX(4, 384), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddfp", VX(4, 10), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddsbs", VX(4, 768), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddshs", VX(4, 832), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddsws", VX(4, 896), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddubm", VX(4, 0), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddubs", VX(4, 512), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduhm", VX(4, 64), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduhs", VX(4, 576), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduwm", VX(4, 128), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduws", VX(4, 640), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vand", VX(4, 1028), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vandc", VX(4, 1092), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsb", VX(4, 1282), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsh", VX(4, 1346), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsw", VX(4, 1410), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgub", VX(4, 1026), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavguh", VX(4, 1090), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavguw", VX(4, 1154), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcfsx", VX(4, 842), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vcfux", VX(4, 778), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vcmpbfp", VXR(4, 966, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpbfp.", VXR(4, 966, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpeqfp", VXR(4, 198, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpeqfp.", VXR(4, 198, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequb", VXR(4, 6, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequb.", VXR(4, 6, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequh", VXR(4, 70, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequh.", VXR(4, 70, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequw", VXR(4, 134, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequw.", VXR(4, 134, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgefp", VXR(4, 454, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgefp.", VXR(4, 454, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtfp", VXR(4, 710, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtfp.", VXR(4, 710, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsb", VXR(4, 774, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsb.", VXR(4, 774, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsh", VXR(4, 838, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsh.", VXR(4, 838, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsw", VXR(4, 902, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsw.", VXR(4, 902, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtub", VXR(4, 518, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtub.", VXR(4, 518, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuh", VXR(4, 582, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuh.", VXR(4, 582, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuw", VXR(4, 646, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuw.", VXR(4, 646, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vctsxs", VX(4, 970), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vctuxs", VX(4, 906), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vexptefp", VX(4, 394), VX_MASK, PPCVEC, { VD, VB } }, +{ "vlogefp", VX(4, 458), VX_MASK, PPCVEC, { VD, VB } }, +{ "vmaddfp", VXA(4, 46), VXA_MASK, PPCVEC, { VD, VA, VC, VB } }, +{ "vmaxfp", VX(4, 1034), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsb", VX(4, 258), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsh", VX(4, 322), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsw", VX(4, 386), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxub", VX(4, 2), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxuh", VX(4, 66), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxuw", VX(4, 130), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmhaddshs", VXA(4, 32), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmhraddshs", VXA(4, 33), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vminfp", VX(4, 1098), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsb", VX(4, 770), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsh", VX(4, 834), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsw", VX(4, 898), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminub", VX(4, 514), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminuh", VX(4, 578), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminuw", VX(4, 642), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmladduhm", VXA(4, 34), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmrghb", VX(4, 12), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrghh", VX(4, 76), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrghw", VX(4, 140), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglb", VX(4, 268), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglh", VX(4, 332), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglw", VX(4, 396), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmsummbm", VXA(4, 37), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumshm", VXA(4, 40), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumshs", VXA(4, 41), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumubm", VXA(4, 36), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumuhm", VXA(4, 38), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumuhs", VXA(4, 39), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmulesb", VX(4, 776), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulesh", VX(4, 840), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuleub", VX(4, 520), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuleuh", VX(4, 584), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulosb", VX(4, 264), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulosh", VX(4, 328), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuloub", VX(4, 8), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulouh", VX(4, 72), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vnmsubfp", VXA(4, 47), VXA_MASK, PPCVEC, { VD, VA, VC, VB } }, +{ "vnor", VX(4, 1284), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vor", VX(4, 1156), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vperm", VXA(4, 43), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vpkpx", VX(4, 782), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkshss", VX(4, 398), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkshus", VX(4, 270), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkswss", VX(4, 462), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkswus", VX(4, 334), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuhum", VX(4, 14), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuhus", VX(4, 142), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuwum", VX(4, 78), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuwus", VX(4, 206), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrefp", VX(4, 266), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfim", VX(4, 714), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfin", VX(4, 522), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfip", VX(4, 650), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfiz", VX(4, 586), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrlb", VX(4, 4), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrlh", VX(4, 68), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrlw", VX(4, 132), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrsqrtefp", VX(4, 330), VX_MASK, PPCVEC, { VD, VB } }, +{ "vsel", VXA(4, 42), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vsl", VX(4, 452), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslb", VX(4, 260), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsldoi", VXA(4, 44), VXA_MASK, PPCVEC, { VD, VA, VB, SHB } }, +{ "vslh", VX(4, 324), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslo", VX(4, 1036), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslw", VX(4, 388), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vspltb", VX(4, 524), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vsplth", VX(4, 588), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vspltisb", VX(4, 780), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltish", VX(4, 844), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltisw", VX(4, 908), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltw", VX(4, 652), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vsr", VX(4, 708), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrab", VX(4, 772), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrah", VX(4, 836), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsraw", VX(4, 900), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrb", VX(4, 516), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrh", VX(4, 580), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsro", VX(4, 1100), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrw", VX(4, 644), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubcuw", VX(4, 1408), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubfp", VX(4, 74), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubsbs", VX(4, 1792), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubshs", VX(4, 1856), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubsws", VX(4, 1920), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsububm", VX(4, 1024), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsububs", VX(4, 1536), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuhm", VX(4, 1088), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuhs", VX(4, 1600), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuwm", VX(4, 1152), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuws", VX(4, 1664), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsumsws", VX(4, 1928), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum2sws", VX(4, 1672), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4sbs", VX(4, 1800), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4shs", VX(4, 1608), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4ubs", VX(4, 1544), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vupkhpx", VX(4, 846), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupkhsb", VX(4, 526), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupkhsh", VX(4, 590), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklpx", VX(4, 974), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklsb", VX(4, 654), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklsh", VX(4, 718), VX_MASK, PPCVEC, { VD, VB } }, +{ "vxor", VX(4, 1220), VX_MASK, PPCVEC, { VD, VA, VB } }, + +{ "evaddw", VX(4, 512), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evaddiw", VX(4, 514), VX_MASK, PPCSPE, { RS, RB, UIMM } }, +{ "evsubfw", VX(4, 516), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evsubw", VX(4, 516), VX_MASK, PPCSPE, { RS, RB, RA } }, +{ "evsubifw", VX(4, 518), VX_MASK, PPCSPE, { RS, UIMM, RB } }, +{ "evsubiw", VX(4, 518), VX_MASK, PPCSPE, { RS, RB, UIMM } }, +{ "evabs", VX(4, 520), VX_MASK, PPCSPE, { RS, RA } }, +{ "evneg", VX(4, 521), VX_MASK, PPCSPE, { RS, RA } }, +{ "evextsb", VX(4, 522), VX_MASK, PPCSPE, { RS, RA } }, +{ "evextsh", VX(4, 523), VX_MASK, PPCSPE, { RS, RA } }, +{ "evrndw", VX(4, 524), VX_MASK, PPCSPE, { RS, RA } }, +{ "evcntlzw", VX(4, 525), VX_MASK, PPCSPE, { RS, RA } }, +{ "evcntlsw", VX(4, 526), VX_MASK, PPCSPE, { RS, RA } }, + +{ "brinc", VX(4, 527), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evand", VX(4, 529), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evandc", VX(4, 530), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmr", VX(4, 535), VX_MASK, PPCSPE, { RS, RA, BBA } }, +{ "evor", VX(4, 535), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evorc", VX(4, 539), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evxor", VX(4, 534), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "eveqv", VX(4, 537), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evnand", VX(4, 542), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evnot", VX(4, 536), VX_MASK, PPCSPE, { RS, RA, BBA } }, +{ "evnor", VX(4, 536), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evrlw", VX(4, 552), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evrlwi", VX(4, 554), VX_MASK, PPCSPE, { RS, RA, EVUIMM } }, +{ "evslw", VX(4, 548), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evslwi", VX(4, 550), VX_MASK, PPCSPE, { RS, RA, EVUIMM } }, +{ "evsrws", VX(4, 545), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evsrwu", VX(4, 544), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evsrwis", VX(4, 547), VX_MASK, PPCSPE, { RS, RA, EVUIMM } }, +{ "evsrwiu", VX(4, 546), VX_MASK, PPCSPE, { RS, RA, EVUIMM } }, +{ "evsplati", VX(4, 553), VX_MASK, PPCSPE, { RS, SIMM } }, +{ "evsplatfi", VX(4, 555), VX_MASK, PPCSPE, { RS, SIMM } }, +{ "evmergehi", VX(4, 556), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmergelo", VX(4, 557), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmergehilo",VX(4,558), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmergelohi",VX(4,559), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evcmpgts", VX(4, 561), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evcmpgtu", VX(4, 560), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evcmplts", VX(4, 563), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evcmpltu", VX(4, 562), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evcmpeq", VX(4, 564), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evsel", EVSEL(4,79),EVSEL_MASK, PPCSPE, { RS, RA, RB, CRFS } }, + +{ "evldd", VX(4, 769), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } }, +{ "evlddx", VX(4, 768), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evldw", VX(4, 771), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } }, +{ "evldwx", VX(4, 770), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evldh", VX(4, 773), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } }, +{ "evldhx", VX(4, 772), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evlwhe", VX(4, 785), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evlwhex", VX(4, 784), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evlwhou", VX(4, 789), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evlwhoux", VX(4, 788), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evlwhos", VX(4, 791), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evlwhosx", VX(4, 790), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evlwwsplat",VX(4, 793), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evlwwsplatx",VX(4, 792), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evlwhsplat",VX(4, 797), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evlwhsplatx",VX(4, 796), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evlhhesplat",VX(4, 777), VX_MASK, PPCSPE, { RS, EVUIMM_2, RA } }, +{ "evlhhesplatx",VX(4, 776), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evlhhousplat",VX(4, 781), VX_MASK, PPCSPE, { RS, EVUIMM_2, RA } }, +{ "evlhhousplatx",VX(4, 780), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evlhhossplat",VX(4, 783), VX_MASK, PPCSPE, { RS, EVUIMM_2, RA } }, +{ "evlhhossplatx",VX(4, 782), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evstdd", VX(4, 801), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } }, +{ "evstddx", VX(4, 800), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evstdw", VX(4, 803), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } }, +{ "evstdwx", VX(4, 802), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evstdh", VX(4, 805), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } }, +{ "evstdhx", VX(4, 804), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evstwwe", VX(4, 825), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evstwwex", VX(4, 824), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evstwwo", VX(4, 829), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evstwwox", VX(4, 828), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evstwhe", VX(4, 817), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evstwhex", VX(4, 816), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evstwho", VX(4, 821), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } }, +{ "evstwhox", VX(4, 820), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evfsabs", VX(4, 644), VX_MASK, PPCSPE, { RS, RA } }, +{ "evfsnabs", VX(4, 645), VX_MASK, PPCSPE, { RS, RA } }, +{ "evfsneg", VX(4, 646), VX_MASK, PPCSPE, { RS, RA } }, +{ "evfsadd", VX(4, 640), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evfssub", VX(4, 641), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evfsmul", VX(4, 648), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evfsdiv", VX(4, 649), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evfscmpgt", VX(4, 652), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evfscmplt", VX(4, 653), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evfscmpeq", VX(4, 654), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evfststgt", VX(4, 668), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evfststlt", VX(4, 669), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evfststeq", VX(4, 670), VX_MASK, PPCSPE, { CRFD, RA, RB } }, +{ "evfscfui", VX(4, 656), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfsctuiz", VX(4, 664), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfscfsi", VX(4, 657), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfscfuf", VX(4, 658), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfscfsf", VX(4, 659), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfsctui", VX(4, 660), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfsctsi", VX(4, 661), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfsctsiz", VX(4, 666), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfsctuf", VX(4, 662), VX_MASK, PPCSPE, { RS, RB } }, +{ "evfsctsf", VX(4, 663), VX_MASK, PPCSPE, { RS, RB } }, + +{ "efsabs", VX(4, 708), VX_MASK, PPCEFS, { RS, RA } }, +{ "efsnabs", VX(4, 709), VX_MASK, PPCEFS, { RS, RA } }, +{ "efsneg", VX(4, 710), VX_MASK, PPCEFS, { RS, RA } }, +{ "efsadd", VX(4, 704), VX_MASK, PPCEFS, { RS, RA, RB } }, +{ "efssub", VX(4, 705), VX_MASK, PPCEFS, { RS, RA, RB } }, +{ "efsmul", VX(4, 712), VX_MASK, PPCEFS, { RS, RA, RB } }, +{ "efsdiv", VX(4, 713), VX_MASK, PPCEFS, { RS, RA, RB } }, +{ "efscmpgt", VX(4, 716), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efscmplt", VX(4, 717), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efscmpeq", VX(4, 718), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efststgt", VX(4, 732), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efststlt", VX(4, 733), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efststeq", VX(4, 734), VX_MASK, PPCEFS, { CRFD, RA, RB } }, +{ "efscfui", VX(4, 720), VX_MASK, PPCEFS, { RS, RB } }, +{ "efsctuiz", VX(4, 728), VX_MASK, PPCEFS, { RS, RB } }, +{ "efscfsi", VX(4, 721), VX_MASK, PPCEFS, { RS, RB } }, +{ "efscfuf", VX(4, 722), VX_MASK, PPCEFS, { RS, RB } }, +{ "efscfsf", VX(4, 723), VX_MASK, PPCEFS, { RS, RB } }, +{ "efsctui", VX(4, 724), VX_MASK, PPCEFS, { RS, RB } }, +{ "efsctsi", VX(4, 725), VX_MASK, PPCEFS, { RS, RB } }, +{ "efsctsiz", VX(4, 730), VX_MASK, PPCEFS, { RS, RB } }, +{ "efsctuf", VX(4, 726), VX_MASK, PPCEFS, { RS, RB } }, +{ "efsctsf", VX(4, 727), VX_MASK, PPCEFS, { RS, RB } }, + +{ "evmhossf", VX(4, 1031), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhossfa", VX(4, 1063), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhosmf", VX(4, 1039), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhosmfa", VX(4, 1071), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhosmi", VX(4, 1037), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhosmia", VX(4, 1069), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhoumi", VX(4, 1036), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhoumia", VX(4, 1068), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhessf", VX(4, 1027), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhessfa", VX(4, 1059), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhesmf", VX(4, 1035), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhesmfa", VX(4, 1067), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhesmi", VX(4, 1033), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhesmia", VX(4, 1065), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmheumi", VX(4, 1032), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmheumia", VX(4, 1064), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmhossfaaw",VX(4, 1287), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhossiaaw",VX(4, 1285), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhosmfaaw",VX(4, 1295), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhosmiaaw",VX(4, 1293), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhousiaaw",VX(4, 1284), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhoumiaaw",VX(4, 1292), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhessfaaw",VX(4, 1283), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhessiaaw",VX(4, 1281), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhesmfaaw",VX(4, 1291), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhesmiaaw",VX(4, 1289), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmheusiaaw",VX(4, 1280), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmheumiaaw",VX(4, 1288), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmhossfanw",VX(4, 1415), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhossianw",VX(4, 1413), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhosmfanw",VX(4, 1423), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhosmianw",VX(4, 1421), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhousianw",VX(4, 1412), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhoumianw",VX(4, 1420), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhessfanw",VX(4, 1411), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhessianw",VX(4, 1409), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhesmfanw",VX(4, 1419), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhesmianw",VX(4, 1417), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmheusianw",VX(4, 1408), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmheumianw",VX(4, 1416), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmhogsmfaa",VX(4, 1327), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhogsmiaa",VX(4, 1325), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhogumiaa",VX(4, 1324), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhegsmfaa",VX(4, 1323), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhegsmiaa",VX(4, 1321), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhegumiaa",VX(4, 1320), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmhogsmfan",VX(4, 1455), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhogsmian",VX(4, 1453), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhogumian",VX(4, 1452), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhegsmfan",VX(4, 1451), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhegsmian",VX(4, 1449), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmhegumian",VX(4, 1448), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmwhssf", VX(4, 1095), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwhssfa", VX(4, 1127), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwhsmf", VX(4, 1103), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwhsmfa", VX(4, 1135), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwhsmi", VX(4, 1101), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwhsmia", VX(4, 1133), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwhumi", VX(4, 1100), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwhumia", VX(4, 1132), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmwlumi", VX(4, 1096), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwlumia", VX(4, 1128), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmwlssiaaw",VX(4, 1345), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwlsmiaaw",VX(4, 1353), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwlusiaaw",VX(4, 1344), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwlumiaaw",VX(4, 1352), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmwlssianw",VX(4, 1473), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwlsmianw",VX(4, 1481), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwlusianw",VX(4, 1472), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwlumianw",VX(4, 1480), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmwssf", VX(4, 1107), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwssfa", VX(4, 1139), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwsmf", VX(4, 1115), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwsmfa", VX(4, 1147), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwsmi", VX(4, 1113), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwsmia", VX(4, 1145), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwumi", VX(4, 1112), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwumia", VX(4, 1144), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmwssfaa", VX(4, 1363), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwsmfaa", VX(4, 1371), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwsmiaa", VX(4, 1369), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwumiaa", VX(4, 1368), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evmwssfan", VX(4, 1491), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwsmfan", VX(4, 1499), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwsmian", VX(4, 1497), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evmwumian", VX(4, 1496), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "evaddssiaaw",VX(4, 1217), VX_MASK, PPCSPE, { RS, RA } }, +{ "evaddsmiaaw",VX(4, 1225), VX_MASK, PPCSPE, { RS, RA } }, +{ "evaddusiaaw",VX(4, 1216), VX_MASK, PPCSPE, { RS, RA } }, +{ "evaddumiaaw",VX(4, 1224), VX_MASK, PPCSPE, { RS, RA } }, + +{ "evsubfssiaaw",VX(4, 1219), VX_MASK, PPCSPE, { RS, RA } }, +{ "evsubfsmiaaw",VX(4, 1227), VX_MASK, PPCSPE, { RS, RA } }, +{ "evsubfusiaaw",VX(4, 1218), VX_MASK, PPCSPE, { RS, RA } }, +{ "evsubfumiaaw",VX(4, 1226), VX_MASK, PPCSPE, { RS, RA } }, + +{ "evmra", VX(4, 1220), VX_MASK, PPCSPE, { RS, RA } }, + +{ "evdivws", VX(4, 1222), VX_MASK, PPCSPE, { RS, RA, RB } }, +{ "evdivwu", VX(4, 1223), VX_MASK, PPCSPE, { RS, RA, RB } }, + +{ "mulli", OP(7), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "muli", OP(7), OP_MASK, PWRCOM, { RT, RA, SI } }, + +{ "subfic", OP(8), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "sfi", OP(8), OP_MASK, PWRCOM, { RT, RA, SI } }, + +{ "dozi", OP(9), OP_MASK, M601, { RT, RA, SI } }, + +{ "bce", B(9,0,0), B_MASK, BOOKE64, { BO, BI, BD } }, +{ "bcel", B(9,0,1), B_MASK, BOOKE64, { BO, BI, BD } }, +{ "bcea", B(9,1,0), B_MASK, BOOKE64, { BO, BI, BDA } }, +{ "bcela", B(9,1,1), B_MASK, BOOKE64, { BO, BI, BDA } }, + +{ "cmplwi", OPL(10,0), OPL_MASK, PPCCOM, { OBF, RA, UI } }, +{ "cmpldi", OPL(10,1), OPL_MASK, PPC64, { OBF, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PWRCOM, { BF, RA, UI } }, + +{ "cmpwi", OPL(11,0), OPL_MASK, PPCCOM, { OBF, RA, SI } }, +{ "cmpdi", OPL(11,1), OPL_MASK, PPC64, { OBF, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PWRCOM, { BF, RA, SI } }, + +{ "addic", OP(12), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "ai", OP(12), OP_MASK, PWRCOM, { RT, RA, SI } }, +{ "subic", OP(12), OP_MASK, PPCCOM, { RT, RA, NSI } }, + +{ "addic.", OP(13), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "ai.", OP(13), OP_MASK, PWRCOM, { RT, RA, SI } }, +{ "subic.", OP(13), OP_MASK, PPCCOM, { RT, RA, NSI } }, + +{ "li", OP(14), DRA_MASK, PPCCOM, { RT, SI } }, +{ "lil", OP(14), DRA_MASK, PWRCOM, { RT, SI } }, +{ "addi", OP(14), OP_MASK, PPCCOM, { RT, RA0, SI } }, +{ "cal", OP(14), OP_MASK, PWRCOM, { RT, D, RA0 } }, +{ "subi", OP(14), OP_MASK, PPCCOM, { RT, RA0, NSI } }, +{ "la", OP(14), OP_MASK, PPCCOM, { RT, D, RA0 } }, + +{ "lis", OP(15), DRA_MASK, PPCCOM, { RT, SISIGNOPT } }, +{ "liu", OP(15), DRA_MASK, PWRCOM, { RT, SISIGNOPT } }, +{ "addis", OP(15), OP_MASK, PPCCOM, { RT,RA0,SISIGNOPT } }, +{ "cau", OP(15), OP_MASK, PWRCOM, { RT,RA0,SISIGNOPT } }, +{ "subis", OP(15), OP_MASK, PPCCOM, { RT, RA0, NSI } }, + +{ "bdnz-", BBO(16,BODNZ,0,0), BBOATBI_MASK, PPCCOM, { BDM } }, +{ "bdnz+", BBO(16,BODNZ,0,0), BBOATBI_MASK, PPCCOM, { BDP } }, +{ "bdnz", BBO(16,BODNZ,0,0), BBOATBI_MASK, PPCCOM, { BD } }, +{ "bdn", BBO(16,BODNZ,0,0), BBOATBI_MASK, PWRCOM, { BD } }, +{ "bdnzl-", BBO(16,BODNZ,0,1), BBOATBI_MASK, PPCCOM, { BDM } }, +{ "bdnzl+", BBO(16,BODNZ,0,1), BBOATBI_MASK, PPCCOM, { BDP } }, +{ "bdnzl", BBO(16,BODNZ,0,1), BBOATBI_MASK, PPCCOM, { BD } }, +{ "bdnl", BBO(16,BODNZ,0,1), BBOATBI_MASK, PWRCOM, { BD } }, +{ "bdnza-", BBO(16,BODNZ,1,0), BBOATBI_MASK, PPCCOM, { BDMA } }, +{ "bdnza+", BBO(16,BODNZ,1,0), BBOATBI_MASK, PPCCOM, { BDPA } }, +{ "bdnza", BBO(16,BODNZ,1,0), BBOATBI_MASK, PPCCOM, { BDA } }, +{ "bdna", BBO(16,BODNZ,1,0), BBOATBI_MASK, PWRCOM, { BDA } }, +{ "bdnzla-", BBO(16,BODNZ,1,1), BBOATBI_MASK, PPCCOM, { BDMA } }, +{ "bdnzla+", BBO(16,BODNZ,1,1), BBOATBI_MASK, PPCCOM, { BDPA } }, +{ "bdnzla", BBO(16,BODNZ,1,1), BBOATBI_MASK, PPCCOM, { BDA } }, +{ "bdnla", BBO(16,BODNZ,1,1), BBOATBI_MASK, PWRCOM, { BDA } }, +{ "bdz-", BBO(16,BODZ,0,0), BBOATBI_MASK, PPCCOM, { BDM } }, +{ "bdz+", BBO(16,BODZ,0,0), BBOATBI_MASK, PPCCOM, { BDP } }, +{ "bdz", BBO(16,BODZ,0,0), BBOATBI_MASK, COM, { BD } }, +{ "bdzl-", BBO(16,BODZ,0,1), BBOATBI_MASK, PPCCOM, { BDM } }, +{ "bdzl+", BBO(16,BODZ,0,1), BBOATBI_MASK, PPCCOM, { BDP } }, +{ "bdzl", BBO(16,BODZ,0,1), BBOATBI_MASK, COM, { BD } }, +{ "bdza-", BBO(16,BODZ,1,0), BBOATBI_MASK, PPCCOM, { BDMA } }, +{ "bdza+", BBO(16,BODZ,1,0), BBOATBI_MASK, PPCCOM, { BDPA } }, +{ "bdza", BBO(16,BODZ,1,0), BBOATBI_MASK, COM, { BDA } }, +{ "bdzla-", BBO(16,BODZ,1,1), BBOATBI_MASK, PPCCOM, { BDMA } }, +{ "bdzla+", BBO(16,BODZ,1,1), BBOATBI_MASK, PPCCOM, { BDPA } }, +{ "bdzla", BBO(16,BODZ,1,1), BBOATBI_MASK, COM, { BDA } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BD } }, +{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BD } }, +{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDA } }, +{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDA } }, +{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, COM, { CR, BD } }, +{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, COM, { CR, BD } }, +{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, COM, { CR, BDA } }, +{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BD } }, +{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BD } }, +{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDA } }, +{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDA } }, +{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, NOPOWER4, { BI, BDM } }, +{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, NOPOWER4, { BI, BDP } }, +{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, NOPOWER4, { BI, BDM } }, +{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, NOPOWER4, { BI, BDP } }, +{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, NOPOWER4, { BI, BDMA } }, +{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, NOPOWER4, { BI, BDPA } }, +{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, NOPOWER4, { BI, BDMA } }, +{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, NOPOWER4, { BI, BDPA } }, +{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, NOPOWER4, { BI, BDM } }, +{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, NOPOWER4, { BI, BDP } }, +{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, NOPOWER4, { BI, BDM } }, +{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, NOPOWER4, { BI, BDP } }, +{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, NOPOWER4, { BI, BDMA } }, +{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, NOPOWER4, { BI, BDPA } }, +{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, NOPOWER4, { BI, BDMA } }, +{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, NOPOWER4, { BI, BDPA } }, +{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bt-", BBO(16,BOT,0,0), BBOAT_MASK, PPCCOM, { BI, BDM } }, +{ "bt+", BBO(16,BOT,0,0), BBOAT_MASK, PPCCOM, { BI, BDP } }, +{ "bt", BBO(16,BOT,0,0), BBOAT_MASK, PPCCOM, { BI, BD } }, +{ "bbt", BBO(16,BOT,0,0), BBOAT_MASK, PWRCOM, { BI, BD } }, +{ "btl-", BBO(16,BOT,0,1), BBOAT_MASK, PPCCOM, { BI, BDM } }, +{ "btl+", BBO(16,BOT,0,1), BBOAT_MASK, PPCCOM, { BI, BDP } }, +{ "btl", BBO(16,BOT,0,1), BBOAT_MASK, PPCCOM, { BI, BD } }, +{ "bbtl", BBO(16,BOT,0,1), BBOAT_MASK, PWRCOM, { BI, BD } }, +{ "bta-", BBO(16,BOT,1,0), BBOAT_MASK, PPCCOM, { BI, BDMA } }, +{ "bta+", BBO(16,BOT,1,0), BBOAT_MASK, PPCCOM, { BI, BDPA } }, +{ "bta", BBO(16,BOT,1,0), BBOAT_MASK, PPCCOM, { BI, BDA } }, +{ "bbta", BBO(16,BOT,1,0), BBOAT_MASK, PWRCOM, { BI, BDA } }, +{ "btla-", BBO(16,BOT,1,1), BBOAT_MASK, PPCCOM, { BI, BDMA } }, +{ "btla+", BBO(16,BOT,1,1), BBOAT_MASK, PPCCOM, { BI, BDPA } }, +{ "btla", BBO(16,BOT,1,1), BBOAT_MASK, PPCCOM, { BI, BDA } }, +{ "bbtla", BBO(16,BOT,1,1), BBOAT_MASK, PWRCOM, { BI, BDA } }, +{ "bf-", BBO(16,BOF,0,0), BBOAT_MASK, PPCCOM, { BI, BDM } }, +{ "bf+", BBO(16,BOF,0,0), BBOAT_MASK, PPCCOM, { BI, BDP } }, +{ "bf", BBO(16,BOF,0,0), BBOAT_MASK, PPCCOM, { BI, BD } }, +{ "bbf", BBO(16,BOF,0,0), BBOAT_MASK, PWRCOM, { BI, BD } }, +{ "bfl-", BBO(16,BOF,0,1), BBOAT_MASK, PPCCOM, { BI, BDM } }, +{ "bfl+", BBO(16,BOF,0,1), BBOAT_MASK, PPCCOM, { BI, BDP } }, +{ "bfl", BBO(16,BOF,0,1), BBOAT_MASK, PPCCOM, { BI, BD } }, +{ "bbfl", BBO(16,BOF,0,1), BBOAT_MASK, PWRCOM, { BI, BD } }, +{ "bfa-", BBO(16,BOF,1,0), BBOAT_MASK, PPCCOM, { BI, BDMA } }, +{ "bfa+", BBO(16,BOF,1,0), BBOAT_MASK, PPCCOM, { BI, BDPA } }, +{ "bfa", BBO(16,BOF,1,0), BBOAT_MASK, PPCCOM, { BI, BDA } }, +{ "bbfa", BBO(16,BOF,1,0), BBOAT_MASK, PWRCOM, { BI, BDA } }, +{ "bfla-", BBO(16,BOF,1,1), BBOAT_MASK, PPCCOM, { BI, BDMA } }, +{ "bfla+", BBO(16,BOF,1,1), BBOAT_MASK, PPCCOM, { BI, BDPA } }, +{ "bfla", BBO(16,BOF,1,1), BBOAT_MASK, PPCCOM, { BI, BDA } }, +{ "bbfla", BBO(16,BOF,1,1), BBOAT_MASK, PWRCOM, { BI, BDA } }, +{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, NOPOWER4, { BI, BDM } }, +{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, NOPOWER4, { BI, BDP } }, +{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, NOPOWER4, { BI, BDM } }, +{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, NOPOWER4, { BI, BDP } }, +{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, NOPOWER4, { BI, BDMA } }, +{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, NOPOWER4, { BI, BDPA } }, +{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, NOPOWER4, { BI, BDMA } }, +{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, NOPOWER4, { BI, BDPA } }, +{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, NOPOWER4, { BI, BDM } }, +{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, NOPOWER4, { BI, BDP } }, +{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, NOPOWER4, { BI, BDM } }, +{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, NOPOWER4, { BI, BDP } }, +{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, NOPOWER4, { BI, BDMA } }, +{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, NOPOWER4, { BI, BDPA } }, +{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, NOPOWER4, { BI, BDMA } }, +{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, NOPOWER4, { BI, BDPA } }, +{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bc-", B(16,0,0), B_MASK, PPCCOM, { BOE, BI, BDM } }, +{ "bc+", B(16,0,0), B_MASK, PPCCOM, { BOE, BI, BDP } }, +{ "bc", B(16,0,0), B_MASK, COM, { BO, BI, BD } }, +{ "bcl-", B(16,0,1), B_MASK, PPCCOM, { BOE, BI, BDM } }, +{ "bcl+", B(16,0,1), B_MASK, PPCCOM, { BOE, BI, BDP } }, +{ "bcl", B(16,0,1), B_MASK, COM, { BO, BI, BD } }, +{ "bca-", B(16,1,0), B_MASK, PPCCOM, { BOE, BI, BDMA } }, +{ "bca+", B(16,1,0), B_MASK, PPCCOM, { BOE, BI, BDPA } }, +{ "bca", B(16,1,0), B_MASK, COM, { BO, BI, BDA } }, +{ "bcla-", B(16,1,1), B_MASK, PPCCOM, { BOE, BI, BDMA } }, +{ "bcla+", B(16,1,1), B_MASK, PPCCOM, { BOE, BI, BDPA } }, +{ "bcla", B(16,1,1), B_MASK, COM, { BO, BI, BDA } }, + +{ "sc", SC(17,1,0), SC_MASK, PPC, { LEV } }, +{ "svc", SC(17,0,0), SC_MASK, POWER, { SVC_LEV, FL1, FL2 } }, +{ "svcl", SC(17,0,1), SC_MASK, POWER, { SVC_LEV, FL1, FL2 } }, +{ "svca", SC(17,1,0), SC_MASK, PWRCOM, { SV } }, +{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } }, + +{ "b", B(18,0,0), B_MASK, COM, { LI } }, +{ "bl", B(18,0,1), B_MASK, COM, { LI } }, +{ "ba", B(18,1,0), B_MASK, COM, { LIA } }, +{ "bla", B(18,1,1), B_MASK, COM, { LIA } }, + +{ "mcrf", XL(19,0), XLBB_MASK|(3 << 21)|(3 << 16), COM, { BF, BFA } }, + +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, PWRCOM, { 0 } }, +{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PWRCOM, { 0 } }, +{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, NOPOWER4, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZM4,16,0), XLBOBIBB_MASK, POWER4, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, NOPOWER4, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP4,16,0), XLBOBIBB_MASK, POWER4, { 0 } }, +{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, NOPOWER4, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZM4,16,1), XLBOBIBB_MASK, POWER4, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, NOPOWER4, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP4,16,1), XLBOBIBB_MASK, POWER4, { 0 } }, +{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, NOPOWER4, { 0 } }, +{ "bdzlr-", XLO(19,BODZM4,16,0), XLBOBIBB_MASK, POWER4, { 0 } }, +{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, NOPOWER4, { 0 } }, +{ "bdzlr+", XLO(19,BODZP4,16,0), XLBOBIBB_MASK, POWER4, { 0 } }, +{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, NOPOWER4, { 0 } }, +{ "bdzlrl-", XLO(19,BODZM4,16,1), XLBOBIBB_MASK, POWER4, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, NOPOWER4, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP4,16,1), XLBOBIBB_MASK, POWER4, { 0 } }, +{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bltlr-", XLOCB(19,BOTM4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bltlr+", XLOCB(19,BOTP4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bltlrl-", XLOCB(19,BOTM4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgtlr-", XLOCB(19,BOTM4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgtlrl-", XLOCB(19,BOTM4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "beqlr-", XLOCB(19,BOTM4,CBEQ,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "beqlr+", XLOCB(19,BOTP4,CBEQ,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "beqlrl-", XLOCB(19,BOTM4,CBEQ,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP4,CBEQ,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bsolr-", XLOCB(19,BOTM4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bsolr+", XLOCB(19,BOTP4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bsolrl-", XLOCB(19,BOTM4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bunlr-", XLOCB(19,BOTM4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bunlr+", XLOCB(19,BOTP4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bunlrl-", XLOCB(19,BOTM4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgelr-", XLOCB(19,BOFM4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgelr+", XLOCB(19,BOFP4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgelrl-", XLOCB(19,BOFM4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnllr-", XLOCB(19,BOFM4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnllr+", XLOCB(19,BOFP4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnllrl-", XLOCB(19,BOFM4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "blelr-", XLOCB(19,BOFM4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "blelr+", XLOCB(19,BOFP4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "blelrl-", XLOCB(19,BOFM4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "blelrl+", XLOCB(19,BOFP4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnglr-", XLOCB(19,BOFM4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnglr+", XLOCB(19,BOFP4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnglrl-", XLOCB(19,BOFM4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnelr-", XLOCB(19,BOFM4,CBEQ,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnelr+", XLOCB(19,BOFP4,CBEQ,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnelrl-", XLOCB(19,BOFM4,CBEQ,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP4,CBEQ,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnslr-", XLOCB(19,BOFM4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnslr+", XLOCB(19,BOFP4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnslrl-", XLOCB(19,BOFM4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnulr-", XLOCB(19,BOFM4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnulr+", XLOCB(19,BOFP4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnulrl-", XLOCB(19,BOFM4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "btlr-", XLO(19,BOTM4,16,0), XLBOBB_MASK, POWER4, { BI } }, +{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "btlr+", XLO(19,BOTP4,16,0), XLBOBB_MASK, POWER4, { BI } }, +{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, PWRCOM, { BI } }, +{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "btlrl-", XLO(19,BOTM4,16,1), XLBOBB_MASK, POWER4, { BI } }, +{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "btlrl+", XLO(19,BOTP4,16,1), XLBOBB_MASK, POWER4, { BI } }, +{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bflr-", XLO(19,BOFM4,16,0), XLBOBB_MASK, POWER4, { BI } }, +{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bflr+", XLO(19,BOFP4,16,0), XLBOBB_MASK, POWER4, { BI } }, +{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bflrl-", XLO(19,BOFM4,16,1), XLBOBB_MASK, POWER4, { BI } }, +{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bflrl+", XLO(19,BOFP4,16,1), XLBOBB_MASK, POWER4, { BI } }, +{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclr", XLLK(19,16,0), XLBH_MASK, PPCCOM, { BO, BI, BH } }, +{ "bclrl", XLLK(19,16,1), XLBH_MASK, PPCCOM, { BO, BI, BH } }, +{ "bcr", XLLK(19,16,0), XLBB_MASK, PWRCOM, { BO, BI } }, +{ "bcrl", XLLK(19,16,1), XLBB_MASK, PWRCOM, { BO, BI } }, +{ "bclre", XLLK(19,17,0), XLBB_MASK, BOOKE64, { BO, BI } }, +{ "bclrel", XLLK(19,17,1), XLBB_MASK, BOOKE64, { BO, BI } }, + +{ "rfid", XL(19,18), 0xffffffff, PPC64, { 0 } }, + +{ "crnot", XL(19,33), XL_MASK, PPCCOM, { BT, BA, BBA } }, +{ "crnor", XL(19,33), XL_MASK, COM, { BT, BA, BB } }, +{ "rfmci", X(19,38), 0xffffffff, PPCRFMCI, { 0 } }, + +{ "rfi", XL(19,50), 0xffffffff, COM, { 0 } }, +{ "rfci", XL(19,51), 0xffffffff, PPC403 | BOOKE, { 0 } }, + +{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } }, + +{ "crandc", XL(19,129), XL_MASK, COM, { BT, BA, BB } }, + +{ "isync", XL(19,150), 0xffffffff, PPCCOM, { 0 } }, +{ "ics", XL(19,150), 0xffffffff, PWRCOM, { 0 } }, + +{ "crclr", XL(19,193), XL_MASK, PPCCOM, { BT, BAT, BBA } }, +{ "crxor", XL(19,193), XL_MASK, COM, { BT, BA, BB } }, + +{ "crnand", XL(19,225), XL_MASK, COM, { BT, BA, BB } }, + +{ "crand", XL(19,257), XL_MASK, COM, { BT, BA, BB } }, + +{ "hrfid", XL(19,274), 0xffffffff, POWER5 | CELL, { 0 } }, + +{ "crset", XL(19,289), XL_MASK, PPCCOM, { BT, BAT, BBA } }, +{ "creqv", XL(19,289), XL_MASK, COM, { BT, BA, BB } }, + +{ "doze", XL(19,402), 0xffffffff, POWER6, { 0 } }, + +{ "crorc", XL(19,417), XL_MASK, COM, { BT, BA, BB } }, + +{ "nap", XL(19,434), 0xffffffff, POWER6, { 0 } }, + +{ "crmove", XL(19,449), XL_MASK, PPCCOM, { BT, BA, BBA } }, +{ "cror", XL(19,449), XL_MASK, COM, { BT, BA, BB } }, + +{ "sleep", XL(19,466), 0xffffffff, POWER6, { 0 } }, +{ "rvwinkle", XL(19,498), 0xffffffff, POWER6, { 0 } }, + +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, COM, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, COM, { 0 } }, +{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bltctr-", XLOCB(19,BOTM4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bltctr+", XLOCB(19,BOTP4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bltctrl-",XLOCB(19,BOTM4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgtctr-", XLOCB(19,BOTM4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgtctrl-",XLOCB(19,BOTM4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "beqctr-", XLOCB(19,BOTM4,CBEQ,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "beqctr+", XLOCB(19,BOTP4,CBEQ,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "beqctrl-",XLOCB(19,BOTM4,CBEQ,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP4,CBEQ,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bsoctr-", XLOCB(19,BOTM4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bsoctrl-",XLOCB(19,BOTM4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bunctr-", XLOCB(19,BOTM4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bunctr+", XLOCB(19,BOTP4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bunctrl-",XLOCB(19,BOTM4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgectr-", XLOCB(19,BOFM4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgectr+", XLOCB(19,BOFP4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgectrl-",XLOCB(19,BOFM4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnlctr-", XLOCB(19,BOFM4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnlctrl-",XLOCB(19,BOFM4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "blectr-", XLOCB(19,BOFM4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "blectr+", XLOCB(19,BOFP4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "blectrl-",XLOCB(19,BOFM4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "blectrl+",XLOCB(19,BOFP4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bngctr-", XLOCB(19,BOFM4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bngctr+", XLOCB(19,BOFP4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bngctrl-",XLOCB(19,BOFM4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnectr-", XLOCB(19,BOFM4,CBEQ,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnectr+", XLOCB(19,BOFP4,CBEQ,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnectrl-",XLOCB(19,BOFM4,CBEQ,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP4,CBEQ,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnsctr-", XLOCB(19,BOFM4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnsctrl-",XLOCB(19,BOFM4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnuctr-", XLOCB(19,BOFM4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnuctrl-",XLOCB(19,BOFM4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } }, +{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "btctr-", XLO(19,BOTM4,528,0), XLBOBB_MASK, POWER4, { BI } }, +{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "btctr+", XLO(19,BOTP4,528,0), XLBOBB_MASK, POWER4, { BI } }, +{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "btctrl-", XLO(19,BOTM4,528,1), XLBOBB_MASK, POWER4, { BI } }, +{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "btctrl+", XLO(19,BOTP4,528,1), XLBOBB_MASK, POWER4, { BI } }, +{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bfctr-", XLO(19,BOFM4,528,0), XLBOBB_MASK, POWER4, { BI } }, +{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bfctr+", XLO(19,BOFP4,528,0), XLBOBB_MASK, POWER4, { BI } }, +{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bfctrl-", XLO(19,BOFM4,528,1), XLBOBB_MASK, POWER4, { BI } }, +{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, NOPOWER4, { BI } }, +{ "bfctrl+", XLO(19,BOFP4,528,1), XLBOBB_MASK, POWER4, { BI } }, +{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctr", XLLK(19,528,0), XLBH_MASK, PPCCOM, { BO, BI, BH } }, +{ "bcctrl", XLLK(19,528,1), XLBH_MASK, PPCCOM, { BO, BI, BH } }, +{ "bcc", XLLK(19,528,0), XLBB_MASK, PWRCOM, { BO, BI } }, +{ "bccl", XLLK(19,528,1), XLBB_MASK, PWRCOM, { BO, BI } }, +{ "bcctre", XLLK(19,529,0), XLBB_MASK, BOOKE64, { BO, BI } }, +{ "bcctrel", XLLK(19,529,1), XLBB_MASK, BOOKE64, { BO, BI } }, + +{ "rlwimi", M(20,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlimi", M(20,0), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rlwimi.", M(20,1), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlimi.", M(20,1), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rotlwi", MME(21,31,0), MMBME_MASK, PPCCOM, { RA, RS, SH } }, +{ "clrlwi", MME(21,31,0), MSHME_MASK, PPCCOM, { RA, RS, MB } }, +{ "rlwinm", M(21,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlinm", M(21,0), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, +{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPCCOM, { RA,RS,SH } }, +{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPCCOM, { RA, RS, MB } }, +{ "rlwinm.", M(21,1), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlinm.", M(21,1), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rlmi", M(22,0), M_MASK, M601, { RA,RS,RB,MBE,ME } }, +{ "rlmi.", M(22,1), M_MASK, M601, { RA,RS,RB,MBE,ME } }, + +{ "be", B(22,0,0), B_MASK, BOOKE64, { LI } }, +{ "bel", B(22,0,1), B_MASK, BOOKE64, { LI } }, +{ "bea", B(22,1,0), B_MASK, BOOKE64, { LIA } }, +{ "bela", B(22,1,1), B_MASK, BOOKE64, { LIA } }, + +{ "rotlw", MME(23,31,0), MMBME_MASK, PPCCOM, { RA, RS, RB } }, +{ "rlwnm", M(23,0), M_MASK, PPCCOM, { RA,RS,RB,MBE,ME } }, +{ "rlnm", M(23,0), M_MASK, PWRCOM, { RA,RS,RB,MBE,ME } }, +{ "rotlw.", MME(23,31,1), MMBME_MASK, PPCCOM, { RA, RS, RB } }, +{ "rlwnm.", M(23,1), M_MASK, PPCCOM, { RA,RS,RB,MBE,ME } }, +{ "rlnm.", M(23,1), M_MASK, PWRCOM, { RA,RS,RB,MBE,ME } }, + +{ "nop", OP(24), 0xffffffff, PPCCOM, { 0 } }, +{ "ori", OP(24), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "oril", OP(24), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "oris", OP(25), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "oriu", OP(25), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "xori", OP(26), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "xoril", OP(26), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "xoris", OP(27), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "xoriu", OP(27), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "andi.", OP(28), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "andil.", OP(28), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "andis.", OP(29), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "andiu.", OP(29), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "rotldi", MD(30,0,0), MDMB_MASK, PPC64, { RA, RS, SH6 } }, +{ "clrldi", MD(30,0,0), MDSH_MASK, PPC64, { RA, RS, MB6 } }, +{ "rldicl", MD(30,0,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC64, { RA, RS, SH6 } }, +{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC64, { RA, RS, MB6 } }, +{ "rldicl.", MD(30,0,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rldicr", MD(30,1,0), MD_MASK, PPC64, { RA, RS, SH6, ME6 } }, +{ "rldicr.", MD(30,1,1), MD_MASK, PPC64, { RA, RS, SH6, ME6 } }, + +{ "rldic", MD(30,2,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rldic.", MD(30,2,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rldimi", MD(30,3,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rldimi.", MD(30,3,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC64, { RA, RS, RB } }, +{ "rldcl", MDS(30,8,0), MDS_MASK, PPC64, { RA, RS, RB, MB6 } }, +{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC64, { RA, RS, RB } }, +{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC64, { RA, RS, RB, MB6 } }, + +{ "rldcr", MDS(30,9,0), MDS_MASK, PPC64, { RA, RS, RB, ME6 } }, +{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC64, { RA, RS, RB, ME6 } }, + +{ "cmpw", XOPL(31,0,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } }, +{ "cmpd", XOPL(31,0,1), XCMPL_MASK, PPC64, { OBF, RA, RB } }, +{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmp", X(31,0), XCMPL_MASK, PWRCOM, { BF, RA, RB } }, + +{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tllt", XTO(31,4,TOLLT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPCCOM, { RA, RB } }, +{ "teq", XTO(31,4,TOEQ), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlge", XTO(31,4,TOLGE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlle", XTO(31,4,TOLLE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlng", XTO(31,4,TOLNG), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tgt", XTO(31,4,TOGT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twge", XTO(31,4,TOGE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tge", XTO(31,4,TOGE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twnl", XTO(31,4,TONL), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tnl", XTO(31,4,TONL), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlt", XTO(31,4,TOLT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twle", XTO(31,4,TOLE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tle", XTO(31,4,TOLE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twng", XTO(31,4,TONG), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tng", XTO(31,4,TONG), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twne", XTO(31,4,TONE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tne", XTO(31,4,TONE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "trap", XTO(31,4,TOU), 0xffffffff, PPCCOM, { 0 } }, +{ "tw", X(31,4), X_MASK, PPCCOM, { TO, RA, RB } }, +{ "t", X(31,4), X_MASK, PWRCOM, { TO, RA, RB } }, + +{ "subfc", XO(31,8,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sf", XO(31,8,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfc.", XO(31,8,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sf.", XO(31,8,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subc.", XO(31,8,0,1), XO_MASK, PPCCOM, { RT, RB, RA } }, +{ "subfco", XO(31,8,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfo", XO(31,8,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco.", XO(31,8,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfo.", XO(31,8,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "addc", XO(31,10,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "a", XO(31,10,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addc.", XO(31,10,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "a.", XO(31,10,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addco", XO(31,10,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ao", XO(31,10,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addco.", XO(31,10,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ao.", XO(31,10,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "isellt", X(31,15), X_MASK, PPCISEL, { RT, RA, RB } }, +{ "iselgt", X(31,47), X_MASK, PPCISEL, { RT, RA, RB } }, +{ "iseleq", X(31,79), X_MASK, PPCISEL, { RT, RA, RB } }, +{ "isel", XISEL(31,15), XISEL_MASK, PPCISEL, { RT, RA, RB, CRB } }, + +{ "mfocrf", XFXM(31,19,0,1), XFXFXM_MASK, COM, { RT, FXM } }, +{ "mfcr", X(31,19), XRARB_MASK, NOPOWER4 | COM, { RT } }, +{ "mfcr", X(31,19), XFXFXM_MASK, POWER4, { RT, FXM4 } }, + +{ "lwarx", X(31,20), XEH_MASK, PPC, { RT, RA0, RB, EH } }, + +{ "ldx", X(31,21), X_MASK, PPC64, { RT, RA0, RB } }, + +{ "icbt", X(31,22), X_MASK, BOOKE|PPCE300, { CT, RA, RB } }, +{ "icbt", X(31,262), XRT_MASK, PPC403, { RA, RB } }, + +{ "lwzx", X(31,23), X_MASK, PPCCOM, { RT, RA0, RB } }, +{ "lx", X(31,23), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "slw", XRC(31,24,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sl", XRC(31,24,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "slw.", XRC(31,24,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sl.", XRC(31,24,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "cntlzw", XRC(31,26,0), XRB_MASK, PPCCOM, { RA, RS } }, +{ "cntlz", XRC(31,26,0), XRB_MASK, PWRCOM, { RA, RS } }, +{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPCCOM, { RA, RS } }, +{ "cntlz.", XRC(31,26,1), XRB_MASK, PWRCOM, { RA, RS } }, + +{ "sld", XRC(31,27,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "sld.", XRC(31,27,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "and", XRC(31,28,0), X_MASK, COM, { RA, RS, RB } }, +{ "and.", XRC(31,28,1), X_MASK, COM, { RA, RS, RB } }, + +{ "maskg", XRC(31,29,0), X_MASK, M601, { RA, RS, RB } }, +{ "maskg.", XRC(31,29,1), X_MASK, M601, { RA, RS, RB } }, + +{ "icbte", X(31,30), X_MASK, BOOKE64, { CT, RA, RB } }, + +{ "lwzxe", X(31,31), X_MASK, BOOKE64, { RT, RA0, RB } }, + +{ "cmplw", XOPL(31,32,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } }, +{ "cmpld", XOPL(31,32,1), XCMPL_MASK, PPC64, { OBF, RA, RB } }, +{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmpl", X(31,32), XCMPL_MASK, PWRCOM, { BF, RA, RB } }, + +{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "ldux", X(31,53), X_MASK, PPC64, { RT, RAL, RB } }, + +{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } }, + +{ "lwzux", X(31,55), X_MASK, PPCCOM, { RT, RAL, RB } }, +{ "lux", X(31,55), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "dcbste", X(31,62), XRT_MASK, BOOKE64, { RA, RB } }, + +{ "lwzuxe", X(31,63), X_MASK, BOOKE64, { RT, RAL, RB } }, + +{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC64, { RA, RS } }, +{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC64, { RA, RS } }, + +{ "andc", XRC(31,60,0), X_MASK, COM, { RA, RS, RB } }, +{ "andc.", XRC(31,60,1), X_MASK, COM, { RA, RS, RB } }, + +{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC64, { RA, RB } }, +{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC64, { RA, RB } }, +{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC64, { RA, RB } }, +{ "td", X(31,68), X_MASK, PPC64, { TO, RA, RB } }, + +{ "mulhd", XO(31,73,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "dlmzb", XRC(31,78,0), X_MASK, PPC403|PPC440, { RA, RS, RB } }, +{ "dlmzb.", XRC(31,78,1), X_MASK, PPC403|PPC440, { RA, RS, RB } }, + +{ "mtsrd", X(31,82), XRB_MASK|(1<<20), PPC64, { SR, RS } }, + +{ "mfmsr", X(31,83), XRARB_MASK, COM, { RT } }, + +{ "ldarx", X(31,84), XEH_MASK, PPC64, { RT, RA0, RB, EH } }, + +{ "dcbfl", XOPL(31,86,1), XRT_MASK, POWER5, { RA, RB } }, +{ "dcbf", X(31,86), XLRT_MASK, PPC, { RA, RB, L } }, + +{ "lbzx", X(31,87), X_MASK, COM, { RT, RA0, RB } }, + +{ "dcbfe", X(31,94), XRT_MASK, BOOKE64, { RA, RB } }, + +{ "lbzxe", X(31,95), X_MASK, BOOKE64, { RT, RA0, RB } }, + +{ "neg", XO(31,104,0,0), XORB_MASK, COM, { RT, RA } }, +{ "neg.", XO(31,104,0,1), XORB_MASK, COM, { RT, RA } }, +{ "nego", XO(31,104,1,0), XORB_MASK, COM, { RT, RA } }, +{ "nego.", XO(31,104,1,1), XORB_MASK, COM, { RT, RA } }, + +{ "mul", XO(31,107,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "mul.", XO(31,107,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "mulo", XO(31,107,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "mulo.", XO(31,107,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "mtsrdin", X(31,114), XRA_MASK, PPC64, { RS, RB } }, + +{ "clf", X(31,118), XTO_MASK, POWER, { RA, RB } }, + +{ "lbzux", X(31,119), X_MASK, COM, { RT, RAL, RB } }, + +{ "popcntb", X(31,122), XRB_MASK, POWER5, { RA, RS } }, + +{ "not", XRC(31,124,0), X_MASK, COM, { RA, RS, RBS } }, +{ "nor", XRC(31,124,0), X_MASK, COM, { RA, RS, RB } }, +{ "not.", XRC(31,124,1), X_MASK, COM, { RA, RS, RBS } }, +{ "nor.", XRC(31,124,1), X_MASK, COM, { RA, RS, RB } }, + +{ "lwarxe", X(31,126), X_MASK, BOOKE64, { RT, RA0, RB } }, + +{ "lbzuxe", X(31,127), X_MASK, BOOKE64, { RT, RAL, RB } }, + +{ "wrtee", X(31,131), XRARB_MASK, PPC403 | BOOKE, { RS } }, + +{ "dcbtstls",X(31,134), X_MASK, PPCCHLK, { CT, RA, RB }}, + +{ "subfe", XO(31,136,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfe", XO(31,136,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfe.", XO(31,136,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfe.", XO(31,136,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfeo", XO(31,136,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfeo", XO(31,136,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfeo.", XO(31,136,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfeo.", XO(31,136,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "adde", XO(31,138,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ae", XO(31,138,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "adde.", XO(31,138,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ae.", XO(31,138,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addeo", XO(31,138,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "aeo", XO(31,138,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addeo.", XO(31,138,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "aeo.", XO(31,138,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "dcbtstlse",X(31,142),X_MASK, PPCCHLK64, { CT, RA, RB }}, + +{ "mtocrf", XFXM(31,144,0,1), XFXFXM_MASK, COM, { FXM, RS } }, +{ "mtcr", XFXM(31,144,0xff,0), XRARB_MASK, COM, { RS }}, +{ "mtcrf", X(31,144), XFXFXM_MASK, COM, { FXM, RS } }, + +{ "mtmsr", X(31,146), XRARB_MASK, COM, { RS } }, + +{ "stdx", X(31,149), X_MASK, PPC64, { RS, RA0, RB } }, + +{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA0, RB } }, + +{ "stwx", X(31,151), X_MASK, PPCCOM, { RS, RA0, RB } }, +{ "stx", X(31,151), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "stwcxe.", XRC(31,158,1), X_MASK, BOOKE64, { RS, RA0, RB } }, + +{ "stwxe", X(31,159), X_MASK, BOOKE64, { RS, RA0, RB } }, + +{ "slq", XRC(31,152,0), X_MASK, M601, { RA, RS, RB } }, +{ "slq.", XRC(31,152,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sle", XRC(31,153,0), X_MASK, M601, { RA, RS, RB } }, +{ "sle.", XRC(31,153,1), X_MASK, M601, { RA, RS, RB } }, + +{ "prtyw", X(31,154), XRB_MASK, POWER6, { RA, RS } }, + +{ "wrteei", X(31,163), XE_MASK, PPC403 | BOOKE, { E } }, + +{ "dcbtls", X(31,166), X_MASK, PPCCHLK, { CT, RA, RB }}, +{ "dcbtlse", X(31,174), X_MASK, PPCCHLK64, { CT, RA, RB }}, + +{ "mtmsrd", X(31,178), XRLARB_MASK, PPC64, { RS, A_L } }, + +{ "stdux", X(31,181), X_MASK, PPC64, { RS, RAS, RB } }, + +{ "stwux", X(31,183), X_MASK, PPCCOM, { RS, RAS, RB } }, +{ "stux", X(31,183), X_MASK, PWRCOM, { RS, RA0, RB } }, + +{ "sliq", XRC(31,184,0), X_MASK, M601, { RA, RS, SH } }, +{ "sliq.", XRC(31,184,1), X_MASK, M601, { RA, RS, SH } }, + +{ "prtyd", X(31,186), XRB_MASK, POWER6, { RA, RS } }, + +{ "stwuxe", X(31,191), X_MASK, BOOKE64, { RS, RAS, RB } }, + +{ "subfze", XO(31,200,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfze", XO(31,200,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfze.", XO(31,200,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfze.", XO(31,200,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfzeo", XO(31,200,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfzeo.", XO(31,200,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "addze", XO(31,202,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "aze", XO(31,202,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addze.", XO(31,202,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "aze.", XO(31,202,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addzeo", XO(31,202,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "azeo", XO(31,202,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "azeo.", XO(31,202,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mtsr", X(31,210), XRB_MASK|(1<<20), COM32, { SR, RS } }, + +{ "stdcx.", XRC(31,214,1), X_MASK, PPC64, { RS, RA0, RB } }, + +{ "stbx", X(31,215), X_MASK, COM, { RS, RA0, RB } }, + +{ "sllq", XRC(31,216,0), X_MASK, M601, { RA, RS, RB } }, +{ "sllq.", XRC(31,216,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sleq", XRC(31,217,0), X_MASK, M601, { RA, RS, RB } }, +{ "sleq.", XRC(31,217,1), X_MASK, M601, { RA, RS, RB } }, + +{ "stbxe", X(31,223), X_MASK, BOOKE64, { RS, RA0, RB } }, + +{ "icblc", X(31,230), X_MASK, PPCCHLK, { CT, RA, RB }}, + +{ "subfme", XO(31,232,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfme", XO(31,232,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfme.", XO(31,232,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfme.", XO(31,232,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfmeo", XO(31,232,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfmeo.", XO(31,232,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mulld", XO(31,233,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulld.", XO(31,233,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulldo", XO(31,233,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "addme", XO(31,234,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ame", XO(31,234,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addme.", XO(31,234,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ame.", XO(31,234,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addmeo", XO(31,234,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ameo", XO(31,234,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ameo.", XO(31,234,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mullw", XO(31,235,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "muls", XO(31,235,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullw.", XO(31,235,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "muls.", XO(31,235,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullwo", XO(31,235,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "mulso", XO(31,235,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullwo.", XO(31,235,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "mulso.", XO(31,235,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "icblce", X(31,238), X_MASK, PPCCHLK64, { CT, RA, RB }}, +{ "mtsrin", X(31,242), XRA_MASK, PPC32, { RS, RB } }, +{ "mtsri", X(31,242), XRA_MASK, POWER32, { RS, RB } }, + +{ "dcbtst", X(31,246), X_MASK, PPC, { CT, RA, RB } }, + +{ "stbux", X(31,247), X_MASK, COM, { RS, RAS, RB } }, + +{ "slliq", XRC(31,248,0), X_MASK, M601, { RA, RS, SH } }, +{ "slliq.", XRC(31,248,1), X_MASK, M601, { RA, RS, SH } }, + +{ "dcbtste", X(31,253), X_MASK, BOOKE64, { CT, RA, RB } }, + +{ "stbuxe", X(31,255), X_MASK, BOOKE64, { RS, RAS, RB } }, + +{ "mfdcrx", X(31,259), X_MASK, BOOKE, { RS, RA } }, + +{ "doz", XO(31,264,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "doz.", XO(31,264,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "dozo", XO(31,264,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "dozo.", XO(31,264,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "add", XO(31,266,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "cax", XO(31,266,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "add.", XO(31,266,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "cax.", XO(31,266,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addo", XO(31,266,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "caxo", XO(31,266,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addo.", XO(31,266,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "caxo.", XO(31,266,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "tlbiel", X(31,274), XRTLRA_MASK, POWER4, { RB, L } }, + +{ "mfapidi", X(31,275), X_MASK, BOOKE, { RT, RA } }, + +{ "lscbx", XRC(31,277,0), X_MASK, M601, { RT, RA, RB } }, +{ "lscbx.", XRC(31,277,1), X_MASK, M601, { RT, RA, RB } }, + +{ "dcbt", X(31,278), X_MASK, PPC, { CT, RA, RB } }, + +{ "lhzx", X(31,279), X_MASK, COM, { RT, RA0, RB } }, + +{ "eqv", XRC(31,284,0), X_MASK, COM, { RA, RS, RB } }, +{ "eqv.", XRC(31,284,1), X_MASK, COM, { RA, RS, RB } }, + +{ "dcbte", X(31,286), X_MASK, BOOKE64, { CT, RA, RB } }, + +{ "lhzxe", X(31,287), X_MASK, BOOKE64, { RT, RA0, RB } }, + +{ "tlbie", X(31,306), XRTLRA_MASK, PPC, { RB, L } }, +{ "tlbi", X(31,306), XRT_MASK, POWER, { RA0, RB } }, + +{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } }, + +{ "lhzux", X(31,311), X_MASK, COM, { RT, RAL, RB } }, + +{ "xor", XRC(31,316,0), X_MASK, COM, { RA, RS, RB } }, +{ "xor.", XRC(31,316,1), X_MASK, COM, { RA, RS, RB } }, + +{ "lhzuxe", X(31,319), X_MASK, BOOKE64, { RT, RAL, RB } }, + +{ "mfexisr", XSPR(31,323,64), XSPR_MASK, PPC403, { RT } }, +{ "mfexier", XSPR(31,323,66), XSPR_MASK, PPC403, { RT } }, +{ "mfbr0", XSPR(31,323,128), XSPR_MASK, PPC403, { RT } }, +{ "mfbr1", XSPR(31,323,129), XSPR_MASK, PPC403, { RT } }, +{ "mfbr2", XSPR(31,323,130), XSPR_MASK, PPC403, { RT } }, +{ "mfbr3", XSPR(31,323,131), XSPR_MASK, PPC403, { RT } }, +{ "mfbr4", XSPR(31,323,132), XSPR_MASK, PPC403, { RT } }, +{ "mfbr5", XSPR(31,323,133), XSPR_MASK, PPC403, { RT } }, +{ "mfbr6", XSPR(31,323,134), XSPR_MASK, PPC403, { RT } }, +{ "mfbr7", XSPR(31,323,135), XSPR_MASK, PPC403, { RT } }, +{ "mfbear", XSPR(31,323,144), XSPR_MASK, PPC403, { RT } }, +{ "mfbesr", XSPR(31,323,145), XSPR_MASK, PPC403, { RT } }, +{ "mfiocr", XSPR(31,323,160), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr0", XSPR(31,323,192), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact0", XSPR(31,323,193), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada0", XSPR(31,323,194), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa0", XSPR(31,323,195), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc0", XSPR(31,323,196), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr1", XSPR(31,323,200), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact1", XSPR(31,323,201), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada1", XSPR(31,323,202), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa1", XSPR(31,323,203), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc1", XSPR(31,323,204), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr2", XSPR(31,323,208), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact2", XSPR(31,323,209), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada2", XSPR(31,323,210), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa2", XSPR(31,323,211), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc2", XSPR(31,323,212), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr3", XSPR(31,323,216), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact3", XSPR(31,323,217), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada3", XSPR(31,323,218), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa3", XSPR(31,323,219), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc3", XSPR(31,323,220), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasr", XSPR(31,323,224), XSPR_MASK, PPC403, { RT } }, +{ "mfdcr", X(31,323), X_MASK, PPC403 | BOOKE, { RT, SPR } }, + +{ "div", XO(31,331,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "div.", XO(31,331,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "divo", XO(31,331,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divo.", XO(31,331,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "mfpmr", X(31,334), X_MASK, PPCPMR, { RT, PMR }}, + +{ "mfmq", XSPR(31,339,0), XSPR_MASK, M601, { RT } }, +{ "mfxer", XSPR(31,339,1), XSPR_MASK, COM, { RT } }, +{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, COM, { RT } }, +{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, COM, { RT } }, +{ "mfdec", XSPR(31,339,6), XSPR_MASK, MFDEC1, { RT } }, +{ "mfdec", XSPR(31,339,22), XSPR_MASK, MFDEC2, { RT } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, COM, { RT } }, +{ "mfctr", XSPR(31,339,9), XSPR_MASK, COM, { RT } }, +{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } }, +{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, COM, { RT } }, +{ "mfdar", XSPR(31,339,19), XSPR_MASK, COM, { RT } }, +{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } }, +{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, COM, { RT } }, +{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, COM, { RT } }, +{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, COM, { RT } }, +{ "mfcfar", XSPR(31,339,28), XSPR_MASK, POWER6, { RT } }, +{ "mfpid", XSPR(31,339,48), XSPR_MASK, BOOKE, { RT } }, +{ "mfpid", XSPR(31,339,945), XSPR_MASK, PPC403, { RT } }, +{ "mfcsrr0", XSPR(31,339,58), XSPR_MASK, BOOKE, { RT } }, +{ "mfcsrr1", XSPR(31,339,59), XSPR_MASK, BOOKE, { RT } }, +{ "mfdear", XSPR(31,339,61), XSPR_MASK, BOOKE, { RT } }, +{ "mfdear", XSPR(31,339,981), XSPR_MASK, PPC403, { RT } }, +{ "mfesr", XSPR(31,339,62), XSPR_MASK, BOOKE, { RT } }, +{ "mfesr", XSPR(31,339,980), XSPR_MASK, PPC403, { RT } }, +{ "mfivpr", XSPR(31,339,63), XSPR_MASK, BOOKE, { RT } }, +{ "mfcmpa", XSPR(31,339,144), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpb", XSPR(31,339,145), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpc", XSPR(31,339,146), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpd", XSPR(31,339,147), XSPR_MASK, PPC860, { RT } }, +{ "mficr", XSPR(31,339,148), XSPR_MASK, PPC860, { RT } }, +{ "mfder", XSPR(31,339,149), XSPR_MASK, PPC860, { RT } }, +{ "mfcounta", XSPR(31,339,150), XSPR_MASK, PPC860, { RT } }, +{ "mfcountb", XSPR(31,339,151), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpe", XSPR(31,339,152), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpf", XSPR(31,339,153), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpg", XSPR(31,339,154), XSPR_MASK, PPC860, { RT } }, +{ "mfcmph", XSPR(31,339,155), XSPR_MASK, PPC860, { RT } }, +{ "mflctrl1", XSPR(31,339,156), XSPR_MASK, PPC860, { RT } }, +{ "mflctrl2", XSPR(31,339,157), XSPR_MASK, PPC860, { RT } }, +{ "mfictrl", XSPR(31,339,158), XSPR_MASK, PPC860, { RT } }, +{ "mfbar", XSPR(31,339,159), XSPR_MASK, PPC860, { RT } }, +{ "mfvrsave", XSPR(31,339,256), XSPR_MASK, PPCVEC, { RT } }, +{ "mfusprg0", XSPR(31,339,256), XSPR_MASK, BOOKE, { RT } }, +{ "mftb", X(31,371), X_MASK, CLASSIC, { RT, TBR } }, +{ "mftb", XSPR(31,339,268), XSPR_MASK, BOOKE, { RT } }, +{ "mftbl", XSPR(31,371,268), XSPR_MASK, CLASSIC, { RT } }, +{ "mftbl", XSPR(31,339,268), XSPR_MASK, BOOKE, { RT } }, +{ "mftbu", XSPR(31,371,269), XSPR_MASK, CLASSIC, { RT } }, +{ "mftbu", XSPR(31,339,269), XSPR_MASK, BOOKE, { RT } }, +{ "mfsprg", XSPR(31,339,256), XSPRG_MASK, PPC, { RT, SPRG } }, +{ "mfsprg0", XSPR(31,339,272), XSPR_MASK, PPC, { RT } }, +{ "mfsprg1", XSPR(31,339,273), XSPR_MASK, PPC, { RT } }, +{ "mfsprg2", XSPR(31,339,274), XSPR_MASK, PPC, { RT } }, +{ "mfsprg3", XSPR(31,339,275), XSPR_MASK, PPC, { RT } }, +{ "mfsprg4", XSPR(31,339,260), XSPR_MASK, PPC405 | BOOKE, { RT } }, +{ "mfsprg5", XSPR(31,339,261), XSPR_MASK, PPC405 | BOOKE, { RT } }, +{ "mfsprg6", XSPR(31,339,262), XSPR_MASK, PPC405 | BOOKE, { RT } }, +{ "mfsprg7", XSPR(31,339,263), XSPR_MASK, PPC405 | BOOKE, { RT } }, +{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC64, { RT } }, +{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } }, +{ "mfpir", XSPR(31,339,286), XSPR_MASK, BOOKE, { RT } }, +{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } }, +{ "mfdbsr", XSPR(31,339,304), XSPR_MASK, BOOKE, { RT } }, +{ "mfdbsr", XSPR(31,339,1008), XSPR_MASK, PPC403, { RT } }, +{ "mfdbcr0", XSPR(31,339,308), XSPR_MASK, BOOKE, { RT } }, +{ "mfdbcr0", XSPR(31,339,1010), XSPR_MASK, PPC405, { RT } }, +{ "mfdbcr1", XSPR(31,339,309), XSPR_MASK, BOOKE, { RT } }, +{ "mfdbcr1", XSPR(31,339,957), XSPR_MASK, PPC405, { RT } }, +{ "mfdbcr2", XSPR(31,339,310), XSPR_MASK, BOOKE, { RT } }, +{ "mfiac1", XSPR(31,339,312), XSPR_MASK, BOOKE, { RT } }, +{ "mfiac1", XSPR(31,339,1012), XSPR_MASK, PPC403, { RT } }, +{ "mfiac2", XSPR(31,339,313), XSPR_MASK, BOOKE, { RT } }, +{ "mfiac2", XSPR(31,339,1013), XSPR_MASK, PPC403, { RT } }, +{ "mfiac3", XSPR(31,339,314), XSPR_MASK, BOOKE, { RT } }, +{ "mfiac3", XSPR(31,339,948), XSPR_MASK, PPC405, { RT } }, +{ "mfiac4", XSPR(31,339,315), XSPR_MASK, BOOKE, { RT } }, +{ "mfiac4", XSPR(31,339,949), XSPR_MASK, PPC405, { RT } }, +{ "mfdac1", XSPR(31,339,316), XSPR_MASK, BOOKE, { RT } }, +{ "mfdac1", XSPR(31,339,1014), XSPR_MASK, PPC403, { RT } }, +{ "mfdac2", XSPR(31,339,317), XSPR_MASK, BOOKE, { RT } }, +{ "mfdac2", XSPR(31,339,1015), XSPR_MASK, PPC403, { RT } }, +{ "mfdvc1", XSPR(31,339,318), XSPR_MASK, BOOKE, { RT } }, +{ "mfdvc1", XSPR(31,339,950), XSPR_MASK, PPC405, { RT } }, +{ "mfdvc2", XSPR(31,339,319), XSPR_MASK, BOOKE, { RT } }, +{ "mfdvc2", XSPR(31,339,951), XSPR_MASK, PPC405, { RT } }, +{ "mftsr", XSPR(31,339,336), XSPR_MASK, BOOKE, { RT } }, +{ "mftsr", XSPR(31,339,984), XSPR_MASK, PPC403, { RT } }, +{ "mftcr", XSPR(31,339,340), XSPR_MASK, BOOKE, { RT } }, +{ "mftcr", XSPR(31,339,986), XSPR_MASK, PPC403, { RT } }, +{ "mfivor0", XSPR(31,339,400), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor1", XSPR(31,339,401), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor2", XSPR(31,339,402), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor3", XSPR(31,339,403), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor4", XSPR(31,339,404), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor5", XSPR(31,339,405), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor6", XSPR(31,339,406), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor7", XSPR(31,339,407), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor8", XSPR(31,339,408), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor9", XSPR(31,339,409), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor10", XSPR(31,339,410), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor11", XSPR(31,339,411), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor12", XSPR(31,339,412), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor13", XSPR(31,339,413), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor14", XSPR(31,339,414), XSPR_MASK, BOOKE, { RT } }, +{ "mfivor15", XSPR(31,339,415), XSPR_MASK, BOOKE, { RT } }, +{ "mfspefscr", XSPR(31,339,512), XSPR_MASK, PPCSPE, { RT } }, +{ "mfbbear", XSPR(31,339,513), XSPR_MASK, PPCBRLK, { RT } }, +{ "mfbbtar", XSPR(31,339,514), XSPR_MASK, PPCBRLK, { RT } }, +{ "mfivor32", XSPR(31,339,528), XSPR_MASK, PPCSPE, { RT } }, +{ "mfivor33", XSPR(31,339,529), XSPR_MASK, PPCSPE, { RT } }, +{ "mfivor34", XSPR(31,339,530), XSPR_MASK, PPCSPE, { RT } }, +{ "mfivor35", XSPR(31,339,531), XSPR_MASK, PPCPMR, { RT } }, +{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfic_cst", XSPR(31,339,560), XSPR_MASK, PPC860, { RT } }, +{ "mfic_adr", XSPR(31,339,561), XSPR_MASK, PPC860, { RT } }, +{ "mfic_dat", XSPR(31,339,562), XSPR_MASK, PPC860, { RT } }, +{ "mfdc_cst", XSPR(31,339,568), XSPR_MASK, PPC860, { RT } }, +{ "mfdc_adr", XSPR(31,339,569), XSPR_MASK, PPC860, { RT } }, +{ "mfmcsrr0", XSPR(31,339,570), XSPR_MASK, PPCRFMCI, { RT } }, +{ "mfdc_dat", XSPR(31,339,570), XSPR_MASK, PPC860, { RT } }, +{ "mfmcsrr1", XSPR(31,339,571), XSPR_MASK, PPCRFMCI, { RT } }, +{ "mfmcsr", XSPR(31,339,572), XSPR_MASK, PPCRFMCI, { RT } }, +{ "mfmcar", XSPR(31,339,573), XSPR_MASK, PPCRFMCI, { RT } }, +{ "mfdpdr", XSPR(31,339,630), XSPR_MASK, PPC860, { RT } }, +{ "mfdpir", XSPR(31,339,631), XSPR_MASK, PPC860, { RT } }, +{ "mfimmr", XSPR(31,339,638), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_ctr", XSPR(31,339,784), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_ap", XSPR(31,339,786), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_epn", XSPR(31,339,787), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_twc", XSPR(31,339,789), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_rpn", XSPR(31,339,790), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_ctr", XSPR(31,339,792), XSPR_MASK, PPC860, { RT } }, +{ "mfm_casid", XSPR(31,339,793), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_ap", XSPR(31,339,794), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_epn", XSPR(31,339,795), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_twb", XSPR(31,339,796), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_twc", XSPR(31,339,797), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_rpn", XSPR(31,339,798), XSPR_MASK, PPC860, { RT } }, +{ "mfm_tw", XSPR(31,339,799), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbcam", XSPR(31,339,816), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbram0",XSPR(31,339,817), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbram1",XSPR(31,339,818), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbcam", XSPR(31,339,824), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbram0",XSPR(31,339,825), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbram1",XSPR(31,339,826), XSPR_MASK, PPC860, { RT } }, +{ "mfummcr0", XSPR(31,339,936), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc1", XSPR(31,339,937), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc2", XSPR(31,339,938), XSPR_MASK, PPC750, { RT } }, +{ "mfusia", XSPR(31,339,939), XSPR_MASK, PPC750, { RT } }, +{ "mfummcr1", XSPR(31,339,940), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc3", XSPR(31,339,941), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc4", XSPR(31,339,942), XSPR_MASK, PPC750, { RT } }, +{ "mfzpr", XSPR(31,339,944), XSPR_MASK, PPC403, { RT } }, +{ "mfccr0", XSPR(31,339,947), XSPR_MASK, PPC405, { RT } }, +{ "mfmmcr0", XSPR(31,339,952), XSPR_MASK, PPC750, { RT } }, +{ "mfpmc1", XSPR(31,339,953), XSPR_MASK, PPC750, { RT } }, +{ "mfsgr", XSPR(31,339,953), XSPR_MASK, PPC403, { RT } }, +{ "mfpmc2", XSPR(31,339,954), XSPR_MASK, PPC750, { RT } }, +{ "mfdcwr", XSPR(31,339,954), XSPR_MASK, PPC403, { RT } }, +{ "mfsia", XSPR(31,339,955), XSPR_MASK, PPC750, { RT } }, +{ "mfsler", XSPR(31,339,955), XSPR_MASK, PPC405, { RT } }, +{ "mfmmcr1", XSPR(31,339,956), XSPR_MASK, PPC750, { RT } }, +{ "mfsu0r", XSPR(31,339,956), XSPR_MASK, PPC405, { RT } }, +{ "mfpmc3", XSPR(31,339,957), XSPR_MASK, PPC750, { RT } }, +{ "mfpmc4", XSPR(31,339,958), XSPR_MASK, PPC750, { RT } }, +{ "mficdbdr", XSPR(31,339,979), XSPR_MASK, PPC403, { RT } }, +{ "mfevpr", XSPR(31,339,982), XSPR_MASK, PPC403, { RT } }, +{ "mfcdbcr", XSPR(31,339,983), XSPR_MASK, PPC403, { RT } }, +{ "mfpit", XSPR(31,339,987), XSPR_MASK, PPC403, { RT } }, +{ "mftbhi", XSPR(31,339,988), XSPR_MASK, PPC403, { RT } }, +{ "mftblo", XSPR(31,339,989), XSPR_MASK, PPC403, { RT } }, +{ "mfsrr2", XSPR(31,339,990), XSPR_MASK, PPC403, { RT } }, +{ "mfsrr3", XSPR(31,339,991), XSPR_MASK, PPC403, { RT } }, +{ "mfl2cr", XSPR(31,339,1017), XSPR_MASK, PPC750, { RT } }, +{ "mfdccr", XSPR(31,339,1018), XSPR_MASK, PPC403, { RT } }, +{ "mficcr", XSPR(31,339,1019), XSPR_MASK, PPC403, { RT } }, +{ "mfictc", XSPR(31,339,1019), XSPR_MASK, PPC750, { RT } }, +{ "mfpbl1", XSPR(31,339,1020), XSPR_MASK, PPC403, { RT } }, +{ "mfthrm1", XSPR(31,339,1020), XSPR_MASK, PPC750, { RT } }, +{ "mfpbu1", XSPR(31,339,1021), XSPR_MASK, PPC403, { RT } }, +{ "mfthrm2", XSPR(31,339,1021), XSPR_MASK, PPC750, { RT } }, +{ "mfpbl2", XSPR(31,339,1022), XSPR_MASK, PPC403, { RT } }, +{ "mfthrm3", XSPR(31,339,1022), XSPR_MASK, PPC750, { RT } }, +{ "mfpbu2", XSPR(31,339,1023), XSPR_MASK, PPC403, { RT } }, +{ "mfspr", X(31,339), X_MASK, COM, { RT, SPR } }, + +{ "lwax", X(31,341), X_MASK, PPC64, { RT, RA0, RB } }, + +{ "dst", XDSS(31,342,0), XDSS_MASK, PPCVEC, { RA, RB, STRM } }, +{ "dstt", XDSS(31,342,1), XDSS_MASK, PPCVEC, { RA, RB, STRM } }, + +{ "lhax", X(31,343), X_MASK, COM, { RT, RA0, RB } }, + +{ "lhaxe", X(31,351), X_MASK, BOOKE64, { RT, RA0, RB } }, + +{ "dstst", XDSS(31,374,0), XDSS_MASK, PPCVEC, { RA, RB, STRM } }, +{ "dststt", XDSS(31,374,1), XDSS_MASK, PPCVEC, { RA, RB, STRM } }, + +{ "dccci", X(31,454), XRT_MASK, PPC403|PPC440, { RA, RB } }, + +{ "abs", XO(31,360,0,0), XORB_MASK, M601, { RT, RA } }, +{ "abs.", XO(31,360,0,1), XORB_MASK, M601, { RT, RA } }, +{ "abso", XO(31,360,1,0), XORB_MASK, M601, { RT, RA } }, +{ "abso.", XO(31,360,1,1), XORB_MASK, M601, { RT, RA } }, + +{ "divs", XO(31,363,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divs.", XO(31,363,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "divso", XO(31,363,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divso.", XO(31,363,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } }, + +{ "lwaux", X(31,373), X_MASK, PPC64, { RT, RAL, RB } }, + +{ "lhaux", X(31,375), X_MASK, COM, { RT, RAL, RB } }, + +{ "lhauxe", X(31,383), X_MASK, BOOKE64, { RT, RAL, RB } }, + +{ "mtdcrx", X(31,387), X_MASK, BOOKE, { RA, RS } }, + +{ "dcblc", X(31,390), X_MASK, PPCCHLK, { CT, RA, RB }}, + +{ "subfe64", XO(31,392,0,0), XO_MASK, BOOKE64, { RT, RA, RB } }, +{ "subfe64o",XO(31,392,1,0), XO_MASK, BOOKE64, { RT, RA, RB } }, + +{ "adde64", XO(31,394,0,0), XO_MASK, BOOKE64, { RT, RA, RB } }, +{ "adde64o", XO(31,394,1,0), XO_MASK, BOOKE64, { RT, RA, RB } }, + +{ "dcblce", X(31,398), X_MASK, PPCCHLK64, { CT, RA, RB }}, + +{ "slbmte", X(31,402), XRA_MASK, PPC64, { RS, RB } }, + +{ "sthx", X(31,407), X_MASK, COM, { RS, RA0, RB } }, + +{ "cmpb", X(31,508), X_MASK, POWER6, { RA, RS, RB } }, + +{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "lfdpx", X(31,791), X_MASK, POWER6, { FRT, RA, RB } }, + +{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "stfdpx", X(31,919), X_MASK, POWER6, { FRS, RA, RB } }, + +{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "orc", XRC(31,412,0), X_MASK, COM, { RA, RS, RB } }, +{ "orc.", XRC(31,412,1), X_MASK, COM, { RA, RS, RB } }, + +{ "sradi", XS(31,413,0), XS_MASK, PPC64, { RA, RS, SH6 } }, +{ "sradi.", XS(31,413,1), XS_MASK, PPC64, { RA, RS, SH6 } }, + +{ "sthxe", X(31,415), X_MASK, BOOKE64, { RS, RA0, RB } }, + +{ "slbie", X(31,434), XRTRA_MASK, PPC64, { RB } }, + +{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } }, + +{ "sthux", X(31,439), X_MASK, COM, { RS, RAS, RB } }, + +{ "sthuxe", X(31,447), X_MASK, BOOKE64, { RS, RAS, RB } }, + +{ "cctpl", 0x7c210b78, 0xffffffff, CELL, { 0 }}, +{ "cctpm", 0x7c421378, 0xffffffff, CELL, { 0 }}, +{ "cctph", 0x7c631b78, 0xffffffff, CELL, { 0 }}, +{ "db8cyc", 0x7f9ce378, 0xffffffff, CELL, { 0 }}, +{ "db10cyc", 0x7fbdeb78, 0xffffffff, CELL, { 0 }}, +{ "db12cyc", 0x7fdef378, 0xffffffff, CELL, { 0 }}, +{ "db16cyc", 0x7ffffb78, 0xffffffff, CELL, { 0 }}, +{ "mr", XRC(31,444,0), X_MASK, COM, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, COM, { RA, RS, RB } }, +{ "mr.", XRC(31,444,1), X_MASK, COM, { RA, RS, RBS } }, +{ "or.", XRC(31,444,1), X_MASK, COM, { RA, RS, RB } }, + +{ "mtexisr", XSPR(31,451,64), XSPR_MASK, PPC403, { RS } }, +{ "mtexier", XSPR(31,451,66), XSPR_MASK, PPC403, { RS } }, +{ "mtbr0", XSPR(31,451,128), XSPR_MASK, PPC403, { RS } }, +{ "mtbr1", XSPR(31,451,129), XSPR_MASK, PPC403, { RS } }, +{ "mtbr2", XSPR(31,451,130), XSPR_MASK, PPC403, { RS } }, +{ "mtbr3", XSPR(31,451,131), XSPR_MASK, PPC403, { RS } }, +{ "mtbr4", XSPR(31,451,132), XSPR_MASK, PPC403, { RS } }, +{ "mtbr5", XSPR(31,451,133), XSPR_MASK, PPC403, { RS } }, +{ "mtbr6", XSPR(31,451,134), XSPR_MASK, PPC403, { RS } }, +{ "mtbr7", XSPR(31,451,135), XSPR_MASK, PPC403, { RS } }, +{ "mtbear", XSPR(31,451,144), XSPR_MASK, PPC403, { RS } }, +{ "mtbesr", XSPR(31,451,145), XSPR_MASK, PPC403, { RS } }, +{ "mtiocr", XSPR(31,451,160), XSPR_MASK, PPC403, { RS } }, +{ "mtdmacr0", XSPR(31,451,192), XSPR_MASK, PPC403, { RS } }, +{ "mtdmact0", XSPR(31,451,193), XSPR_MASK, PPC403, { RS } }, +{ "mtdmada0", XSPR(31,451,194), XSPR_MASK, PPC403, { RS } }, +{ "mtdmasa0", XSPR(31,451,195), XSPR_MASK, PPC403, { RS } }, +{ "mtdmacc0", XSPR(31,451,196), XSPR_MASK, PPC403, { RS } }, +{ "mtdmacr1", XSPR(31,451,200), XSPR_MASK, PPC403, { RS } }, +{ "mtdmact1", XSPR(31,451,201), XSPR_MASK, PPC403, { RS } }, +{ "mtdmada1", XSPR(31,451,202), XSPR_MASK, PPC403, { RS } }, +{ "mtdmasa1", XSPR(31,451,203), XSPR_MASK, PPC403, { RS } }, +{ "mtdmacc1", XSPR(31,451,204), XSPR_MASK, PPC403, { RS } }, +{ "mtdmacr2", XSPR(31,451,208), XSPR_MASK, PPC403, { RS } }, +{ "mtdmact2", XSPR(31,451,209), XSPR_MASK, PPC403, { RS } }, +{ "mtdmada2", XSPR(31,451,210), XSPR_MASK, PPC403, { RS } }, +{ "mtdmasa2", XSPR(31,451,211), XSPR_MASK, PPC403, { RS } }, +{ "mtdmacc2", XSPR(31,451,212), XSPR_MASK, PPC403, { RS } }, +{ "mtdmacr3", XSPR(31,451,216), XSPR_MASK, PPC403, { RS } }, +{ "mtdmact3", XSPR(31,451,217), XSPR_MASK, PPC403, { RS } }, +{ "mtdmada3", XSPR(31,451,218), XSPR_MASK, PPC403, { RS } }, +{ "mtdmasa3", XSPR(31,451,219), XSPR_MASK, PPC403, { RS } }, +{ "mtdmacc3", XSPR(31,451,220), XSPR_MASK, PPC403, { RS } }, +{ "mtdmasr", XSPR(31,451,224), XSPR_MASK, PPC403, { RS } }, +{ "mtdcr", X(31,451), X_MASK, PPC403 | BOOKE, { SPR, RS } }, + +{ "subfze64",XO(31,456,0,0), XORB_MASK, BOOKE64, { RT, RA } }, +{ "subfze64o",XO(31,456,1,0), XORB_MASK, BOOKE64, { RT, RA } }, + +{ "divdu", XO(31,457,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdu.", XO(31,457,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divduo", XO(31,457,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divduo.", XO(31,457,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "addze64", XO(31,458,0,0), XORB_MASK, BOOKE64, { RT, RA } }, +{ "addze64o",XO(31,458,1,0), XORB_MASK, BOOKE64, { RT, RA } }, + +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtmq", XSPR(31,467,0), XSPR_MASK, M601, { RS } }, +{ "mtxer", XSPR(31,467,1), XSPR_MASK, COM, { RS } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, COM, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, COM, { RS } }, +{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } }, +{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, COM, { RS } }, +{ "mtdar", XSPR(31,467,19), XSPR_MASK, COM, { RS } }, +{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, COM, { RS } }, +{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, COM, { RS } }, +{ "mtdec", XSPR(31,467,22), XSPR_MASK, COM, { RS } }, +{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } }, +{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, COM, { RS } }, +{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, COM, { RS } }, +{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, COM, { RS } }, +{ "mtcfar", XSPR(31,467,28), XSPR_MASK, POWER6, { RS } }, +{ "mtpid", XSPR(31,467,48), XSPR_MASK, BOOKE, { RS } }, +{ "mtpid", XSPR(31,467,945), XSPR_MASK, PPC403, { RS } }, +{ "mtdecar", XSPR(31,467,54), XSPR_MASK, BOOKE, { RS } }, +{ "mtcsrr0", XSPR(31,467,58), XSPR_MASK, BOOKE, { RS } }, +{ "mtcsrr1", XSPR(31,467,59), XSPR_MASK, BOOKE, { RS } }, +{ "mtdear", XSPR(31,467,61), XSPR_MASK, BOOKE, { RS } }, +{ "mtdear", XSPR(31,467,981), XSPR_MASK, PPC403, { RS } }, +{ "mtesr", XSPR(31,467,62), XSPR_MASK, BOOKE, { RS } }, +{ "mtesr", XSPR(31,467,980), XSPR_MASK, PPC403, { RS } }, +{ "mtivpr", XSPR(31,467,63), XSPR_MASK, BOOKE, { RS } }, +{ "mtcmpa", XSPR(31,467,144), XSPR_MASK, PPC860, { RS } }, +{ "mtcmpb", XSPR(31,467,145), XSPR_MASK, PPC860, { RS } }, +{ "mtcmpc", XSPR(31,467,146), XSPR_MASK, PPC860, { RS } }, +{ "mtcmpd", XSPR(31,467,147), XSPR_MASK, PPC860, { RS } }, +{ "mticr", XSPR(31,467,148), XSPR_MASK, PPC860, { RS } }, +{ "mtder", XSPR(31,467,149), XSPR_MASK, PPC860, { RS } }, +{ "mtcounta", XSPR(31,467,150), XSPR_MASK, PPC860, { RS } }, +{ "mtcountb", XSPR(31,467,151), XSPR_MASK, PPC860, { RS } }, +{ "mtcmpe", XSPR(31,467,152), XSPR_MASK, PPC860, { RS } }, +{ "mtcmpf", XSPR(31,467,153), XSPR_MASK, PPC860, { RS } }, +{ "mtcmpg", XSPR(31,467,154), XSPR_MASK, PPC860, { RS } }, +{ "mtcmph", XSPR(31,467,155), XSPR_MASK, PPC860, { RS } }, +{ "mtlctrl1", XSPR(31,467,156), XSPR_MASK, PPC860, { RS } }, +{ "mtlctrl2", XSPR(31,467,157), XSPR_MASK, PPC860, { RS } }, +{ "mtictrl", XSPR(31,467,158), XSPR_MASK, PPC860, { RS } }, +{ "mtbar", XSPR(31,467,159), XSPR_MASK, PPC860, { RS } }, +{ "mtvrsave", XSPR(31,467,256), XSPR_MASK, PPCVEC, { RS } }, +{ "mtusprg0", XSPR(31,467,256), XSPR_MASK, BOOKE, { RS } }, +{ "mtsprg", XSPR(31,467,256), XSPRG_MASK,PPC, { SPRG, RS } }, +{ "mtsprg0", XSPR(31,467,272), XSPR_MASK, PPC, { RS } }, +{ "mtsprg1", XSPR(31,467,273), XSPR_MASK, PPC, { RS } }, +{ "mtsprg2", XSPR(31,467,274), XSPR_MASK, PPC, { RS } }, +{ "mtsprg3", XSPR(31,467,275), XSPR_MASK, PPC, { RS } }, +{ "mtsprg4", XSPR(31,467,276), XSPR_MASK, PPC405 | BOOKE, { RS } }, +{ "mtsprg5", XSPR(31,467,277), XSPR_MASK, PPC405 | BOOKE, { RS } }, +{ "mtsprg6", XSPR(31,467,278), XSPR_MASK, PPC405 | BOOKE, { RS } }, +{ "mtsprg7", XSPR(31,467,279), XSPR_MASK, PPC405 | BOOKE, { RS } }, +{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC64, { RS } }, +{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } }, +{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } }, +{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } }, +{ "mtdbsr", XSPR(31,467,304), XSPR_MASK, BOOKE, { RS } }, +{ "mtdbsr", XSPR(31,467,1008), XSPR_MASK, PPC403, { RS } }, +{ "mtdbcr0", XSPR(31,467,308), XSPR_MASK, BOOKE, { RS } }, +{ "mtdbcr0", XSPR(31,467,1010), XSPR_MASK, PPC405, { RS } }, +{ "mtdbcr1", XSPR(31,467,309), XSPR_MASK, BOOKE, { RS } }, +{ "mtdbcr1", XSPR(31,467,957), XSPR_MASK, PPC405, { RS } }, +{ "mtdbcr2", XSPR(31,467,310), XSPR_MASK, BOOKE, { RS } }, +{ "mtiac1", XSPR(31,467,312), XSPR_MASK, BOOKE, { RS } }, +{ "mtiac1", XSPR(31,467,1012), XSPR_MASK, PPC403, { RS } }, +{ "mtiac2", XSPR(31,467,313), XSPR_MASK, BOOKE, { RS } }, +{ "mtiac2", XSPR(31,467,1013), XSPR_MASK, PPC403, { RS } }, +{ "mtiac3", XSPR(31,467,314), XSPR_MASK, BOOKE, { RS } }, +{ "mtiac3", XSPR(31,467,948), XSPR_MASK, PPC405, { RS } }, +{ "mtiac4", XSPR(31,467,315), XSPR_MASK, BOOKE, { RS } }, +{ "mtiac4", XSPR(31,467,949), XSPR_MASK, PPC405, { RS } }, +{ "mtdac1", XSPR(31,467,316), XSPR_MASK, BOOKE, { RS } }, +{ "mtdac1", XSPR(31,467,1014), XSPR_MASK, PPC403, { RS } }, +{ "mtdac2", XSPR(31,467,317), XSPR_MASK, BOOKE, { RS } }, +{ "mtdac2", XSPR(31,467,1015), XSPR_MASK, PPC403, { RS } }, +{ "mtdvc1", XSPR(31,467,318), XSPR_MASK, BOOKE, { RS } }, +{ "mtdvc1", XSPR(31,467,950), XSPR_MASK, PPC405, { RS } }, +{ "mtdvc2", XSPR(31,467,319), XSPR_MASK, BOOKE, { RS } }, +{ "mtdvc2", XSPR(31,467,951), XSPR_MASK, PPC405, { RS } }, +{ "mttsr", XSPR(31,467,336), XSPR_MASK, BOOKE, { RS } }, +{ "mttsr", XSPR(31,467,984), XSPR_MASK, PPC403, { RS } }, +{ "mttcr", XSPR(31,467,340), XSPR_MASK, BOOKE, { RS } }, +{ "mttcr", XSPR(31,467,986), XSPR_MASK, PPC403, { RS } }, +{ "mtivor0", XSPR(31,467,400), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor1", XSPR(31,467,401), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor2", XSPR(31,467,402), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor3", XSPR(31,467,403), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor4", XSPR(31,467,404), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor5", XSPR(31,467,405), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor6", XSPR(31,467,406), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor7", XSPR(31,467,407), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor8", XSPR(31,467,408), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor9", XSPR(31,467,409), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor10", XSPR(31,467,410), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor11", XSPR(31,467,411), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor12", XSPR(31,467,412), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor13", XSPR(31,467,413), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor14", XSPR(31,467,414), XSPR_MASK, BOOKE, { RS } }, +{ "mtivor15", XSPR(31,467,415), XSPR_MASK, BOOKE, { RS } }, +{ "mtspefscr", XSPR(31,467,512), XSPR_MASK, PPCSPE, { RS } }, +{ "mtbbear", XSPR(31,467,513), XSPR_MASK, PPCBRLK, { RS } }, +{ "mtbbtar", XSPR(31,467,514), XSPR_MASK, PPCBRLK, { RS } }, +{ "mtivor32", XSPR(31,467,528), XSPR_MASK, PPCSPE, { RS } }, +{ "mtivor33", XSPR(31,467,529), XSPR_MASK, PPCSPE, { RS } }, +{ "mtivor34", XSPR(31,467,530), XSPR_MASK, PPCSPE, { RS } }, +{ "mtivor35", XSPR(31,467,531), XSPR_MASK, PPCPMR, { RS } }, +{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtmcsrr0", XSPR(31,467,570), XSPR_MASK, PPCRFMCI, { RS } }, +{ "mtmcsrr1", XSPR(31,467,571), XSPR_MASK, PPCRFMCI, { RS } }, +{ "mtmcsr", XSPR(31,467,572), XSPR_MASK, PPCRFMCI, { RS } }, +{ "mtummcr0", XSPR(31,467,936), XSPR_MASK, PPC750, { RS } }, +{ "mtupmc1", XSPR(31,467,937), XSPR_MASK, PPC750, { RS } }, +{ "mtupmc2", XSPR(31,467,938), XSPR_MASK, PPC750, { RS } }, +{ "mtusia", XSPR(31,467,939), XSPR_MASK, PPC750, { RS } }, +{ "mtummcr1", XSPR(31,467,940), XSPR_MASK, PPC750, { RS } }, +{ "mtupmc3", XSPR(31,467,941), XSPR_MASK, PPC750, { RS } }, +{ "mtupmc4", XSPR(31,467,942), XSPR_MASK, PPC750, { RS } }, +{ "mtzpr", XSPR(31,467,944), XSPR_MASK, PPC403, { RS } }, +{ "mtccr0", XSPR(31,467,947), XSPR_MASK, PPC405, { RS } }, +{ "mtmmcr0", XSPR(31,467,952), XSPR_MASK, PPC750, { RS } }, +{ "mtsgr", XSPR(31,467,953), XSPR_MASK, PPC403, { RS } }, +{ "mtpmc1", XSPR(31,467,953), XSPR_MASK, PPC750, { RS } }, +{ "mtdcwr", XSPR(31,467,954), XSPR_MASK, PPC403, { RS } }, +{ "mtpmc2", XSPR(31,467,954), XSPR_MASK, PPC750, { RS } }, +{ "mtsler", XSPR(31,467,955), XSPR_MASK, PPC405, { RS } }, +{ "mtsia", XSPR(31,467,955), XSPR_MASK, PPC750, { RS } }, +{ "mtsu0r", XSPR(31,467,956), XSPR_MASK, PPC405, { RS } }, +{ "mtmmcr1", XSPR(31,467,956), XSPR_MASK, PPC750, { RS } }, +{ "mtpmc3", XSPR(31,467,957), XSPR_MASK, PPC750, { RS } }, +{ "mtpmc4", XSPR(31,467,958), XSPR_MASK, PPC750, { RS } }, +{ "mticdbdr", XSPR(31,467,979), XSPR_MASK, PPC403, { RS } }, +{ "mtevpr", XSPR(31,467,982), XSPR_MASK, PPC403, { RS } }, +{ "mtcdbcr", XSPR(31,467,983), XSPR_MASK, PPC403, { RS } }, +{ "mtpit", XSPR(31,467,987), XSPR_MASK, PPC403, { RS } }, +{ "mttbhi", XSPR(31,467,988), XSPR_MASK, PPC403, { RS } }, +{ "mttblo", XSPR(31,467,989), XSPR_MASK, PPC403, { RS } }, +{ "mtsrr2", XSPR(31,467,990), XSPR_MASK, PPC403, { RS } }, +{ "mtsrr3", XSPR(31,467,991), XSPR_MASK, PPC403, { RS } }, +{ "mtl2cr", XSPR(31,467,1017), XSPR_MASK, PPC750, { RS } }, +{ "mtdccr", XSPR(31,467,1018), XSPR_MASK, PPC403, { RS } }, +{ "mticcr", XSPR(31,467,1019), XSPR_MASK, PPC403, { RS } }, +{ "mtictc", XSPR(31,467,1019), XSPR_MASK, PPC750, { RS } }, +{ "mtpbl1", XSPR(31,467,1020), XSPR_MASK, PPC403, { RS } }, +{ "mtthrm1", XSPR(31,467,1020), XSPR_MASK, PPC750, { RS } }, +{ "mtpbu1", XSPR(31,467,1021), XSPR_MASK, PPC403, { RS } }, +{ "mtthrm2", XSPR(31,467,1021), XSPR_MASK, PPC750, { RS } }, +{ "mtpbl2", XSPR(31,467,1022), XSPR_MASK, PPC403, { RS } }, +{ "mtthrm3", XSPR(31,467,1022), XSPR_MASK, PPC750, { RS } }, +{ "mtpbu2", XSPR(31,467,1023), XSPR_MASK, PPC403, { RS } }, +{ "mtspr", X(31,467), X_MASK, COM, { SPR, RS } }, + +{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } }, + +{ "nand", XRC(31,476,0), X_MASK, COM, { RA, RS, RB } }, +{ "nand.", XRC(31,476,1), X_MASK, COM, { RA, RS, RB } }, + +{ "dcbie", X(31,478), XRT_MASK, BOOKE64, { RA, RB } }, + +{ "dcread", X(31,486), X_MASK, PPC403|PPC440, { RT, RA, RB }}, + +{ "mtpmr", X(31,462), X_MASK, PPCPMR, { PMR, RS }}, + +{ "icbtls", X(31,486), X_MASK, PPCCHLK, { CT, RA, RB }}, + +{ "nabs", XO(31,488,0,0), XORB_MASK, M601, { RT, RA } }, +{ "subfme64",XO(31,488,0,0), XORB_MASK, BOOKE64, { RT, RA } }, +{ "nabs.", XO(31,488,0,1), XORB_MASK, M601, { RT, RA } }, +{ "nabso", XO(31,488,1,0), XORB_MASK, M601, { RT, RA } }, +{ "subfme64o",XO(31,488,1,0), XORB_MASK, BOOKE64, { RT, RA } }, +{ "nabso.", XO(31,488,1,1), XORB_MASK, M601, { RT, RA } }, + +{ "divd", XO(31,489,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divd.", XO(31,489,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdo", XO(31,489,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdo.", XO(31,489,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "addme64", XO(31,490,0,0), XORB_MASK, BOOKE64, { RT, RA } }, +{ "addme64o",XO(31,490,1,0), XORB_MASK, BOOKE64, { RT, RA } }, + +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "icbtlse", X(31,494), X_MASK, PPCCHLK64, { CT, RA, RB }}, + +{ "slbia", X(31,498), 0xffffffff, PPC64, { 0 } }, + +{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } }, + +{ "stdcxe.", XRC(31,511,1), X_MASK, BOOKE64, { RS, RA, RB } }, + +{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), COM, { BF } }, + +{ "bblels", X(31,518), X_MASK, PPCBRLK, { 0 }}, +{ "mcrxr64", X(31,544), XRARB_MASK|(3<<21), BOOKE64, { BF } }, + +{ "clcs", X(31,531), XRB_MASK, M601, { RT, RA } }, + +{ "ldbrx", X(31,532), X_MASK, CELL, { RT, RA0, RB } }, + +{ "lswx", X(31,533), X_MASK, PPCCOM, { RT, RA0, RB } }, +{ "lsx", X(31,533), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lwbrx", X(31,534), X_MASK, PPCCOM, { RT, RA0, RB } }, +{ "lbrx", X(31,534), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lfsx", X(31,535), X_MASK, COM, { FRT, RA0, RB } }, + +{ "srw", XRC(31,536,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sr", XRC(31,536,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "srw.", XRC(31,536,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sr.", XRC(31,536,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "rrib", XRC(31,537,0), X_MASK, M601, { RA, RS, RB } }, +{ "rrib.", XRC(31,537,1), X_MASK, M601, { RA, RS, RB } }, + +{ "srd", XRC(31,539,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "srd.", XRC(31,539,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "maskir", XRC(31,541,0), X_MASK, M601, { RA, RS, RB } }, +{ "maskir.", XRC(31,541,1), X_MASK, M601, { RA, RS, RB } }, + +{ "lwbrxe", X(31,542), X_MASK, BOOKE64, { RT, RA0, RB } }, + +{ "lfsxe", X(31,543), X_MASK, BOOKE64, { FRT, RA0, RB } }, + +{ "bbelr", X(31,550), X_MASK, PPCBRLK, { 0 }}, + +{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } }, + +{ "lfsux", X(31,567), X_MASK, COM, { FRT, RAS, RB } }, + +{ "lfsuxe", X(31,575), X_MASK, BOOKE64, { FRT, RAS, RB } }, + +{ "mfsr", X(31,595), XRB_MASK|(1<<20), COM32, { RT, SR } }, + +{ "lswi", X(31,597), X_MASK, PPCCOM, { RT, RA0, NB } }, +{ "lsi", X(31,597), X_MASK, PWRCOM, { RT, RA0, NB } }, + +{ "lwsync", XSYNC(31,598,1), 0xffffffff, PPC, { 0 } }, +{ "ptesync", XSYNC(31,598,2), 0xffffffff, PPC64, { 0 } }, +{ "msync", X(31,598), 0xffffffff, BOOKE, { 0 } }, +{ "sync", X(31,598), XSYNC_MASK, PPCCOM, { LS } }, +{ "dcs", X(31,598), 0xffffffff, PWRCOM, { 0 } }, + +{ "lfdx", X(31,599), X_MASK, COM, { FRT, RA0, RB } }, + +{ "lfdxe", X(31,607), X_MASK, BOOKE64, { FRT, RA0, RB } }, + +{ "mffgpr", XRC(31,607,0), XRA_MASK, POWER6, { FRT, RB } }, + +{ "mfsri", X(31,627), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "dclst", X(31,630), XRB_MASK, PWRCOM, { RS, RA } }, + +{ "lfdux", X(31,631), X_MASK, COM, { FRT, RAS, RB } }, + +{ "lfduxe", X(31,639), X_MASK, BOOKE64, { FRT, RAS, RB } }, + +{ "mfsrin", X(31,659), XRA_MASK, PPC32, { RT, RB } }, + +{ "stdbrx", X(31,660), X_MASK, CELL, { RS, RA0, RB } }, + +{ "stswx", X(31,661), X_MASK, PPCCOM, { RS, RA0, RB } }, +{ "stsx", X(31,661), X_MASK, PWRCOM, { RS, RA0, RB } }, + +{ "stwbrx", X(31,662), X_MASK, PPCCOM, { RS, RA0, RB } }, +{ "stbrx", X(31,662), X_MASK, PWRCOM, { RS, RA0, RB } }, + +{ "stfsx", X(31,663), X_MASK, COM, { FRS, RA0, RB } }, + +{ "srq", XRC(31,664,0), X_MASK, M601, { RA, RS, RB } }, +{ "srq.", XRC(31,664,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sre", XRC(31,665,0), X_MASK, M601, { RA, RS, RB } }, +{ "sre.", XRC(31,665,1), X_MASK, M601, { RA, RS, RB } }, + +{ "stwbrxe", X(31,670), X_MASK, BOOKE64, { RS, RA0, RB } }, + +{ "stfsxe", X(31,671), X_MASK, BOOKE64, { FRS, RA0, RB } }, + +{ "stfsux", X(31,695), X_MASK, COM, { FRS, RAS, RB } }, + +{ "sriq", XRC(31,696,0), X_MASK, M601, { RA, RS, SH } }, +{ "sriq.", XRC(31,696,1), X_MASK, M601, { RA, RS, SH } }, + +{ "stfsuxe", X(31,703), X_MASK, BOOKE64, { FRS, RAS, RB } }, + +{ "stswi", X(31,725), X_MASK, PPCCOM, { RS, RA0, NB } }, +{ "stsi", X(31,725), X_MASK, PWRCOM, { RS, RA0, NB } }, + +{ "stfdx", X(31,727), X_MASK, COM, { FRS, RA0, RB } }, + +{ "srlq", XRC(31,728,0), X_MASK, M601, { RA, RS, RB } }, +{ "srlq.", XRC(31,728,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sreq", XRC(31,729,0), X_MASK, M601, { RA, RS, RB } }, +{ "sreq.", XRC(31,729,1), X_MASK, M601, { RA, RS, RB } }, + +{ "stfdxe", X(31,735), X_MASK, BOOKE64, { FRS, RA0, RB } }, + +{ "mftgpr", XRC(31,735,0), XRA_MASK, POWER6, { RT, FRB } }, + +{ "dcba", X(31,758), XRT_MASK, PPC405 | BOOKE, { RA, RB } }, + +{ "stfdux", X(31,759), X_MASK, COM, { FRS, RAS, RB } }, + +{ "srliq", XRC(31,760,0), X_MASK, M601, { RA, RS, SH } }, +{ "srliq.", XRC(31,760,1), X_MASK, M601, { RA, RS, SH } }, + +{ "dcbae", X(31,766), XRT_MASK, BOOKE64, { RA, RB } }, + +{ "stfduxe", X(31,767), X_MASK, BOOKE64, { FRS, RAS, RB } }, + +{ "tlbivax", X(31,786), XRT_MASK, BOOKE, { RA, RB } }, +{ "tlbivaxe",X(31,787), XRT_MASK, BOOKE64, { RA, RB } }, + +{ "lwzcix", X(31,789), X_MASK, POWER6, { RT, RA0, RB } }, + +{ "lhbrx", X(31,790), X_MASK, COM, { RT, RA0, RB } }, + +{ "sraw", XRC(31,792,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sra", XRC(31,792,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "sraw.", XRC(31,792,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sra.", XRC(31,792,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "srad", XRC(31,794,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "srad.", XRC(31,794,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "lhbrxe", X(31,798), X_MASK, BOOKE64, { RT, RA0, RB } }, + +{ "ldxe", X(31,799), X_MASK, BOOKE64, { RT, RA0, RB } }, +{ "lduxe", X(31,831), X_MASK, BOOKE64, { RT, RA0, RB } }, + +{ "rac", X(31,818), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lhzcix", X(31,821), X_MASK, POWER6, { RT, RA0, RB } }, + +{ "dss", XDSS(31,822,0), XDSS_MASK, PPCVEC, { STRM } }, +{ "dssall", XDSS(31,822,1), XDSS_MASK, PPCVEC, { 0 } }, + +{ "srawi", XRC(31,824,0), X_MASK, PPCCOM, { RA, RS, SH } }, +{ "srai", XRC(31,824,0), X_MASK, PWRCOM, { RA, RS, SH } }, +{ "srawi.", XRC(31,824,1), X_MASK, PPCCOM, { RA, RS, SH } }, +{ "srai.", XRC(31,824,1), X_MASK, PWRCOM, { RA, RS, SH } }, + +{ "slbmfev", X(31,851), XRA_MASK, PPC64, { RT, RB } }, + +{ "lbzcix", X(31,853), X_MASK, POWER6, { RT, RA0, RB } }, + +{ "mbar", X(31,854), X_MASK, BOOKE, { MO } }, +{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } }, + +{ "lfiwax", X(31,855), X_MASK, POWER6, { FRT, RA0, RB } }, + +{ "ldcix", X(31,885), X_MASK, POWER6, { RT, RA0, RB } }, + +{ "tlbsx", XRC(31,914,0), X_MASK, PPC403|BOOKE, { RTO, RA, RB } }, +{ "tlbsx.", XRC(31,914,1), X_MASK, PPC403|BOOKE, { RTO, RA, RB } }, +{ "tlbsxe", XRC(31,915,0), X_MASK, BOOKE64, { RTO, RA, RB } }, +{ "tlbsxe.", XRC(31,915,1), X_MASK, BOOKE64, { RTO, RA, RB } }, + +{ "slbmfee", X(31,915), XRA_MASK, PPC64, { RT, RB } }, + +{ "stwcix", X(31,917), X_MASK, POWER6, { RS, RA0, RB } }, + +{ "sthbrx", X(31,918), X_MASK, COM, { RS, RA0, RB } }, + +{ "sraq", XRC(31,920,0), X_MASK, M601, { RA, RS, RB } }, +{ "sraq.", XRC(31,920,1), X_MASK, M601, { RA, RS, RB } }, + +{ "srea", XRC(31,921,0), X_MASK, M601, { RA, RS, RB } }, +{ "srea.", XRC(31,921,1), X_MASK, M601, { RA, RS, RB } }, + +{ "extsh", XRC(31,922,0), XRB_MASK, PPCCOM, { RA, RS } }, +{ "exts", XRC(31,922,0), XRB_MASK, PWRCOM, { RA, RS } }, +{ "extsh.", XRC(31,922,1), XRB_MASK, PPCCOM, { RA, RS } }, +{ "exts.", XRC(31,922,1), XRB_MASK, PWRCOM, { RA, RS } }, + +{ "sthbrxe", X(31,926), X_MASK, BOOKE64, { RS, RA0, RB } }, + +{ "stdxe", X(31,927), X_MASK, BOOKE64, { RS, RA0, RB } }, + +{ "tlbrehi", XTLB(31,946,0), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbrelo", XTLB(31,946,1), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbre", X(31,946), X_MASK, PPC403|BOOKE, { RSO, RAOPT, SHO } }, + +{ "sthcix", X(31,949), X_MASK, POWER6, { RS, RA0, RB } }, + +{ "sraiq", XRC(31,952,0), X_MASK, M601, { RA, RS, SH } }, +{ "sraiq.", XRC(31,952,1), X_MASK, M601, { RA, RS, SH } }, + +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, +{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} }, + +{ "stduxe", X(31,959), X_MASK, BOOKE64, { RS, RAS, RB } }, + +{ "iccci", X(31,966), XRT_MASK, PPC403|PPC440, { RA, RB } }, + +{ "tlbwehi", XTLB(31,978,0), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbwelo", XTLB(31,978,1), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbwe", X(31,978), X_MASK, PPC403|BOOKE, { RSO, RAOPT, SHO } }, +{ "tlbld", X(31,978), XRTRA_MASK, PPC, { RB } }, + +{ "stbcix", X(31,981), X_MASK, POWER6, { RS, RA0, RB } }, + +{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } }, + +{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA0, RB } }, + +{ "extsw", XRC(31,986,0), XRB_MASK, PPC64 | BOOKE64,{ RA, RS } }, +{ "extsw.", XRC(31,986,1), XRB_MASK, PPC64, { RA, RS } }, + +{ "icread", X(31,998), XRT_MASK, PPC403|PPC440, { RA, RB } }, + +{ "icbie", X(31,990), XRT_MASK, BOOKE64, { RA, RB } }, +{ "stfiwxe", X(31,991), X_MASK, BOOKE64, { FRS, RA0, RB } }, + +{ "tlbli", X(31,1010), XRTRA_MASK, PPC, { RB } }, + +{ "stdcix", X(31,1013), X_MASK, POWER6, { RS, RA0, RB } }, + +{ "dcbzl", XOPL(31,1014,1), XRT_MASK,POWER4, { RA, RB } }, +{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, +{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, + +{ "dcbze", X(31,1022), XRT_MASK, BOOKE64, { RA, RB } }, + +{ "lvebx", X(31, 7), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvehx", X(31, 39), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvewx", X(31, 71), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvsl", X(31, 6), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvsr", X(31, 38), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvx", X(31, 103), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvxl", X(31, 359), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "stvebx", X(31, 135), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvehx", X(31, 167), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvewx", X(31, 199), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvx", X(31, 231), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvxl", X(31, 487), X_MASK, PPCVEC, { VS, RA, RB } }, + +/* New load/store left/right index vector instructions that are in the Cell only. */ +{ "lvlx", X(31, 519), X_MASK, CELL, { VD, RA0, RB } }, +{ "lvlxl", X(31, 775), X_MASK, CELL, { VD, RA0, RB } }, +{ "lvrx", X(31, 551), X_MASK, CELL, { VD, RA0, RB } }, +{ "lvrxl", X(31, 807), X_MASK, CELL, { VD, RA0, RB } }, +{ "stvlx", X(31, 647), X_MASK, CELL, { VS, RA0, RB } }, +{ "stvlxl", X(31, 903), X_MASK, CELL, { VS, RA0, RB } }, +{ "stvrx", X(31, 679), X_MASK, CELL, { VS, RA0, RB } }, +{ "stvrxl", X(31, 935), X_MASK, CELL, { VS, RA0, RB } }, + +{ "lwz", OP(32), OP_MASK, PPCCOM, { RT, D, RA0 } }, +{ "l", OP(32), OP_MASK, PWRCOM, { RT, D, RA0 } }, + +{ "lwzu", OP(33), OP_MASK, PPCCOM, { RT, D, RAL } }, +{ "lu", OP(33), OP_MASK, PWRCOM, { RT, D, RA0 } }, + +{ "lbz", OP(34), OP_MASK, COM, { RT, D, RA0 } }, + +{ "lbzu", OP(35), OP_MASK, COM, { RT, D, RAL } }, + +{ "stw", OP(36), OP_MASK, PPCCOM, { RS, D, RA0 } }, +{ "st", OP(36), OP_MASK, PWRCOM, { RS, D, RA0 } }, + +{ "stwu", OP(37), OP_MASK, PPCCOM, { RS, D, RAS } }, +{ "stu", OP(37), OP_MASK, PWRCOM, { RS, D, RA0 } }, + +{ "stb", OP(38), OP_MASK, COM, { RS, D, RA0 } }, + +{ "stbu", OP(39), OP_MASK, COM, { RS, D, RAS } }, + +{ "lhz", OP(40), OP_MASK, COM, { RT, D, RA0 } }, + +{ "lhzu", OP(41), OP_MASK, COM, { RT, D, RAL } }, + +{ "lha", OP(42), OP_MASK, COM, { RT, D, RA0 } }, + +{ "lhau", OP(43), OP_MASK, COM, { RT, D, RAL } }, + +{ "sth", OP(44), OP_MASK, COM, { RS, D, RA0 } }, + +{ "sthu", OP(45), OP_MASK, COM, { RS, D, RAS } }, + +{ "lmw", OP(46), OP_MASK, PPCCOM, { RT, D, RAM } }, +{ "lm", OP(46), OP_MASK, PWRCOM, { RT, D, RA0 } }, + +{ "stmw", OP(47), OP_MASK, PPCCOM, { RS, D, RA0 } }, +{ "stm", OP(47), OP_MASK, PWRCOM, { RS, D, RA0 } }, + +{ "lfs", OP(48), OP_MASK, COM, { FRT, D, RA0 } }, + +{ "lfsu", OP(49), OP_MASK, COM, { FRT, D, RAS } }, + +{ "lfd", OP(50), OP_MASK, COM, { FRT, D, RA0 } }, + +{ "lfdu", OP(51), OP_MASK, COM, { FRT, D, RAS } }, + +{ "stfs", OP(52), OP_MASK, COM, { FRS, D, RA0 } }, + +{ "stfsu", OP(53), OP_MASK, COM, { FRS, D, RAS } }, + +{ "stfd", OP(54), OP_MASK, COM, { FRS, D, RA0 } }, + +{ "stfdu", OP(55), OP_MASK, COM, { FRS, D, RAS } }, + +{ "lq", OP(56), OP_MASK, POWER4, { RTQ, DQ, RAQ } }, + +{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA0 } }, + +{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA0 } }, + +{ "lfdp", OP(57), OP_MASK, POWER6, { FRT, D, RA0 } }, + +{ "lbze", DEO(58,0), DE_MASK, BOOKE64, { RT, DE, RA0 } }, +{ "lbzue", DEO(58,1), DE_MASK, BOOKE64, { RT, DE, RAL } }, +{ "lhze", DEO(58,2), DE_MASK, BOOKE64, { RT, DE, RA0 } }, +{ "lhzue", DEO(58,3), DE_MASK, BOOKE64, { RT, DE, RAL } }, +{ "lhae", DEO(58,4), DE_MASK, BOOKE64, { RT, DE, RA0 } }, +{ "lhaue", DEO(58,5), DE_MASK, BOOKE64, { RT, DE, RAL } }, +{ "lwze", DEO(58,6), DE_MASK, BOOKE64, { RT, DE, RA0 } }, +{ "lwzue", DEO(58,7), DE_MASK, BOOKE64, { RT, DE, RAL } }, +{ "stbe", DEO(58,8), DE_MASK, BOOKE64, { RS, DE, RA0 } }, +{ "stbue", DEO(58,9), DE_MASK, BOOKE64, { RS, DE, RAS } }, +{ "sthe", DEO(58,10), DE_MASK, BOOKE64, { RS, DE, RA0 } }, +{ "sthue", DEO(58,11), DE_MASK, BOOKE64, { RS, DE, RAS } }, +{ "stwe", DEO(58,14), DE_MASK, BOOKE64, { RS, DE, RA0 } }, +{ "stwue", DEO(58,15), DE_MASK, BOOKE64, { RS, DE, RAS } }, + +{ "ld", DSO(58,0), DS_MASK, PPC64, { RT, DS, RA0 } }, + +{ "ldu", DSO(58,1), DS_MASK, PPC64, { RT, DS, RAL } }, + +{ "lwa", DSO(58,2), DS_MASK, PPC64, { RT, DS, RA0 } }, + +{ "dadd", XRC(59,2,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "dadd.", XRC(59,2,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "dqua", ZRC(59,3,0), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, +{ "dqua.", ZRC(59,3,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fres", A(59,24,0), AFRALFRC_MASK, PPC, { FRT, FRB, A_L } }, +{ "fres.", A(59,24,1), AFRALFRC_MASK, PPC, { FRT, FRB, A_L } }, + +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, + +{ "frsqrtes", A(59,26,0), AFRALFRC_MASK,POWER5, { FRT, FRB, A_L } }, +{ "frsqrtes.",A(59,26,1), AFRALFRC_MASK,POWER5, { FRT, FRB, A_L } }, + +{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "dmul", XRC(59,34,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "dmul.", XRC(59,34,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "drrnd", ZRC(59,35,0), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, +{ "drrnd.", ZRC(59,35,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, + +{ "dscli", ZRC(59,66,0), Z_MASK, POWER6, { FRT, FRA, SH16 } }, +{ "dscli.", ZRC(59,66,1), Z_MASK, POWER6, { FRT, FRA, SH16 } }, + +{ "dquai", ZRC(59,67,0), Z2_MASK, POWER6, { TE, FRT, FRB, RMC } }, +{ "dquai.", ZRC(59,67,1), Z2_MASK, POWER6, { TE, FRT, FRB, RMC } }, + +{ "dscri", ZRC(59,98,0), Z_MASK, POWER6, { FRT, FRA, SH16 } }, +{ "dscri.", ZRC(59,98,1), Z_MASK, POWER6, { FRT, FRA, SH16 } }, + +{ "drintx", ZRC(59,99,0), Z2_MASK, POWER6, { R, FRT, FRB, RMC } }, +{ "drintx.", ZRC(59,99,1), Z2_MASK, POWER6, { R, FRT, FRB, RMC } }, + +{ "dcmpo", X(59,130), X_MASK, POWER6, { BF, FRA, FRB } }, + +{ "dtstex", X(59,162), X_MASK, POWER6, { BF, FRA, FRB } }, +{ "dtstdc", Z(59,194), Z_MASK, POWER6, { BF, FRA, DCM } }, +{ "dtstdg", Z(59,226), Z_MASK, POWER6, { BF, FRA, DGM } }, + +{ "drintn", ZRC(59,227,0), Z2_MASK, POWER6, { R, FRT, FRB, RMC } }, +{ "drintn.", ZRC(59,227,1), Z2_MASK, POWER6, { R, FRT, FRB, RMC } }, + +{ "dctdp", XRC(59,258,0), X_MASK, POWER6, { FRT, FRB } }, +{ "dctdp.", XRC(59,258,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "dctfix", XRC(59,290,0), X_MASK, POWER6, { FRT, FRB } }, +{ "dctfix.", XRC(59,290,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "ddedpd", XRC(59,322,0), X_MASK, POWER6, { SP, FRT, FRB } }, +{ "ddedpd.", XRC(59,322,1), X_MASK, POWER6, { SP, FRT, FRB } }, + +{ "dxex", XRC(59,354,0), X_MASK, POWER6, { FRT, FRB } }, +{ "dxex.", XRC(59,354,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "dsub", XRC(59,514,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "dsub.", XRC(59,514,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "ddiv", XRC(59,546,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "ddiv.", XRC(59,546,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "dcmpu", X(59,642), X_MASK, POWER6, { BF, FRA, FRB } }, + +{ "dtstsf", X(59,674), X_MASK, POWER6, { BF, FRA, FRB } }, + +{ "drsp", XRC(59,770,0), X_MASK, POWER6, { FRT, FRB } }, +{ "drsp.", XRC(59,770,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "dcffix", XRC(59,802,0), X_MASK, POWER6, { FRT, FRB } }, +{ "dcffix.", XRC(59,802,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "denbcd", XRC(59,834,0), X_MASK, POWER6, { S, FRT, FRB } }, +{ "denbcd.", XRC(59,834,1), X_MASK, POWER6, { S, FRT, FRB } }, + +{ "diex", XRC(59,866,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "diex.", XRC(59,866,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfdp", OP(61), OP_MASK, POWER6, { FRT, D, RA0 } }, + +{ "lde", DEO(62,0), DE_MASK, BOOKE64, { RT, DES, RA0 } }, +{ "ldue", DEO(62,1), DE_MASK, BOOKE64, { RT, DES, RA0 } }, +{ "lfse", DEO(62,4), DE_MASK, BOOKE64, { FRT, DES, RA0 } }, +{ "lfsue", DEO(62,5), DE_MASK, BOOKE64, { FRT, DES, RAS } }, +{ "lfde", DEO(62,6), DE_MASK, BOOKE64, { FRT, DES, RA0 } }, +{ "lfdue", DEO(62,7), DE_MASK, BOOKE64, { FRT, DES, RAS } }, +{ "stde", DEO(62,8), DE_MASK, BOOKE64, { RS, DES, RA0 } }, +{ "stdue", DEO(62,9), DE_MASK, BOOKE64, { RS, DES, RAS } }, +{ "stfse", DEO(62,12), DE_MASK, BOOKE64, { FRS, DES, RA0 } }, +{ "stfsue", DEO(62,13), DE_MASK, BOOKE64, { FRS, DES, RAS } }, +{ "stfde", DEO(62,14), DE_MASK, BOOKE64, { FRS, DES, RA0 } }, +{ "stfdue", DEO(62,15), DE_MASK, BOOKE64, { FRS, DES, RAS } }, + +{ "std", DSO(62,0), DS_MASK, PPC64, { RS, DS, RA0 } }, + +{ "stdu", DSO(62,1), DS_MASK, PPC64, { RS, DS, RAS } }, + +{ "stq", DSO(62,2), DS_MASK, POWER4, { RSQ, DS, RA0 } }, + +{ "fcmpu", X(63,0), X_MASK|(3<<21), COM, { BF, FRA, FRB } }, + +{ "daddq", XRC(63,2,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "daddq.", XRC(63,2,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "dquaq", ZRC(63,3,0), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, +{ "dquaq.", ZRC(63,3,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, + +{ "fcpsgn", XRC(63,8,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "fcpsgn.", XRC(63,8,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "frsp", XRC(63,12,0), XRA_MASK, COM, { FRT, FRB } }, +{ "frsp.", XRC(63,12,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "fctiw", XRC(63,14,0), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiw.", XRC(63,14,1), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fdiv", A(63,18,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fd", A(63,18,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fdiv.", A(63,18,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fd.", A(63,18,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fsub", A(63,20,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fs", A(63,20,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fsub.", A(63,20,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fs.", A(63,20,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fadd", A(63,21,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fa", A(63,21,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fadd.", A(63,21,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fa.", A(63,21,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPCPWR2, { FRT, FRB } }, +{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPCPWR2, { FRT, FRB } }, + +{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fre", A(63,24,0), AFRALFRC_MASK, POWER5, { FRT, FRB, A_L } }, +{ "fre.", A(63,24,1), AFRALFRC_MASK, POWER5, { FRT, FRB, A_L } }, + +{ "fmul", A(63,25,0), AFRB_MASK, PPCCOM, { FRT, FRA, FRC } }, +{ "fm", A(63,25,0), AFRB_MASK, PWRCOM, { FRT, FRA, FRC } }, +{ "fmul.", A(63,25,1), AFRB_MASK, PPCCOM, { FRT, FRA, FRC } }, +{ "fm.", A(63,25,1), AFRB_MASK, PWRCOM, { FRT, FRA, FRC } }, + +{ "frsqrte", A(63,26,0), AFRALFRC_MASK, PPC, { FRT, FRB, A_L } }, +{ "frsqrte.",A(63,26,1), AFRALFRC_MASK, PPC, { FRT, FRB, A_L } }, + +{ "fmsub", A(63,28,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fms", A(63,28,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fmsub.", A(63,28,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fms.", A(63,28,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fmadd", A(63,29,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fma", A(63,29,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fmadd.", A(63,29,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fma.", A(63,29,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fnmsub", A(63,30,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnms", A(63,30,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fnmsub.", A(63,30,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnms.", A(63,30,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fnmadd", A(63,31,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnma", A(63,31,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fnmadd.", A(63,31,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnma.", A(63,31,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fcmpo", X(63,32), X_MASK|(3<<21), COM, { BF, FRA, FRB } }, + +{ "dmulq", XRC(63,34,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "dmulq.", XRC(63,34,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "drrndq", ZRC(63,35,0), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, +{ "drrndq.", ZRC(63,35,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, + +{ "mtfsb1", XRC(63,38,0), XRARB_MASK, COM, { BT } }, +{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, COM, { BT } }, + +{ "fneg", XRC(63,40,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fneg.", XRC(63,40,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), COM, { BF, BFA } }, + +{ "dscliq", ZRC(63,66,0), Z_MASK, POWER6, { FRT, FRA, SH16 } }, +{ "dscliq.", ZRC(63,66,1), Z_MASK, POWER6, { FRT, FRA, SH16 } }, + +{ "dquaiq", ZRC(63,67,0), Z2_MASK, POWER6, { TE, FRT, FRB, RMC } }, +{ "dquaiq.", ZRC(63,67,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } }, + +{ "mtfsb0", XRC(63,70,0), XRARB_MASK, COM, { BT } }, +{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, COM, { BT } }, + +{ "fmr", XRC(63,72,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fmr.", XRC(63,72,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "dscriq", ZRC(63,98,0), Z_MASK, POWER6, { FRT, FRA, SH16 } }, +{ "dscriq.", ZRC(63,98,1), Z_MASK, POWER6, { FRT, FRA, SH16 } }, + +{ "drintxq", ZRC(63,99,0), Z2_MASK, POWER6, { R, FRT, FRB, RMC } }, +{ "drintxq.",ZRC(63,99,1), Z2_MASK, POWER6, { R, FRT, FRB, RMC } }, + +{ "dcmpoq", X(63,130), X_MASK, POWER6, { BF, FRA, FRB } }, + +{ "mtfsfi", XRC(63,134,0), XWRA_MASK|(3<<21)|(1<<11), COM, { BFF, U, W } }, +{ "mtfsfi.", XRC(63,134,1), XWRA_MASK|(3<<21)|(1<<11), COM, { BFF, U, W } }, + +{ "fnabs", XRC(63,136,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fnabs.", XRC(63,136,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "dtstexq", X(63,162), X_MASK, POWER6, { BF, FRA, FRB } }, +{ "dtstdcq", Z(63,194), Z_MASK, POWER6, { BF, FRA, DCM } }, +{ "dtstdgq", Z(63,226), Z_MASK, POWER6, { BF, FRA, DGM } }, + +{ "drintnq", ZRC(63,227,0), Z2_MASK, POWER6, { R, FRT, FRB, RMC } }, +{ "drintnq.",ZRC(63,227,1), Z2_MASK, POWER6, { R, FRT, FRB, RMC } }, + +{ "dctqpq", XRC(63,258,0), X_MASK, POWER6, { FRT, FRB } }, +{ "dctqpq.", XRC(63,258,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "fabs", XRC(63,264,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fabs.", XRC(63,264,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "dctfixq", XRC(63,290,0), X_MASK, POWER6, { FRT, FRB } }, +{ "dctfixq.",XRC(63,290,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "ddedpdq", XRC(63,322,0), X_MASK, POWER6, { SP, FRT, FRB } }, +{ "ddedpdq.",XRC(63,322,1), X_MASK, POWER6, { SP, FRT, FRB } }, + +{ "dxexq", XRC(63,354,0), X_MASK, POWER6, { FRT, FRB } }, +{ "dxexq.", XRC(63,354,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "frin", XRC(63,392,0), XRA_MASK, POWER5, { FRT, FRB } }, +{ "frin.", XRC(63,392,1), XRA_MASK, POWER5, { FRT, FRB } }, +{ "friz", XRC(63,424,0), XRA_MASK, POWER5, { FRT, FRB } }, +{ "friz.", XRC(63,424,1), XRA_MASK, POWER5, { FRT, FRB } }, +{ "frip", XRC(63,456,0), XRA_MASK, POWER5, { FRT, FRB } }, +{ "frip.", XRC(63,456,1), XRA_MASK, POWER5, { FRT, FRB } }, +{ "frim", XRC(63,488,0), XRA_MASK, POWER5, { FRT, FRB } }, +{ "frim.", XRC(63,488,1), XRA_MASK, POWER5, { FRT, FRB } }, + +{ "dsubq", XRC(63,514,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "dsubq.", XRC(63,514,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "ddivq", XRC(63,546,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "ddivq.", XRC(63,546,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +{ "mffs", XRC(63,583,0), XRARB_MASK, COM, { FRT } }, +{ "mffs.", XRC(63,583,1), XRARB_MASK, COM, { FRT } }, + +{ "dcmpuq", X(63,642), X_MASK, POWER6, { BF, FRA, FRB } }, + +{ "dtstsfq", X(63,674), X_MASK, POWER6, { BF, FRA, FRB } }, + +{ "mtfsf", XFL(63,711,0), XFL_MASK, COM, { FLM, FRB, XFL_L, W } }, +{ "mtfsf.", XFL(63,711,1), XFL_MASK, COM, { FLM, FRB, XFL_L, W } }, + +{ "drdpq", XRC(63,770,0), X_MASK, POWER6, { FRT, FRB } }, +{ "drdpq.", XRC(63,770,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "dcffixq", XRC(63,802,0), X_MASK, POWER6, { FRT, FRB } }, +{ "dcffixq.",XRC(63,802,1), X_MASK, POWER6, { FRT, FRB } }, + +{ "fctid", XRC(63,814,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fctid.", XRC(63,814,1), XRA_MASK, PPC64, { FRT, FRB } }, + +{ "fctidz", XRC(63,815,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC64, { FRT, FRB } }, + +{ "denbcdq", XRC(63,834,0), X_MASK, POWER6, { S, FRT, FRB } }, +{ "denbcdq.",XRC(63,834,1), X_MASK, POWER6, { S, FRT, FRB } }, + +{ "fcfid", XRC(63,846,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC64, { FRT, FRB } }, + +{ "diexq", XRC(63,866,0), X_MASK, POWER6, { FRT, FRA, FRB } }, +{ "diexq.", XRC(63,866,1), X_MASK, POWER6, { FRT, FRA, FRB } }, + +}; + +const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); + +/* The macro table. This is only used by the assembler. */ + +/* The expressions of the form (-x ! 31) & (x | 31) have the value 0 + when x=0; 32-x when x is between 1 and 31; are negative if x is + negative; and are 32 or more otherwise. This is what you want + when, for instance, you are emulating a right shift by a + rotate-left-and-mask, because the underlying instructions support + shifts of size 0 but not shifts of size 32. By comparison, when + extracting x bits from some word you want to use just 32-x, because + the underlying instructions don't support extracting 0 bits but do + support extracting the whole word (32 bits in this case). */ + +const struct powerpc_macro powerpc_macros[] = { +{ "extldi", 4, PPC64, "rldicr %0,%1,%3,(%2)-1" }, +{ "extldi.", 4, PPC64, "rldicr. %0,%1,%3,(%2)-1" }, +{ "extrdi", 4, PPC64, "rldicl %0,%1,(%2)+(%3),64-(%2)" }, +{ "extrdi.", 4, PPC64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" }, +{ "insrdi", 4, PPC64, "rldimi %0,%1,64-((%2)+(%3)),%3" }, +{ "insrdi.", 4, PPC64, "rldimi. %0,%1,64-((%2)+(%3)),%3" }, +{ "rotrdi", 3, PPC64, "rldicl %0,%1,(-(%2)!63)&((%2)|63),0" }, +{ "rotrdi.", 3, PPC64, "rldicl. %0,%1,(-(%2)!63)&((%2)|63),0" }, +{ "sldi", 3, PPC64, "rldicr %0,%1,%2,63-(%2)" }, +{ "sldi.", 3, PPC64, "rldicr. %0,%1,%2,63-(%2)" }, +{ "srdi", 3, PPC64, "rldicl %0,%1,(-(%2)!63)&((%2)|63),%2" }, +{ "srdi.", 3, PPC64, "rldicl. %0,%1,(-(%2)!63)&((%2)|63),%2" }, +{ "clrrdi", 3, PPC64, "rldicr %0,%1,0,63-(%2)" }, +{ "clrrdi.", 3, PPC64, "rldicr. %0,%1,0,63-(%2)" }, +{ "clrlsldi",4, PPC64, "rldic %0,%1,%3,(%2)-(%3)" }, +{ "clrlsldi.",4, PPC64, "rldic. %0,%1,%3,(%2)-(%3)" }, + +{ "extlwi", 4, PPCCOM, "rlwinm %0,%1,%3,0,(%2)-1" }, +{ "extlwi.", 4, PPCCOM, "rlwinm. %0,%1,%3,0,(%2)-1" }, +{ "extrwi", 4, PPCCOM, "rlwinm %0,%1,((%2)+(%3))&((%2)+(%3)<>32),32-(%2),31" }, +{ "extrwi.", 4, PPCCOM, "rlwinm. %0,%1,((%2)+(%3))&((%2)+(%3)<>32),32-(%2),31" }, +{ "inslwi", 4, PPCCOM, "rlwimi %0,%1,(-(%3)!31)&((%3)|31),%3,(%2)+(%3)-1" }, +{ "inslwi.", 4, PPCCOM, "rlwimi. %0,%1,(-(%3)!31)&((%3)|31),%3,(%2)+(%3)-1"}, +{ "insrwi", 4, PPCCOM, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" }, +{ "insrwi.", 4, PPCCOM, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"}, +{ "rotrwi", 3, PPCCOM, "rlwinm %0,%1,(-(%2)!31)&((%2)|31),0,31" }, +{ "rotrwi.", 3, PPCCOM, "rlwinm. %0,%1,(-(%2)!31)&((%2)|31),0,31" }, +{ "slwi", 3, PPCCOM, "rlwinm %0,%1,%2,0,31-(%2)" }, +{ "sli", 3, PWRCOM, "rlinm %0,%1,%2,0,31-(%2)" }, +{ "slwi.", 3, PPCCOM, "rlwinm. %0,%1,%2,0,31-(%2)" }, +{ "sli.", 3, PWRCOM, "rlinm. %0,%1,%2,0,31-(%2)" }, +{ "srwi", 3, PPCCOM, "rlwinm %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "sri", 3, PWRCOM, "rlinm %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "srwi.", 3, PPCCOM, "rlwinm. %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "sri.", 3, PWRCOM, "rlinm. %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "clrrwi", 3, PPCCOM, "rlwinm %0,%1,0,0,31-(%2)" }, +{ "clrrwi.", 3, PPCCOM, "rlwinm. %0,%1,0,0,31-(%2)" }, +{ "clrlslwi",4, PPCCOM, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" }, +{ "clrlslwi.",4, PPCCOM, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" }, +}; + +const int powerpc_num_macros = + sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); + + +/* This file provides several disassembler functions, all of which use + the disassembler interface defined in dis-asm.h. Several functions + are provided because this file handles disassembly for the PowerPC + in both big and little endian mode and also for the POWER (RS/6000) + chip. */ + +static int print_insn_powerpc (bfd_vma, struct disassemble_info *, int, int); + +/* Determine which set of machines to disassemble for. PPC403/601 or + BookE. For convenience, also disassemble instructions supported + by the AltiVec vector unit. */ + +static int +powerpc_dialect (struct disassemble_info *info) +{ + int dialect = PPC_OPCODE_PPC; + + if (BFD_DEFAULT_TARGET_SIZE == 64) + dialect |= PPC_OPCODE_64; + + if (info->disassembler_options + && strstr (info->disassembler_options, "booke") != NULL) + dialect |= PPC_OPCODE_BOOKE | PPC_OPCODE_BOOKE64; + else if ((info->mach == bfd_mach_ppc_e500) + || (info->disassembler_options + && strstr (info->disassembler_options, "e500") != NULL)) + dialect |= (PPC_OPCODE_BOOKE + | PPC_OPCODE_SPE | PPC_OPCODE_ISEL + | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK + | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK + | PPC_OPCODE_RFMCI); + else if (info->disassembler_options + && strstr (info->disassembler_options, "efs") != NULL) + dialect |= PPC_OPCODE_EFS; + else if (info->disassembler_options + && strstr (info->disassembler_options, "e300") != NULL) + dialect |= PPC_OPCODE_E300 | PPC_OPCODE_CLASSIC | PPC_OPCODE_COMMON; + else if (info->disassembler_options + && strstr (info->disassembler_options, "440") != NULL) + dialect |= PPC_OPCODE_BOOKE | PPC_OPCODE_32 + | PPC_OPCODE_440 | PPC_OPCODE_ISEL | PPC_OPCODE_RFMCI; + else + dialect |= (PPC_OPCODE_403 | PPC_OPCODE_601 | PPC_OPCODE_CLASSIC + | PPC_OPCODE_COMMON | PPC_OPCODE_ALTIVEC); + + if (info->disassembler_options + && strstr (info->disassembler_options, "power4") != NULL) + dialect |= PPC_OPCODE_POWER4; + + if (info->disassembler_options + && strstr (info->disassembler_options, "power5") != NULL) + dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5; + + if (info->disassembler_options + && strstr (info->disassembler_options, "cell") != NULL) + dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC; + + if (info->disassembler_options + && strstr (info->disassembler_options, "power6") != NULL) + dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_ALTIVEC; + + if (info->disassembler_options + && strstr (info->disassembler_options, "any") != NULL) + dialect |= PPC_OPCODE_ANY; + + if (info->disassembler_options) + { + if (strstr (info->disassembler_options, "32") != NULL) + dialect &= ~PPC_OPCODE_64; + else if (strstr (info->disassembler_options, "64") != NULL) + dialect |= PPC_OPCODE_64; + } + + info->private_data = (char *) 0 + dialect; + return dialect; +} + +/* QEMU default */ +int +print_insn_ppc (bfd_vma memaddr, struct disassemble_info *info) +{ + int dialect = (char *) info->private_data - (char *) 0; + return print_insn_powerpc (memaddr, info, info->endian == BFD_ENDIAN_BIG, + dialect); +} + +/* Print a big endian PowerPC instruction. */ + +int +print_insn_big_powerpc (bfd_vma memaddr, struct disassemble_info *info) +{ + int dialect = (char *) info->private_data - (char *) 0; + return print_insn_powerpc (memaddr, info, 1, dialect); +} + +/* Print a little endian PowerPC instruction. */ + +int +print_insn_little_powerpc (bfd_vma memaddr, struct disassemble_info *info) +{ + int dialect = (char *) info->private_data - (char *) 0; + return print_insn_powerpc (memaddr, info, 0, dialect); +} + +/* Print a POWER (RS/6000) instruction. */ + +int +print_insn_rs6000 (bfd_vma memaddr, struct disassemble_info *info) +{ + return print_insn_powerpc (memaddr, info, 1, PPC_OPCODE_POWER); +} + +/* Extract the operand value from the PowerPC or POWER instruction. */ + +static long +operand_value_powerpc (const struct powerpc_operand *operand, + unsigned long insn, int dialect) +{ + long value; + int invalid; + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, dialect, &invalid); + else + { + value = (insn >> operand->shift) & operand->bitm; + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + { + /* BITM is always some number of zeros followed by some + number of ones, followed by some numer of zeros. */ + unsigned long top = operand->bitm; + /* top & -top gives the rightmost 1 bit, so this + fills in any trailing zeros. */ + top |= (top & -top) - 1; + top &= ~(top >> 1); + value = (value ^ top) - top; + } + } + + return value; +} + +/* Determine whether the optional operand(s) should be printed. */ + +static int +skip_optional_operands (const unsigned char *opindex, + unsigned long insn, int dialect) +{ + const struct powerpc_operand *operand; + + for (; *opindex != 0; opindex++) + { + operand = &powerpc_operands[*opindex]; + if ((operand->flags & PPC_OPERAND_NEXT) != 0 + || ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && operand_value_powerpc (operand, insn, dialect) != 0)) + return 0; + } + + return 1; +} + +/* Print a PowerPC or POWER instruction. */ + +static int +print_insn_powerpc (bfd_vma memaddr, + struct disassemble_info *info, + int bigendian, + int dialect) +{ + bfd_byte buffer[4]; + int status; + unsigned long insn; + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; + + if (dialect == 0) + dialect = powerpc_dialect (info); + + status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + if (bigendian) + insn = bfd_getb32 (buffer); + else + insn = bfd_getl32 (buffer); + + /* Get the major opcode of the instruction. */ + op = PPC_OP (insn); + + /* Find the first match in the opcode table. We could speed this up + a bit by doing a binary search on the major opcode. */ + opcode_end = powerpc_opcodes + powerpc_num_opcodes; + again: + for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + { + unsigned long table_op; + const unsigned char *opindex; + const struct powerpc_operand *operand; + int invalid; + int need_comma; + int need_paren; + int skip_optional; + + table_op = PPC_OP (opcode->opcode); + if (op < table_op) + break; + if (op > table_op) + continue; + + if ((insn & opcode->mask) != opcode->opcode + || (opcode->flags & dialect) == 0) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + operand = powerpc_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, dialect, &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + if (opcode->operands[0] != 0) + (*info->fprintf_func) (info->stream, "%-7s ", opcode->name); + else + (*info->fprintf_func) (info->stream, "%s", opcode->name); + + /* Now extract and print the operands. */ + need_comma = 0; + need_paren = 0; + skip_optional = -1; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + long value; + + operand = powerpc_operands + *opindex; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & PPC_OPERAND_FAKE) != 0) + continue; + + /* If all of the optional operands have the value zero, + then don't print any of them. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0) + { + if (skip_optional < 0) + skip_optional = skip_optional_operands (opindex, insn, + dialect); + if (skip_optional) + continue; + } + + value = operand_value_powerpc (operand, insn, dialect); + + if (need_comma) + { + (*info->fprintf_func) (info->stream, ","); + need_comma = 0; + } + + /* Print the operand as directed by the flags. */ + if ((operand->flags & PPC_OPERAND_GPR) != 0 + || ((operand->flags & PPC_OPERAND_GPR_0) != 0 && value != 0)) + (*info->fprintf_func) (info->stream, "r%ld", value); + else if ((operand->flags & PPC_OPERAND_FPR) != 0) + (*info->fprintf_func) (info->stream, "f%ld", value); + else if ((operand->flags & PPC_OPERAND_VR) != 0) + (*info->fprintf_func) (info->stream, "v%ld", value); + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) + (*info->print_address_func) (memaddr + value, info); + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info); + else if ((operand->flags & PPC_OPERAND_CR) == 0 + || (dialect & PPC_OPCODE_PPC) == 0) + (*info->fprintf_func) (info->stream, "%ld", value); + else + { + if (operand->bitm == 7) + (*info->fprintf_func) (info->stream, "cr%ld", value); + else + { + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + (*info->fprintf_func) (info->stream, "4*cr%d+", cr); + cc = value & 3; + (*info->fprintf_func) (info->stream, "%s", cbnames[cc]); + } + } + + if (need_paren) + { + (*info->fprintf_func) (info->stream, ")"); + need_paren = 0; + } + + if ((operand->flags & PPC_OPERAND_PARENS) == 0) + need_comma = 1; + else + { + (*info->fprintf_func) (info->stream, "("); + need_paren = 1; + } + } + + /* We have found and printed an instruction; return. */ + return 4; + } + + if ((dialect & PPC_OPCODE_ANY) != 0) + { + dialect = ~PPC_OPCODE_ANY; + goto again; + } + + /* We could not find a match. */ + (*info->fprintf_func) (info->stream, ".long 0x%lx", insn); + + return 4; +} diff --git a/disas/s390.c b/disas/s390.c new file mode 100644 index 0000000..974460c --- /dev/null +++ b/disas/s390.c @@ -0,0 +1,1896 @@ +/* opcodes/s390-dis.c revision 1.12 */ +/* s390-dis.c -- Disassemble S390 instructions + Copyright 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). + + This file is part of GDB, GAS and the GNU binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "qemu-common.h" +#include "disas/bfd.h" + +/* include/opcode/s390.h revision 1.9 */ +/* s390.h -- Header file for S390 opcode table + Copyright 2000, 2001, 2003 Free Software Foundation, Inc. + Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). + + This file is part of BFD, the Binary File Descriptor library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef S390_H +#define S390_H + +/* List of instruction sets variations. */ + +enum s390_opcode_mode_val + { + S390_OPCODE_ESA = 0, + S390_OPCODE_ZARCH + }; + +enum s390_opcode_cpu_val + { + S390_OPCODE_G5 = 0, + S390_OPCODE_G6, + S390_OPCODE_Z900, + S390_OPCODE_Z990, + S390_OPCODE_Z9_109, + S390_OPCODE_Z9_EC, + S390_OPCODE_Z10 + }; + +/* The opcode table is an array of struct s390_opcode. */ + +struct s390_opcode + { + /* The opcode name. */ + const char * name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned char opcode[6]; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned char mask[6]; + + /* The opcode length in bytes. */ + int oplen; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[6]; + + /* Bitmask of execution modes this opcode is available for. */ + unsigned int modes; + + /* First cpu this opcode is available for. */ + enum s390_opcode_cpu_val min_cpu; + }; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +/* QEMU: Mark these static. */ +static const struct s390_opcode s390_opcodes[]; +static const int s390_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* The operands table is an array of struct s390_operand. */ + +struct s390_operand + { + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + + /* One bit syntax flags. */ + unsigned long flags; + }; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +static const struct s390_operand s390_operands[]; + +/* Values defined for the flags field of a struct s390_operand. */ + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define S390_OPERAND_GPR 0x1 + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define S390_OPERAND_FPR 0x2 + +/* This operand names an access register. The disassembler + prints these with a leading 'a'. */ +#define S390_OPERAND_AR 0x4 + +/* This operand names a control register. The disassembler + prints these with a leading 'c'. */ +#define S390_OPERAND_CR 0x8 + +/* This operand is a displacement. */ +#define S390_OPERAND_DISP 0x10 + +/* This operand names a base register. */ +#define S390_OPERAND_BASE 0x20 + +/* This operand names an index register, it can be skipped. */ +#define S390_OPERAND_INDEX 0x40 + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define S390_OPERAND_PCREL 0x80 + +/* This operand takes signed values. */ +#define S390_OPERAND_SIGNED 0x100 + +/* This operand is a length. */ +#define S390_OPERAND_LENGTH 0x200 + +/* This operand is optional. Only a single operand at the end of + the instruction may be optional. */ +#define S390_OPERAND_OPTIONAL 0x400 + +/* QEMU-ADD */ +/* ??? Not quite the format the assembler takes, but easy to implement + without recourse to the table generator. */ +#define S390_OPERAND_CCODE 0x800 + +static const char s390_ccode_name[16][4] = { + "n", /* 0000 */ + "o", /* 0001 */ + "h", /* 0010 */ + "nle", /* 0011 */ + "l", /* 0100 */ + "nhe", /* 0101 */ + "lh", /* 0110 */ + "ne", /* 0111 */ + "e", /* 1000 */ + "nlh", /* 1001 */ + "he", /* 1010 */ + "nl", /* 1011 */ + "le", /* 1100 */ + "nh", /* 1101 */ + "no", /* 1110 */ + "a" /* 1111 */ +}; +/* QEMU-END */ + +#endif /* S390_H */ + +static int init_flag = 0; +static int opc_index[256]; + +/* QEMU: We've disabled the architecture check below. */ +/* static int current_arch_mask = 0; */ + +/* Set up index table for first opcode byte. */ + +static void +init_disasm (struct disassemble_info *info) +{ + const struct s390_opcode *opcode; + const struct s390_opcode *opcode_end; + + memset (opc_index, 0, sizeof (opc_index)); + opcode_end = s390_opcodes + s390_num_opcodes; + for (opcode = s390_opcodes; opcode < opcode_end; opcode++) + { + opc_index[(int) opcode->opcode[0]] = opcode - s390_opcodes; + while ((opcode < opcode_end) && + (opcode[1].opcode[0] == opcode->opcode[0])) + opcode++; + } + +#ifdef QEMU_DISABLE + switch (info->mach) + { + case bfd_mach_s390_31: + current_arch_mask = 1 << S390_OPCODE_ESA; + break; + case bfd_mach_s390_64: + current_arch_mask = 1 << S390_OPCODE_ZARCH; + break; + default: + abort (); + } +#endif /* QEMU_DISABLE */ + + init_flag = 1; +} + +/* Extracts an operand value from an instruction. */ + +static inline unsigned int +s390_extract_operand (unsigned char *insn, const struct s390_operand *operand) +{ + unsigned int val; + int bits; + + /* Extract fragments of the operand byte for byte. */ + insn += operand->shift / 8; + bits = (operand->shift & 7) + operand->bits; + val = 0; + do + { + val <<= 8; + val |= (unsigned int) *insn++; + bits -= 8; + } + while (bits > 0); + val >>= -bits; + val &= ((1U << (operand->bits - 1)) << 1) - 1; + + /* Check for special long displacement case. */ + if (operand->bits == 20 && operand->shift == 20) + val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; + + /* Sign extend value if the operand is signed or pc relative. */ + if ((operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL)) + && (val & (1U << (operand->bits - 1)))) + val |= (-1U << (operand->bits - 1)) << 1; + + /* Double value if the operand is pc relative. */ + if (operand->flags & S390_OPERAND_PCREL) + val <<= 1; + + /* Length x in an instructions has real length x + 1. */ + if (operand->flags & S390_OPERAND_LENGTH) + val++; + return val; +} + +/* Print a S390 instruction. */ + +int +print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info) +{ + bfd_byte buffer[6]; + const struct s390_opcode *opcode; + const struct s390_opcode *opcode_end; + unsigned int value; + int status, opsize, bufsize; + char separator; + + if (init_flag == 0) + init_disasm (info); + + /* The output looks better if we put 6 bytes on a line. */ + info->bytes_per_line = 6; + + /* Every S390 instruction is max 6 bytes long. */ + memset (buffer, 0, 6); + status = (*info->read_memory_func) (memaddr, buffer, 6, info); + if (status != 0) + { + for (bufsize = 0; bufsize < 6; bufsize++) + if ((*info->read_memory_func) (memaddr, buffer, bufsize + 1, info) != 0) + break; + if (bufsize <= 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + /* Opsize calculation looks strange but it works + 00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes, + 11xxxxxx -> 6 bytes. */ + opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1; + status = opsize > bufsize; + } + else + { + bufsize = 6; + opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1; + } + + if (status == 0) + { + /* Find the first match in the opcode table. */ + opcode_end = s390_opcodes + s390_num_opcodes; + for (opcode = s390_opcodes + opc_index[(int) buffer[0]]; + (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]); + opcode++) + { + const struct s390_operand *operand; + const unsigned char *opindex; + +#ifdef QEMU_DISABLE + /* Check architecture. */ + if (!(opcode->modes & current_arch_mask)) + continue; +#endif /* QEMU_DISABLE */ + + /* Check signature of the opcode. */ + if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1] + || (buffer[2] & opcode->mask[2]) != opcode->opcode[2] + || (buffer[3] & opcode->mask[3]) != opcode->opcode[3] + || (buffer[4] & opcode->mask[4]) != opcode->opcode[4] + || (buffer[5] & opcode->mask[5]) != opcode->opcode[5]) + continue; + + /* The instruction is valid. */ +/* QEMU-MOD */ + (*info->fprintf_func) (info->stream, "%s", opcode->name); + + if (s390_operands[opcode->operands[0]].flags & S390_OPERAND_CCODE) + separator = 0; + else + separator = '\t'; +/* QEMU-END */ + + /* Extract the operands. */ + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + unsigned int value; + + operand = s390_operands + *opindex; + value = s390_extract_operand (buffer, operand); + + if ((operand->flags & S390_OPERAND_INDEX) && value == 0) + continue; + if ((operand->flags & S390_OPERAND_BASE) && + value == 0 && separator == '(') + { + separator = ','; + continue; + } + + if (separator) + (*info->fprintf_func) (info->stream, "%c", separator); + + if (operand->flags & S390_OPERAND_GPR) + (*info->fprintf_func) (info->stream, "%%r%i", value); + else if (operand->flags & S390_OPERAND_FPR) + (*info->fprintf_func) (info->stream, "%%f%i", value); + else if (operand->flags & S390_OPERAND_AR) + (*info->fprintf_func) (info->stream, "%%a%i", value); + else if (operand->flags & S390_OPERAND_CR) + (*info->fprintf_func) (info->stream, "%%c%i", value); + else if (operand->flags & S390_OPERAND_PCREL) + (*info->print_address_func) (memaddr + (int) value, info); + else if (operand->flags & S390_OPERAND_SIGNED) + (*info->fprintf_func) (info->stream, "%i", (int) value); +/* QEMU-ADD */ + else if (operand->flags & S390_OPERAND_CCODE) + { + (*info->fprintf_func) (info->stream, "%s", + s390_ccode_name[(int) value]); + separator = '\t'; + continue; + } +/* QEMU-END */ + else + (*info->fprintf_func) (info->stream, "%u", value); + + if (operand->flags & S390_OPERAND_DISP) + { + separator = '('; + } + else if (operand->flags & S390_OPERAND_BASE) + { + (*info->fprintf_func) (info->stream, ")"); + separator = ','; + } + else + separator = ','; + } + + /* Found instruction, printed it, return its size. */ + return opsize; + } + /* No matching instruction found, fall through to hex print. */ + } + + if (bufsize >= 4) + { + value = (unsigned int) buffer[0]; + value = (value << 8) + (unsigned int) buffer[1]; + value = (value << 8) + (unsigned int) buffer[2]; + value = (value << 8) + (unsigned int) buffer[3]; + (*info->fprintf_func) (info->stream, ".long\t0x%08x", value); + return 4; + } + else if (bufsize >= 2) + { + value = (unsigned int) buffer[0]; + value = (value << 8) + (unsigned int) buffer[1]; + (*info->fprintf_func) (info->stream, ".short\t0x%04x", value); + return 2; + } + else + { + value = (unsigned int) buffer[0]; + (*info->fprintf_func) (info->stream, ".byte\t0x%02x", value); + return 1; + } +} + +/* opcodes/s390-opc.c revision 1.16 */ +/* s390-opc.c -- S390 opcode list + Copyright 2000, 2001, 2003 Free Software Foundation, Inc. + Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). + + This file is part of GDB, GAS, and the GNU binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* This file holds the S390 opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* The operands table. + The fields are bits, shift, insert, extract, flags. */ + +static const struct s390_operand s390_operands[] = +{ +#define UNUSED 0 + { 0, 0, 0 }, /* Indicates the end of the operand list */ + +#define R_8 1 /* GPR starting at position 8 */ + { 4, 8, S390_OPERAND_GPR }, +#define R_12 2 /* GPR starting at position 12 */ + { 4, 12, S390_OPERAND_GPR }, +#define R_16 3 /* GPR starting at position 16 */ + { 4, 16, S390_OPERAND_GPR }, +#define R_20 4 /* GPR starting at position 20 */ + { 4, 20, S390_OPERAND_GPR }, +#define R_24 5 /* GPR starting at position 24 */ + { 4, 24, S390_OPERAND_GPR }, +#define R_28 6 /* GPR starting at position 28 */ + { 4, 28, S390_OPERAND_GPR }, +#define R_32 7 /* GPR starting at position 32 */ + { 4, 32, S390_OPERAND_GPR }, + +#define F_8 8 /* FPR starting at position 8 */ + { 4, 8, S390_OPERAND_FPR }, +#define F_12 9 /* FPR starting at position 12 */ + { 4, 12, S390_OPERAND_FPR }, +#define F_16 10 /* FPR starting at position 16 */ + { 4, 16, S390_OPERAND_FPR }, +#define F_20 11 /* FPR starting at position 16 */ + { 4, 16, S390_OPERAND_FPR }, +#define F_24 12 /* FPR starting at position 24 */ + { 4, 24, S390_OPERAND_FPR }, +#define F_28 13 /* FPR starting at position 28 */ + { 4, 28, S390_OPERAND_FPR }, +#define F_32 14 /* FPR starting at position 32 */ + { 4, 32, S390_OPERAND_FPR }, + +#define A_8 15 /* Access reg. starting at position 8 */ + { 4, 8, S390_OPERAND_AR }, +#define A_12 16 /* Access reg. starting at position 12 */ + { 4, 12, S390_OPERAND_AR }, +#define A_24 17 /* Access reg. starting at position 24 */ + { 4, 24, S390_OPERAND_AR }, +#define A_28 18 /* Access reg. starting at position 28 */ + { 4, 28, S390_OPERAND_AR }, + +#define C_8 19 /* Control reg. starting at position 8 */ + { 4, 8, S390_OPERAND_CR }, +#define C_12 20 /* Control reg. starting at position 12 */ + { 4, 12, S390_OPERAND_CR }, + +#define B_16 21 /* Base register starting at position 16 */ + { 4, 16, S390_OPERAND_BASE|S390_OPERAND_GPR }, +#define B_32 22 /* Base register starting at position 32 */ + { 4, 32, S390_OPERAND_BASE|S390_OPERAND_GPR }, + +#define X_12 23 /* Index register starting at position 12 */ + { 4, 12, S390_OPERAND_INDEX|S390_OPERAND_GPR }, + +#define D_20 24 /* Displacement starting at position 20 */ + { 12, 20, S390_OPERAND_DISP }, +#define D_36 25 /* Displacement starting at position 36 */ + { 12, 36, S390_OPERAND_DISP }, +#define D20_20 26 /* 20 bit displacement starting at 20 */ + { 20, 20, S390_OPERAND_DISP|S390_OPERAND_SIGNED }, + +#define L4_8 27 /* 4 bit length starting at position 8 */ + { 4, 8, S390_OPERAND_LENGTH }, +#define L4_12 28 /* 4 bit length starting at position 12 */ + { 4, 12, S390_OPERAND_LENGTH }, +#define L8_8 29 /* 8 bit length starting at position 8 */ + { 8, 8, S390_OPERAND_LENGTH }, + +#define U4_8 30 /* 4 bit unsigned value starting at 8 */ + { 4, 8, 0 }, +#define U4_12 31 /* 4 bit unsigned value starting at 12 */ + { 4, 12, 0 }, +#define U4_16 32 /* 4 bit unsigned value starting at 16 */ + { 4, 16, 0 }, +#define U4_20 33 /* 4 bit unsigned value starting at 20 */ + { 4, 20, 0 }, +#define U8_8 34 /* 8 bit unsigned value starting at 8 */ + { 8, 8, 0 }, +#define U8_16 35 /* 8 bit unsigned value starting at 16 */ + { 8, 16, 0 }, +#define I16_16 36 /* 16 bit signed value starting at 16 */ + { 16, 16, S390_OPERAND_SIGNED }, +#define U16_16 37 /* 16 bit unsigned value starting at 16 */ + { 16, 16, 0 }, +#define J16_16 38 /* PC relative jump offset at 16 */ + { 16, 16, S390_OPERAND_PCREL }, +#define J32_16 39 /* PC relative long offset at 16 */ + { 32, 16, S390_OPERAND_PCREL }, +#define I32_16 40 /* 32 bit signed value starting at 16 */ + { 32, 16, S390_OPERAND_SIGNED }, +#define U32_16 41 /* 32 bit unsigned value starting at 16 */ + { 32, 16, 0 }, +#define M_16 42 /* 4 bit optional mask starting at 16 */ + { 4, 16, S390_OPERAND_OPTIONAL }, +#define RO_28 43 /* optional GPR starting at position 28 */ + { 4, 28, (S390_OPERAND_GPR | S390_OPERAND_OPTIONAL) }, + +/* QEMU-ADD: */ +#define M4_12 44 /* 4-bit condition-code starting at 12 */ + { 4, 12, S390_OPERAND_CCODE }, +#define M4_32 45 /* 4-bit condition-code starting at 32 */ + { 4, 32, S390_OPERAND_CCODE }, +#define I8_32 46 /* 8 bit signed value starting at 32 */ + { 8, 32, S390_OPERAND_SIGNED }, +#define U8_24 47 /* 8 bit unsigned value starting at 24 */ + { 8, 24, 0 }, +#define U8_32 48 /* 8 bit unsigned value starting at 32 */ + { 8, 32, 0 }, +#define I16_32 49 + { 16, 32, S390_OPERAND_SIGNED }, +#define M4_16 50 /* 4-bit condition-code starting at 12 */ + { 4, 16, S390_OPERAND_CCODE }, +#define I8_16 51 + { 8, 16, S390_OPERAND_SIGNED }, +/* QEMU-END */ +}; + + +/* Macros used to form opcodes. */ + +/* 8/16/48 bit opcodes. */ +#define OP8(x) { x, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define OP16(x) { x >> 8, x & 255, 0x00, 0x00, 0x00, 0x00 } +#define OP48(x) { x >> 40, (x >> 32) & 255, (x >> 24) & 255, \ + (x >> 16) & 255, (x >> 8) & 255, x & 255} + +/* The new format of the INSTR_x_y and MASK_x_y defines is based + on the following rules: + 1) the middle part of the definition (x in INSTR_x_y) is the official + names of the instruction format that you can find in the principals + of operation. + 2) the last part of the definition (y in INSTR_x_y) gives you an idea + which operands the binary represenation of the instruction has. + The meanings of the letters in y are: + a - access register + c - control register + d - displacement, 12 bit + f - floating pointer register + i - signed integer, 4, 8, 16 or 32 bit + l - length, 4 or 8 bit + p - pc relative + r - general purpose register + u - unsigned integer, 4, 8, 16 or 32 bit + m - mode field, 4 bit + 0 - operand skipped. + The order of the letters reflects the layout of the format in + storage and not the order of the paramaters of the instructions. + The use of the letters is not a 100% match with the PoP but it is + quite close. + + For example the instruction "mvo" is defined in the PoP as follows: + + MVO D1(L1,B1),D2(L2,B2) [SS] + + -------------------------------------- + | 'F1' | L1 | L2 | B1 | D1 | B2 | D2 | + -------------------------------------- + 0 8 12 16 20 32 36 + + The instruction format is: INSTR_SS_LLRDRD / MASK_SS_LLRDRD. */ + +#define INSTR_E 2, { 0,0,0,0,0,0 } /* e.g. pr */ +#define INSTR_RIE_RRP 6, { R_8,R_12,J16_16,0,0,0 } /* e.g. brxhg */ +#define INSTR_RIL_0P 6, { J32_16,0,0,0,0 } /* e.g. jg */ +#define INSTR_RIL_RP 6, { R_8,J32_16,0,0,0,0 } /* e.g. brasl */ +#define INSTR_RIL_UP 6, { U4_8,J32_16,0,0,0,0 } /* e.g. brcl */ +#define INSTR_RIL_RI 6, { R_8,I32_16,0,0,0,0 } /* e.g. afi */ +#define INSTR_RIL_RU 6, { R_8,U32_16,0,0,0,0 } /* e.g. alfi */ +#define INSTR_RI_0P 4, { J16_16,0,0,0,0,0 } /* e.g. j */ +#define INSTR_RI_RI 4, { R_8,I16_16,0,0,0,0 } /* e.g. ahi */ +#define INSTR_RI_RP 4, { R_8,J16_16,0,0,0,0 } /* e.g. brct */ +#define INSTR_RI_RU 4, { R_8,U16_16,0,0,0,0 } /* e.g. tml */ +#define INSTR_RI_UP 4, { U4_8,J16_16,0,0,0,0 } /* e.g. brc */ +#define INSTR_RRE_00 4, { 0,0,0,0,0,0 } /* e.g. palb */ +#define INSTR_RRE_0R 4, { R_28,0,0,0,0,0 } /* e.g. tb */ +#define INSTR_RRE_AA 4, { A_24,A_28,0,0,0,0 } /* e.g. cpya */ +#define INSTR_RRE_AR 4, { A_24,R_28,0,0,0,0 } /* e.g. sar */ +#define INSTR_RRE_F0 4, { F_24,0,0,0,0,0 } /* e.g. sqer */ +#define INSTR_RRE_FF 4, { F_24,F_28,0,0,0,0 } /* e.g. debr */ +#define INSTR_RRE_R0 4, { R_24,0,0,0,0,0 } /* e.g. ipm */ +#define INSTR_RRE_RA 4, { R_24,A_28,0,0,0,0 } /* e.g. ear */ +#define INSTR_RRE_RF 4, { R_24,F_28,0,0,0,0 } /* e.g. cefbr */ +#define INSTR_RRE_RR 4, { R_24,R_28,0,0,0,0 } /* e.g. lura */ +#define INSTR_RRE_FR 4, { F_24,R_28,0,0,0,0 } /* e.g. ldgr */ +/* Actually efpc and sfpc do not take an optional operand. + This is just a workaround for existing code e.g. glibc. */ +#define INSTR_RRE_RR_OPT 4, { R_24,RO_28,0,0,0,0 } /* efpc, sfpc */ +#define INSTR_RRF_F0FF 4, { F_16,F_24,F_28,0,0,0 } /* e.g. madbr */ +/* QEMU-MOD */ +#define INSTR_RRF_F0FF2 4, { F_24,F_28,F_16,0,0,0 } /* e.g. cpsdr */ +/* QEMU-END */ +#define INSTR_RRF_F0FR 4, { F_24,F_16,R_28,0,0,0 } /* e.g. iedtr */ +#define INSTR_RRF_FUFF 4, { F_24,F_16,F_28,U4_20,0,0 } /* e.g. didbr */ +#define INSTR_RRF_RURR 4, { R_24,R_28,R_16,U4_20,0,0 } /* e.g. .insn */ +#define INSTR_RRF_R0RR 4, { R_24,R_28,R_16,0,0,0 } /* e.g. idte */ +#define INSTR_RRF_U0FF 4, { F_24,U4_16,F_28,0,0,0 } /* e.g. fixr */ +#define INSTR_RRF_U0RF 4, { R_24,U4_16,F_28,0,0,0 } /* e.g. cfebr */ +#define INSTR_RRF_UUFF 4, { F_24,U4_16,F_28,U4_20,0,0 } /* e.g. fidtr */ +#define INSTR_RRF_0UFF 4, { F_24,F_28,U4_20,0,0,0 } /* e.g. ldetr */ +#define INSTR_RRF_FFFU 4, { F_24,F_16,F_28,U4_20,0,0 } /* e.g. qadtr */ +#define INSTR_RRF_M0RR 4, { R_24,R_28,M_16,0,0,0 } /* e.g. sske */ +#define INSTR_RR_0R 2, { R_12, 0,0,0,0,0 } /* e.g. br */ +#define INSTR_RR_FF 2, { F_8,F_12,0,0,0,0 } /* e.g. adr */ +#define INSTR_RR_R0 2, { R_8, 0,0,0,0,0 } /* e.g. spm */ +#define INSTR_RR_RR 2, { R_8,R_12,0,0,0,0 } /* e.g. lr */ +#define INSTR_RR_U0 2, { U8_8, 0,0,0,0,0 } /* e.g. svc */ +#define INSTR_RR_UR 2, { U4_8,R_12,0,0,0,0 } /* e.g. bcr */ +#define INSTR_RRR_F0FF 4, { F_24,F_28,F_16,0,0,0 } /* e.g. ddtr */ +#define INSTR_RSE_RRRD 6, { R_8,R_12,D_20,B_16,0,0 } /* e.g. lmh */ +#define INSTR_RSE_CCRD 6, { C_8,C_12,D_20,B_16,0,0 } /* e.g. lmh */ +#define INSTR_RSE_RURD 6, { R_8,U4_12,D_20,B_16,0,0 } /* e.g. icmh */ +#define INSTR_RSL_R0RD 6, { R_8,D_20,B_16,0,0,0 } /* e.g. tp */ +#define INSTR_RSI_RRP 4, { R_8,R_12,J16_16,0,0,0 } /* e.g. brxh */ +#define INSTR_RSY_RRRD 6, { R_8,R_12,D20_20,B_16,0,0 } /* e.g. stmy */ +#define INSTR_RSY_RURD 6, { R_8,U4_12,D20_20,B_16,0,0 } /* e.g. icmh */ +#define INSTR_RSY_AARD 6, { A_8,A_12,D20_20,B_16,0,0 } /* e.g. lamy */ +#define INSTR_RSY_CCRD 6, { C_8,C_12,D20_20,B_16,0,0 } /* e.g. lamy */ +#define INSTR_RS_AARD 4, { A_8,A_12,D_20,B_16,0,0 } /* e.g. lam */ +#define INSTR_RS_CCRD 4, { C_8,C_12,D_20,B_16,0,0 } /* e.g. lctl */ +#define INSTR_RS_R0RD 4, { R_8,D_20,B_16,0,0,0 } /* e.g. sll */ +#define INSTR_RS_RRRD 4, { R_8,R_12,D_20,B_16,0,0 } /* e.g. cs */ +#define INSTR_RS_RURD 4, { R_8,U4_12,D_20,B_16,0,0 } /* e.g. icm */ +#define INSTR_RXE_FRRD 6, { F_8,D_20,X_12,B_16,0,0 } /* e.g. axbr */ +#define INSTR_RXE_RRRD 6, { R_8,D_20,X_12,B_16,0,0 } /* e.g. lg */ +#define INSTR_RXF_FRRDF 6, { F_32,F_8,D_20,X_12,B_16,0 } /* e.g. madb */ +#define INSTR_RXF_RRRDR 6, { R_32,R_8,D_20,X_12,B_16,0 } /* e.g. .insn */ +#define INSTR_RXY_RRRD 6, { R_8,D20_20,X_12,B_16,0,0 } /* e.g. ly */ +#define INSTR_RXY_FRRD 6, { F_8,D20_20,X_12,B_16,0,0 } /* e.g. ley */ +#define INSTR_RX_0RRD 4, { D_20,X_12,B_16,0,0,0 } /* e.g. be */ +#define INSTR_RX_FRRD 4, { F_8,D_20,X_12,B_16,0,0 } /* e.g. ae */ +#define INSTR_RX_RRRD 4, { R_8,D_20,X_12,B_16,0,0 } /* e.g. l */ +#define INSTR_RX_URRD 4, { U4_8,D_20,X_12,B_16,0,0 } /* e.g. bc */ +#define INSTR_SI_URD 4, { D_20,B_16,U8_8,0,0,0 } /* e.g. cli */ +#define INSTR_SIY_URD 6, { D20_20,B_16,U8_8,0,0,0 } /* e.g. tmy */ +#define INSTR_SSE_RDRD 6, { D_20,B_16,D_36,B_32,0,0 } /* e.g. mvsdk */ +#define INSTR_SS_L0RDRD 6, { D_20,L8_8,B_16,D_36,B_32,0 } /* e.g. mvc */ +#define INSTR_SS_L2RDRD 6, { D_20,B_16,D_36,L8_8,B_32,0 } /* e.g. pka */ +#define INSTR_SS_LIRDRD 6, { D_20,L4_8,B_16,D_36,B_32,U4_12 } /* e.g. srp */ +#define INSTR_SS_LLRDRD 6, { D_20,L4_8,B_16,D_36,L4_12,B_32 } /* e.g. pack */ +#define INSTR_SS_RRRDRD 6, { D_20,R_8,B_16,D_36,B_32,R_12 } /* e.g. mvck */ +#define INSTR_SS_RRRDRD2 6, { R_8,D_20,B_16,R_12,D_36,B_32 } /* e.g. plo */ +#define INSTR_SS_RRRDRD3 6, { R_8,R_12,D_20,B_16,D_36,B_32 } /* e.g. lmd */ +#define INSTR_S_00 4, { 0,0,0,0,0,0 } /* e.g. hsch */ +#define INSTR_S_RD 4, { D_20,B_16,0,0,0,0 } /* e.g. lpsw */ +#define INSTR_SSF_RRDRD 6, { D_20,B_16,D_36,B_32,R_8,0 } /* e.g. mvcos */ + +#define MASK_E { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RIE_RRP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RIL_0P { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RIL_RP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RIL_UP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RIL_RI { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RIL_RU { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RI_0P { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RI_RI { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RI_RP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RI_RU { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RI_UP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RRE_00 { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } +#define MASK_RRE_0R { 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00 } +#define MASK_RRE_AA { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } +#define MASK_RRE_AR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } +#define MASK_RRE_F0 { 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00 } +#define MASK_RRE_FF { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } +#define MASK_RRE_R0 { 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00 } +#define MASK_RRE_RA { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } +#define MASK_RRE_RF { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } +#define MASK_RRE_RR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } +#define MASK_RRE_FR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } +#define MASK_RRE_RR_OPT { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } +#define MASK_RRF_F0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } +#define MASK_RRF_F0FF2 { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } +#define MASK_RRF_F0FR { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } +#define MASK_RRF_FUFF { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RRF_RURR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RRF_R0RR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RRF_U0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } +#define MASK_RRF_U0RF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } +#define MASK_RRF_UUFF { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RRF_0UFF { 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00 } +#define MASK_RRF_FFFU { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RRF_M0RR { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } +#define MASK_RR_0R { 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RR_FF { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RR_R0 { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RR_RR { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RR_U0 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RR_UR { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RRR_F0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } +#define MASK_RSE_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RSE_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RSE_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RSL_R0RD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RSI_RRP { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RS_AARD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RS_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RS_R0RD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RS_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RS_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RSY_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RSY_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RSY_AARD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RSY_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RXE_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RXE_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RXF_FRRDF { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RXF_RRRDR { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RXY_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RXY_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_RX_0RRD { 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RX_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RX_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_RX_URRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SI_URD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SIY_URD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define MASK_SSE_RDRD { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SS_L0RDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SS_L2RDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SS_LIRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SS_LLRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SS_RRRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SS_RRRDRD2 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SS_RRRDRD3 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define MASK_S_00 { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } +#define MASK_S_RD { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } +#define MASK_SSF_RRDRD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } + +/* QEMU-ADD: */ +#define INSTR_RIE_MRRP 6, { M4_32, R_8, R_12, J16_16, 0, 0 } /* e.g. crj */ +#define MASK_RIE_MRRP { 0xff, 0x00, 0x00, 0x00, 0x0f, 0xff } + +#define INSTR_RIE_MRIP 6, { M4_12, R_8, I8_32, J16_16, 0, 0 } /* e.g. cij */ +#define MASK_RIE_MRIP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } + +#define INSTR_RIE_RRIII 6, { R_8, R_12, U8_16, U8_24, U8_32, 0 } /* risbg */ +#define MASK_RIE_RRIII { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define INSTR_RIE_MRI 6, { M4_32, R_8, I16_16, 0, 0, 0 } /* e.g. cit */ +#define MASK_RIE_MRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define INSTR_RIE_MRU 6, { M4_32, R_8, U16_16, 0, 0, 0 } /* e.g. clfit */ +#define MASK_RIE_MRU { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define INSTR_RIE_RRI 6, { R_8, R_12, I16_16, 0, 0, 0 } +#define MASK_RIE_RRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } + +#define INSTR_RXY_URRD 6, { U8_8, D20_20, X_12, B_16, 0, 0 } +#define MASK_RXY_URRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } + +#define INSTR_SIL_DRI 6, { D_20, B_16, I16_32, 0, 0, 0 } +#define MASK_SIL_DRI { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } + +#define INSTR_RSY_MRRD 6, { M4_12, R_8, D20_20, B_16, 0, 0 } +#define MASK_SRY_MRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } + +#define INSTR_RRF_MRR 6, { M4_16, R_24, R_28, 0, 0, 0 } +#define MASK_RRF_MRR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } + +#define INSTR_SIY_DRI 6, { D20_20, B_16, I8_16, 0, 0, 0 } +#define MASK_SIY_DRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +/* QEMU-END */ + +/* include "s390-opc.tab" generated from opcodes/s390-opc.txt rev 1.17 */ +/* The opcode table. This file was generated by s390-mkopc. + + The format of the opcode table is: + + NAME OPCODE MASK OPERANDS + + Name is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches. */ + +static const struct s390_opcode s390_opcodes[] = + { + { "dp", OP8(0xfdLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "mp", OP8(0xfcLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "sp", OP8(0xfbLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "ap", OP8(0xfaLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "cp", OP8(0xf9LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "zap", OP8(0xf8LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "unpk", OP8(0xf3LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "pack", OP8(0xf2LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "mvo", OP8(0xf1LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, + { "srp", OP8(0xf0LL), MASK_SS_LIRDRD, INSTR_SS_LIRDRD, 3, 0}, + { "lmd", OP8(0xefLL), MASK_SS_RRRDRD3, INSTR_SS_RRRDRD3, 2, 2}, + { "plo", OP8(0xeeLL), MASK_SS_RRRDRD2, INSTR_SS_RRRDRD2, 3, 0}, + { "stdy", OP48(0xed0000000067LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3}, + { "stey", OP48(0xed0000000066LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3}, + { "ldy", OP48(0xed0000000065LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3}, + { "ley", OP48(0xed0000000064LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3}, + { "tgxt", OP48(0xed0000000059LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, + { "tcxt", OP48(0xed0000000058LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, + { "tgdt", OP48(0xed0000000055LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, + { "tcdt", OP48(0xed0000000054LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, + { "tget", OP48(0xed0000000051LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, + { "tcet", OP48(0xed0000000050LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, + { "srxt", OP48(0xed0000000049LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5}, + { "slxt", OP48(0xed0000000048LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5}, + { "srdt", OP48(0xed0000000041LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5}, + { "sldt", OP48(0xed0000000040LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5}, + { "msd", OP48(0xed000000003fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3}, + { "mad", OP48(0xed000000003eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3}, + { "myh", OP48(0xed000000003dLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, + { "mayh", OP48(0xed000000003cLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, + { "my", OP48(0xed000000003bLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, + { "may", OP48(0xed000000003aLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, + { "myl", OP48(0xed0000000039LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, + { "mayl", OP48(0xed0000000038LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, + { "mee", OP48(0xed0000000037LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "sqe", OP48(0xed0000000034LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "mse", OP48(0xed000000002fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3}, + { "mae", OP48(0xed000000002eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3}, + { "lxe", OP48(0xed0000000026LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "lxd", OP48(0xed0000000025LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "lde", OP48(0xed0000000024LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "msdb", OP48(0xed000000001fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0}, + { "madb", OP48(0xed000000001eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0}, + { "ddb", OP48(0xed000000001dLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "mdb", OP48(0xed000000001cLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "sdb", OP48(0xed000000001bLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "adb", OP48(0xed000000001aLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "cdb", OP48(0xed0000000019LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "kdb", OP48(0xed0000000018LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "meeb", OP48(0xed0000000017LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "sqdb", OP48(0xed0000000015LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "sqeb", OP48(0xed0000000014LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "tcxb", OP48(0xed0000000012LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "tcdb", OP48(0xed0000000011LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "tceb", OP48(0xed0000000010LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "mseb", OP48(0xed000000000fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0}, + { "maeb", OP48(0xed000000000eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0}, + { "deb", OP48(0xed000000000dLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "mdeb", OP48(0xed000000000cLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "seb", OP48(0xed000000000bLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "aeb", OP48(0xed000000000aLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "ceb", OP48(0xed0000000009LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "keb", OP48(0xed0000000008LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "mxdb", OP48(0xed0000000007LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "lxeb", OP48(0xed0000000006LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "lxdb", OP48(0xed0000000005LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "ldeb", OP48(0xed0000000004LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, + { "brxlg", OP48(0xec0000000045LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2}, + { "brxhg", OP48(0xec0000000044LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2}, +/* QEMU-ADD: */ + { "crj", OP48(0xec0000000076LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "cgrj", OP48(0xec0000000064LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "clrj", OP48(0xec0000000077LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "clgrj", OP48(0xec0000000065LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "cij", OP48(0xec000000007eLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "cgij", OP48(0xec000000007cLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "clij", OP48(0xec000000007fLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "clgij", OP48(0xec000000007dLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "risbg", OP48(0xec0000000055LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "risbhg", OP48(0xec000000005dLL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "risblg", OP48(0xec0000000051LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "rnsbg", OP48(0xec0000000054LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "rosbg", OP48(0xec0000000056LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "rxsbg", OP48(0xec0000000057LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "cit", OP48(0xec0000000072LL), MASK_RIE_MRI, INSTR_RIE_MRI, 3, 6}, + { "cgit", OP48(0xec0000000070LL), MASK_RIE_MRI, INSTR_RIE_MRI, 3, 6}, + { "clfit", OP48(0xec0000000073LL), MASK_RIE_MRU, INSTR_RIE_MRU, 3, 6}, + { "clgit", OP48(0xec0000000071LL), MASK_RIE_MRU, INSTR_RIE_MRU, 3, 6}, + { "ahik", OP48(0xec00000000d8LL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, + { "aghik", OP48(0xec00000000d9LL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, + { "alhsik", OP48(0xec00000000daLL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, + { "alghsik", OP48(0xec00000000dbLL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, +/* QEMU-END */ + { "tp", OP48(0xeb00000000c0LL), MASK_RSL_R0RD, INSTR_RSL_R0RD, 3, 0}, + { "stamy", OP48(0xeb000000009bLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3}, + { "lamy", OP48(0xeb000000009aLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3}, + { "lmy", OP48(0xeb0000000098LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "lmh", OP48(0xeb0000000096LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "lmh", OP48(0xeb0000000096LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "stmy", OP48(0xeb0000000090LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "clclu", OP48(0xeb000000008fLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "mvclu", OP48(0xeb000000008eLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 3}, + { "mvclu", OP48(0xeb000000008eLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 3, 0}, + { "icmy", OP48(0xeb0000000081LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, + { "icmh", OP48(0xeb0000000080LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, + { "icmh", OP48(0xeb0000000080LL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2}, + { "xiy", OP48(0xeb0000000057LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, + { "oiy", OP48(0xeb0000000056LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, + { "cliy", OP48(0xeb0000000055LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, + { "niy", OP48(0xeb0000000054LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, + { "mviy", OP48(0xeb0000000052LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, + { "tmy", OP48(0xeb0000000051LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, + { "bxleg", OP48(0xeb0000000045LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "bxleg", OP48(0xeb0000000045LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "bxhg", OP48(0xeb0000000044LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "bxhg", OP48(0xeb0000000044LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "cdsg", OP48(0xeb000000003eLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "cdsg", OP48(0xeb000000003eLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "cdsy", OP48(0xeb0000000031LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "csg", OP48(0xeb0000000030LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "csg", OP48(0xeb0000000030LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "lctlg", OP48(0xeb000000002fLL), MASK_RSY_CCRD, INSTR_RSY_CCRD, 2, 3}, + { "lctlg", OP48(0xeb000000002fLL), MASK_RSE_CCRD, INSTR_RSE_CCRD, 2, 2}, + { "stcmy", OP48(0xeb000000002dLL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, + { "stcmh", OP48(0xeb000000002cLL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, + { "stcmh", OP48(0xeb000000002cLL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2}, + { "stmh", OP48(0xeb0000000026LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "stmh", OP48(0xeb0000000026LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "stctg", OP48(0xeb0000000025LL), MASK_RSY_CCRD, INSTR_RSY_CCRD, 2, 3}, + { "stctg", OP48(0xeb0000000025LL), MASK_RSE_CCRD, INSTR_RSE_CCRD, 2, 2}, + { "stmg", OP48(0xeb0000000024LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "stmg", OP48(0xeb0000000024LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "clmy", OP48(0xeb0000000021LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, + { "clmh", OP48(0xeb0000000020LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, + { "clmh", OP48(0xeb0000000020LL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2}, + { "rll", OP48(0xeb000000001dLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 3}, + { "rll", OP48(0xeb000000001dLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 3, 2}, + { "rllg", OP48(0xeb000000001cLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "rllg", OP48(0xeb000000001cLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "csy", OP48(0xeb0000000014LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "tracg", OP48(0xeb000000000fLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "tracg", OP48(0xeb000000000fLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "sllg", OP48(0xeb000000000dLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "sllg", OP48(0xeb000000000dLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "srlg", OP48(0xeb000000000cLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "srlg", OP48(0xeb000000000cLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "slag", OP48(0xeb000000000bLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "slag", OP48(0xeb000000000bLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "srag", OP48(0xeb000000000aLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "srag", OP48(0xeb000000000aLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, + { "lmg", OP48(0xeb0000000004LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, + { "lmg", OP48(0xeb0000000004LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, +/* QEMU-ADD: */ + { "loc", OP48(0xeb00000000f2LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, + { "locg", OP48(0xeb00000000e2LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, + { "stoc", OP48(0xeb00000000f3LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, + { "stocg", OP48(0xeb00000000e3LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, + { "srak", OP48(0xeb00000000dcLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, + { "slak", OP48(0xeb00000000ddLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, + { "srlk", OP48(0xeb00000000deLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, + { "sllk", OP48(0xeb00000000dfLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, + { "asi", OP48(0xeb000000006aLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, + { "alsi", OP48(0xeb000000006eLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, + { "agsi", OP48(0xeb000000007aLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, + { "algsi", OP48(0xeb000000007eLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, +/* QEMU-END */ + { "unpka", OP8(0xeaLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "pka", OP8(0xe9LL), MASK_SS_L2RDRD, INSTR_SS_L2RDRD, 3, 0}, + { "mvcin", OP8(0xe8LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "mvcdk", OP16(0xe50fLL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, + { "mvcsk", OP16(0xe50eLL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, + { "tprot", OP16(0xe501LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, + { "strag", OP48(0xe50000000002LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 2, 2}, + { "lasp", OP16(0xe500LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, +/* QEMU-ADD: */ + { "mvhhi", OP16(0xe544LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "mvghi", OP16(0xe548LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "mvhi", OP16(0xe54cLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "chhsi", OP16(0xe554LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "clhhsi", OP16(0xe555LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "cghsi", OP16(0xe558LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "clghsi", OP16(0xe559LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "chsi", OP16(0xe55cLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "clfhsi", OP16(0xe55dLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, +/* QEMU-END */ + { "slb", OP48(0xe30000000099LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, + { "slb", OP48(0xe30000000099LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, + { "alc", OP48(0xe30000000098LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, + { "alc", OP48(0xe30000000098LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, + { "dl", OP48(0xe30000000097LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, + { "dl", OP48(0xe30000000097LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, + { "ml", OP48(0xe30000000096LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, + { "ml", OP48(0xe30000000096LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, + { "llh", OP48(0xe30000000095LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, + { "llc", OP48(0xe30000000094LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, + { "llgh", OP48(0xe30000000091LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "llgh", OP48(0xe30000000091LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "llgc", OP48(0xe30000000090LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "llgc", OP48(0xe30000000090LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "lpq", OP48(0xe3000000008fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lpq", OP48(0xe3000000008fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "stpq", OP48(0xe3000000008eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "stpq", OP48(0xe3000000008eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "slbg", OP48(0xe30000000089LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "slbg", OP48(0xe30000000089LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "alcg", OP48(0xe30000000088LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "alcg", OP48(0xe30000000088LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "dlg", OP48(0xe30000000087LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "dlg", OP48(0xe30000000087LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "mlg", OP48(0xe30000000086LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "mlg", OP48(0xe30000000086LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "xg", OP48(0xe30000000082LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "xg", OP48(0xe30000000082LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "og", OP48(0xe30000000081LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "og", OP48(0xe30000000081LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "ng", OP48(0xe30000000080LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "ng", OP48(0xe30000000080LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "shy", OP48(0xe3000000007bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "ahy", OP48(0xe3000000007aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "chy", OP48(0xe30000000079LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lhy", OP48(0xe30000000078LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lgb", OP48(0xe30000000077LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lb", OP48(0xe30000000076LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "icy", OP48(0xe30000000073LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "stcy", OP48(0xe30000000072LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lay", OP48(0xe30000000071LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "sthy", OP48(0xe30000000070LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "sly", OP48(0xe3000000005fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "aly", OP48(0xe3000000005eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "sy", OP48(0xe3000000005bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "ay", OP48(0xe3000000005aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "cy", OP48(0xe30000000059LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "ly", OP48(0xe30000000058LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "xy", OP48(0xe30000000057LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "oy", OP48(0xe30000000056LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "cly", OP48(0xe30000000055LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "ny", OP48(0xe30000000054LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "msy", OP48(0xe30000000051LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "sty", OP48(0xe30000000050LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "bctg", OP48(0xe30000000046LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "bctg", OP48(0xe30000000046LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "strvh", OP48(0xe3000000003fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "strvh", OP48(0xe3000000003fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, + { "strv", OP48(0xe3000000003eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, + { "strv", OP48(0xe3000000003eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, + { "clgf", OP48(0xe30000000031LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "clgf", OP48(0xe30000000031LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "cgf", OP48(0xe30000000030LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "cgf", OP48(0xe30000000030LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "strvg", OP48(0xe3000000002fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "strvg", OP48(0xe3000000002fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "cvdg", OP48(0xe3000000002eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "cvdg", OP48(0xe3000000002eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "cvdy", OP48(0xe30000000026LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "stg", OP48(0xe30000000024LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "stg", OP48(0xe30000000024LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "clg", OP48(0xe30000000021LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "clg", OP48(0xe30000000021LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "cg", OP48(0xe30000000020LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "cg", OP48(0xe30000000020LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "lrvh", OP48(0xe3000000001fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, + { "lrvh", OP48(0xe3000000001fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, + { "lrv", OP48(0xe3000000001eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, + { "lrv", OP48(0xe3000000001eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, + { "dsgf", OP48(0xe3000000001dLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "dsgf", OP48(0xe3000000001dLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "msgf", OP48(0xe3000000001cLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "msgf", OP48(0xe3000000001cLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "slgf", OP48(0xe3000000001bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "slgf", OP48(0xe3000000001bLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "algf", OP48(0xe3000000001aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "algf", OP48(0xe3000000001aLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "sgf", OP48(0xe30000000019LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "sgf", OP48(0xe30000000019LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "agf", OP48(0xe30000000018LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "agf", OP48(0xe30000000018LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "llgt", OP48(0xe30000000017LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "llgt", OP48(0xe30000000017LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "llgf", OP48(0xe30000000016LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "llgf", OP48(0xe30000000016LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "lgh", OP48(0xe30000000015LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lgh", OP48(0xe30000000015LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "lgf", OP48(0xe30000000014LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lgf", OP48(0xe30000000014LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "lray", OP48(0xe30000000013LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lt", OP48(0xe30000000012LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, + { "lrvg", OP48(0xe3000000000fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lrvg", OP48(0xe3000000000fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "cvbg", OP48(0xe3000000000eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "cvbg", OP48(0xe3000000000eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "dsg", OP48(0xe3000000000dLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "dsg", OP48(0xe3000000000dLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "msg", OP48(0xe3000000000cLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "msg", OP48(0xe3000000000cLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "slg", OP48(0xe3000000000bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "slg", OP48(0xe3000000000bLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "alg", OP48(0xe3000000000aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "alg", OP48(0xe3000000000aLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "sg", OP48(0xe30000000009LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "sg", OP48(0xe30000000009LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "ag", OP48(0xe30000000008LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "ag", OP48(0xe30000000008LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "cvby", OP48(0xe30000000006LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lg", OP48(0xe30000000004LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lg", OP48(0xe30000000004LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "lrag", OP48(0xe30000000003LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, + { "lrag", OP48(0xe30000000003LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, + { "ltg", OP48(0xe30000000002LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, +/* QEMU-ADD: */ + { "pfd", OP48(0xe30000000036LL), MASK_RXY_URRD, INSTR_RXY_URRD, 3, 6}, +/* QEMU-END */ + { "unpku", OP8(0xe2LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "pku", OP8(0xe1LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "edmk", OP8(0xdfLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "ed", OP8(0xdeLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "trt", OP8(0xddLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "tr", OP8(0xdcLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "mvcs", OP8(0xdbLL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0}, + { "mvcp", OP8(0xdaLL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0}, + { "mvck", OP8(0xd9LL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0}, + { "xc", OP8(0xd7LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "oc", OP8(0xd6LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "clc", OP8(0xd5LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "nc", OP8(0xd4LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "mvz", OP8(0xd3LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "mvc", OP8(0xd2LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "mvn", OP8(0xd1LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, + { "csst", OP16(0xc802LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5}, + { "ectg", OP16(0xc801LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5}, + { "mvcos", OP16(0xc800LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 4}, +/* QEMU-ADD: */ + { "exrl", OP16(0xc600ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "pfdrl", OP16(0xc602ll), MASK_RIL_UP, INSTR_RIL_UP, 3, 6}, + { "cghrl", OP16(0xc604ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "chrl", OP16(0xc605ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clghrl", OP16(0xc606ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clhrl", OP16(0xc607ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "cgrl", OP16(0xc608ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clgrl", OP16(0xc60all), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "cgfrl", OP16(0xc60cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "crl", OP16(0xc60dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clgfrl", OP16(0xc60ell), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clrl", OP16(0xc60fll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + + { "llhrl", OP16(0xc400ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lghrl", OP16(0xc404ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lhrl", OP16(0xc405ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "llghrl", OP16(0xc406ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "sthrl", OP16(0xc407ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lgrl", OP16(0xc408ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "stgrl", OP16(0xc40bll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lgfrl", OP16(0xc40cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lrl", OP16(0xc40dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "llgfrl", OP16(0xc40ell), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "strl", OP16(0xc40fll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, +/* QEMU-END */ + { "clfi", OP16(0xc20fLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "clgfi", OP16(0xc20eLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "cfi", OP16(0xc20dLL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, + { "cgfi", OP16(0xc20cLL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, + { "alfi", OP16(0xc20bLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "algfi", OP16(0xc20aLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "afi", OP16(0xc209LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, + { "agfi", OP16(0xc208LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, + { "slfi", OP16(0xc205LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "slgfi", OP16(0xc204LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, +/* QEMU-ADD: */ + { "msfi", OP16(0xc201ll), MASK_RIL_RI, INSTR_RIL_RI, 3, 6}, + { "msgfi", OP16(0xc200ll), MASK_RIL_RI, INSTR_RIL_RI, 3, 6}, +/* QEMU-END */ + { "jg", OP16(0xc0f4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgno", OP16(0xc0e4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgnh", OP16(0xc0d4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgnp", OP16(0xc0d4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgle", OP16(0xc0c4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgnl", OP16(0xc0b4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgnm", OP16(0xc0b4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jghe", OP16(0xc0a4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgnlh", OP16(0xc094LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jge", OP16(0xc084LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgz", OP16(0xc084LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgne", OP16(0xc074LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgnz", OP16(0xc074LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jglh", OP16(0xc064LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgnhe", OP16(0xc054LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgl", OP16(0xc044LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgm", OP16(0xc044LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgnle", OP16(0xc034LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgh", OP16(0xc024LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgp", OP16(0xc024LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "jgo", OP16(0xc014LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, + { "llilf", OP16(0xc00fLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "llihf", OP16(0xc00eLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "oilf", OP16(0xc00dLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "oihf", OP16(0xc00cLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "nilf", OP16(0xc00bLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "nihf", OP16(0xc00aLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "iilf", OP16(0xc009LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "iihf", OP16(0xc008LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "xilf", OP16(0xc007LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "xihf", OP16(0xc006LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, + { "brasl", OP16(0xc005LL), MASK_RIL_RP, INSTR_RIL_RP, 3, 2}, + { "brcl", OP16(0xc004LL), MASK_RIL_UP, INSTR_RIL_UP, 3, 2}, + { "lgfi", OP16(0xc001LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, + { "larl", OP16(0xc000LL), MASK_RIL_RP, INSTR_RIL_RP, 3, 2}, + { "icm", OP8(0xbfLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0}, + { "stcm", OP8(0xbeLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0}, + { "clm", OP8(0xbdLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0}, + { "cds", OP8(0xbbLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "cs", OP8(0xbaLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "cu42", OP16(0xb9b3LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "cu41", OP16(0xb9b2LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "cu24", OP16(0xb9b1LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "cu14", OP16(0xb9b0LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "lptea", OP16(0xb9aaLL), MASK_RRF_RURR, INSTR_RRF_RURR, 2, 4}, + { "esea", OP16(0xb99dLL), MASK_RRE_R0, INSTR_RRE_R0, 2, 2}, + { "slbr", OP16(0xb999LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, + { "alcr", OP16(0xb998LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, + { "dlr", OP16(0xb997LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, + { "mlr", OP16(0xb996LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, + { "llhr", OP16(0xb995LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "llcr", OP16(0xb994LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "troo", OP16(0xb993LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4}, + { "troo", OP16(0xb993LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "trot", OP16(0xb992LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4}, + { "trot", OP16(0xb992LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "trto", OP16(0xb991LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4}, + { "trto", OP16(0xb991LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "trtt", OP16(0xb990LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4}, + { "trtt", OP16(0xb990LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "idte", OP16(0xb98eLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 2, 3}, + { "epsw", OP16(0xb98dLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, + { "cspg", OP16(0xb98aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 3}, + { "slbgr", OP16(0xb989LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "alcgr", OP16(0xb988LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "dlgr", OP16(0xb987LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "mlgr", OP16(0xb986LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "llghr", OP16(0xb985LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "llgcr", OP16(0xb984LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "flogr", OP16(0xb983LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "xgr", OP16(0xb982LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "ogr", OP16(0xb981LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "ngr", OP16(0xb980LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "bctgr", OP16(0xb946LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "klmd", OP16(0xb93fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, + { "kimd", OP16(0xb93eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, + { "clgfr", OP16(0xb931LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "cgfr", OP16(0xb930LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "kmc", OP16(0xb92fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, + { "km", OP16(0xb92eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, + { "lhr", OP16(0xb927LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "lbr", OP16(0xb926LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "sturg", OP16(0xb925LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "clgr", OP16(0xb921LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "cgr", OP16(0xb920LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lrvr", OP16(0xb91fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, + { "kmac", OP16(0xb91eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, + { "dsgfr", OP16(0xb91dLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "msgfr", OP16(0xb91cLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "slgfr", OP16(0xb91bLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "algfr", OP16(0xb91aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "sgfr", OP16(0xb919LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "agfr", OP16(0xb918LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "llgtr", OP16(0xb917LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "llgfr", OP16(0xb916LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lgfr", OP16(0xb914LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lcgfr", OP16(0xb913LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "ltgfr", OP16(0xb912LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lngfr", OP16(0xb911LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lpgfr", OP16(0xb910LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lrvgr", OP16(0xb90fLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "eregg", OP16(0xb90eLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "dsgr", OP16(0xb90dLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "msgr", OP16(0xb90cLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "slgr", OP16(0xb90bLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "algr", OP16(0xb90aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "sgr", OP16(0xb909LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "agr", OP16(0xb908LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lghr", OP16(0xb907LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "lgbr", OP16(0xb906LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, + { "lurag", OP16(0xb905LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lgr", OP16(0xb904LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lcgr", OP16(0xb903LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "ltgr", OP16(0xb902LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lngr", OP16(0xb901LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "lpgr", OP16(0xb900LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, +/* QEMU-ADD: */ + { "crt", OP16(0xb972LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, + { "cgrt", OP16(0xb960LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, + { "clrt", OP16(0xb973LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, + { "clgrt", OP16(0xb961LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, + { "locr", OP16(0xb9f2LL), MASK_RRF_MRR, INSTR_RRF_MRR, 3, 6}, + { "locgr", OP16(0xb9e2LL), MASK_RRF_MRR, INSTR_RRF_MRR, 3, 6}, + { "popcnt", OP16(0xb9e1LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 6}, + { "ngrk", OP16(0xb9e4LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "ogrk", OP16(0xb9e6LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "xgrk", OP16(0xb9e7LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "agrk", OP16(0xb9e8LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "sgrk", OP16(0xb9e9LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "algrk", OP16(0xb9eaLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "slgrk", OP16(0xb9ebLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "nrk", OP16(0xb9f4LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "ork", OP16(0xb9f6LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "xrk", OP16(0xb9f7LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "ark", OP16(0xb9f8LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "srk", OP16(0xb9f9LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "alrk", OP16(0xb9faLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "slrk", OP16(0xb9fbLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, +/* QEMU-END */ + { "lctl", OP8(0xb7LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0}, + { "stctl", OP8(0xb6LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0}, + { "rrxtr", OP16(0xb3ffLL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, + { "iextr", OP16(0xb3feLL), MASK_RRF_F0FR, INSTR_RRF_F0FR, 2, 5}, + { "qaxtr", OP16(0xb3fdLL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, + { "cextr", OP16(0xb3fcLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "cxstr", OP16(0xb3fbLL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, + { "cxutr", OP16(0xb3faLL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, + { "cxgtr", OP16(0xb3f9LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, + { "rrdtr", OP16(0xb3f7LL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, + { "iedtr", OP16(0xb3f6LL), MASK_RRF_F0FR, INSTR_RRF_F0FR, 2, 5}, + { "qadtr", OP16(0xb3f5LL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, + { "cedtr", OP16(0xb3f4LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "cdstr", OP16(0xb3f3LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, + { "cdutr", OP16(0xb3f2LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, + { "cdgtr", OP16(0xb3f1LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, + { "esxtr", OP16(0xb3efLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "eextr", OP16(0xb3edLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "cxtr", OP16(0xb3ecLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "csxtr", OP16(0xb3ebLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "cuxtr", OP16(0xb3eaLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "cgxtr", OP16(0xb3e9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 5}, + { "kxtr", OP16(0xb3e8LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "esdtr", OP16(0xb3e7LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "eedtr", OP16(0xb3e5LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "cdtr", OP16(0xb3e4LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "csdtr", OP16(0xb3e3LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "cudtr", OP16(0xb3e2LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "cgdtr", OP16(0xb3e1LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 5}, + { "kdtr", OP16(0xb3e0LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "fixtr", OP16(0xb3dfLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5}, + { "ltxtr", OP16(0xb3deLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "ldxtr", OP16(0xb3ddLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5}, + { "lxdtr", OP16(0xb3dcLL), MASK_RRF_0UFF, INSTR_RRF_0UFF, 2, 5}, + { "sxtr", OP16(0xb3dbLL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, + { "axtr", OP16(0xb3daLL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, + { "dxtr", OP16(0xb3d9LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, + { "mxtr", OP16(0xb3d8LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, + { "fidtr", OP16(0xb3d7LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5}, + { "ltdtr", OP16(0xb3d6LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "ledtr", OP16(0xb3d5LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5}, + { "ldetr", OP16(0xb3d4LL), MASK_RRF_0UFF, INSTR_RRF_0UFF, 2, 5}, + { "sdtr", OP16(0xb3d3LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, + { "adtr", OP16(0xb3d2LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, + { "ddtr", OP16(0xb3d1LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, + { "mdtr", OP16(0xb3d0LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, + { "lgdr", OP16(0xb3cdLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, + { "cgxr", OP16(0xb3caLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cgdr", OP16(0xb3c9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cger", OP16(0xb3c8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cxgr", OP16(0xb3c6LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "cdgr", OP16(0xb3c5LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "cegr", OP16(0xb3c4LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "ldgr", OP16(0xb3c1LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, + { "cfxr", OP16(0xb3baLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cfdr", OP16(0xb3b9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cfer", OP16(0xb3b8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cxfr", OP16(0xb3b6LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, + { "cdfr", OP16(0xb3b5LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, + { "cefr", OP16(0xb3b4LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, + { "cgxbr", OP16(0xb3aaLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cgdbr", OP16(0xb3a9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cgebr", OP16(0xb3a8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, + { "cxgbr", OP16(0xb3a6LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "cdgbr", OP16(0xb3a5LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "cegbr", OP16(0xb3a4LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, + { "cfxbr", OP16(0xb39aLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0}, + { "cfdbr", OP16(0xb399LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0}, + { "cfebr", OP16(0xb398LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0}, + { "cxfbr", OP16(0xb396LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, + { "cdfbr", OP16(0xb395LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, + { "cefbr", OP16(0xb394LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, + { "efpc", OP16(0xb38cLL), MASK_RRE_RR_OPT, INSTR_RRE_RR_OPT, 3, 0}, + { "sfasr", OP16(0xb385LL), MASK_RRE_R0, INSTR_RRE_R0, 2, 5}, + { "sfpc", OP16(0xb384LL), MASK_RRE_RR_OPT, INSTR_RRE_RR_OPT, 3, 0}, + { "fidr", OP16(0xb37fLL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, + { "fier", OP16(0xb377LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, + { "lzxr", OP16(0xb376LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "lzdr", OP16(0xb375LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "lzer", OP16(0xb374LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "lcdfr", OP16(0xb373LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "cpsdr", OP16(0xb372LL), MASK_RRF_F0FF2, INSTR_RRF_F0FF2, 2, 5}, + { "lndfr", OP16(0xb371LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "lpdfr", OP16(0xb370LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, + { "cxr", OP16(0xb369LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "fixr", OP16(0xb367LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, + { "lexr", OP16(0xb366LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lxr", OP16(0xb365LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "lcxr", OP16(0xb363LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "ltxr", OP16(0xb362LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lnxr", OP16(0xb361LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lpxr", OP16(0xb360LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "fidbr", OP16(0xb35fLL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, + { "didbr", OP16(0xb35bLL), MASK_RRF_FUFF, INSTR_RRF_FUFF, 3, 0}, + { "thdr", OP16(0xb359LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "thder", OP16(0xb358LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "fiebr", OP16(0xb357LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, + { "diebr", OP16(0xb353LL), MASK_RRF_FUFF, INSTR_RRF_FUFF, 3, 0}, + { "tbdr", OP16(0xb351LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, + { "tbedr", OP16(0xb350LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, + { "dxbr", OP16(0xb34dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "mxbr", OP16(0xb34cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "sxbr", OP16(0xb34bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "axbr", OP16(0xb34aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "cxbr", OP16(0xb349LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "kxbr", OP16(0xb348LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "fixbr", OP16(0xb347LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, + { "lexbr", OP16(0xb346LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "ldxbr", OP16(0xb345LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "ledbr", OP16(0xb344LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lcxbr", OP16(0xb343LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "ltxbr", OP16(0xb342LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lnxbr", OP16(0xb341LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lpxbr", OP16(0xb340LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "msdr", OP16(0xb33fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3}, + { "madr", OP16(0xb33eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3}, + { "myhr", OP16(0xb33dLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, + { "mayhr", OP16(0xb33cLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, + { "myr", OP16(0xb33bLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, + { "mayr", OP16(0xb33aLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, + { "mylr", OP16(0xb339LL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, + { "maylr", OP16(0xb338LL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, + { "meer", OP16(0xb337LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "sqxr", OP16(0xb336LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "mser", OP16(0xb32fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3}, + { "maer", OP16(0xb32eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3}, + { "lxer", OP16(0xb326LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lxdr", OP16(0xb325LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lder", OP16(0xb324LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "msdbr", OP16(0xb31fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0}, + { "madbr", OP16(0xb31eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0}, + { "ddbr", OP16(0xb31dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "mdbr", OP16(0xb31cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "sdbr", OP16(0xb31bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "adbr", OP16(0xb31aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "cdbr", OP16(0xb319LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "kdbr", OP16(0xb318LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "meebr", OP16(0xb317LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "sqxbr", OP16(0xb316LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "sqdbr", OP16(0xb315LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "sqebr", OP16(0xb314LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lcdbr", OP16(0xb313LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "ltdbr", OP16(0xb312LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lndbr", OP16(0xb311LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lpdbr", OP16(0xb310LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "msebr", OP16(0xb30fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0}, + { "maebr", OP16(0xb30eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0}, + { "debr", OP16(0xb30dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "mdebr", OP16(0xb30cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "sebr", OP16(0xb30bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "aebr", OP16(0xb30aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "cebr", OP16(0xb309LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "kebr", OP16(0xb308LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "mxdbr", OP16(0xb307LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lxebr", OP16(0xb306LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lxdbr", OP16(0xb305LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "ldebr", OP16(0xb304LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lcebr", OP16(0xb303LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "ltebr", OP16(0xb302LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lnebr", OP16(0xb301LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, + { "lpebr", OP16(0xb300LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, +/* QEMU-ADD: */ + { "clfebr", OP16(0xb39cLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clfdbr", OP16(0xb39dLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clfxbr", OP16(0xb39eLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clgebr", OP16(0xb3acLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clgdbr", OP16(0xb3adLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clgxbr", OP16(0xb3aeLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "celfbr", OP16(0xb390LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "cdlfbr", OP16(0xb391LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "cxlfbr", OP16(0xb392LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "celgbr", OP16(0xb3a0LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "cdlgbr", OP16(0xb3a1LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "cxlgbr", OP16(0xb3a2LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, +/* QEMU-END */ + { "trap4", OP16(0xb2ffLL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "lfas", OP16(0xb2bdLL), MASK_S_RD, INSTR_S_RD, 2, 5}, + { "srnmt", OP16(0xb2b9LL), MASK_S_RD, INSTR_S_RD, 2, 5}, + { "lpswe", OP16(0xb2b2LL), MASK_S_RD, INSTR_S_RD, 2, 2}, + { "stfl", OP16(0xb2b1LL), MASK_S_RD, INSTR_S_RD, 3, 2}, + { "stfle", OP16(0xb2b0LL), MASK_S_RD, INSTR_S_RD, 2, 4}, + { "cu12", OP16(0xb2a7LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "cutfu", OP16(0xb2a7LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "cutfu", OP16(0xb2a7LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "cu21", OP16(0xb2a6LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "cuutf", OP16(0xb2a6LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "cuutf", OP16(0xb2a6LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "tre", OP16(0xb2a5LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "lfpc", OP16(0xb29dLL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stfpc", OP16(0xb29cLL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "srnm", OP16(0xb299LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stsi", OP16(0xb27dLL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stckf", OP16(0xb27cLL), MASK_S_RD, INSTR_S_RD, 2, 4}, + { "sacf", OP16(0xb279LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stcke", OP16(0xb278LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "rp", OP16(0xb277LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "xsch", OP16(0xb276LL), MASK_S_00, INSTR_S_00, 3, 0}, + { "siga", OP16(0xb274LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "cmpsc", OP16(0xb263LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "cmpsc", OP16(0xb263LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "srst", OP16(0xb25eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "clst", OP16(0xb25dLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "bsa", OP16(0xb25aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "bsg", OP16(0xb258LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "cuse", OP16(0xb257LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "mvst", OP16(0xb255LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "mvpg", OP16(0xb254LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "msr", OP16(0xb252LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "csp", OP16(0xb250LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "ear", OP16(0xb24fLL), MASK_RRE_RA, INSTR_RRE_RA, 3, 0}, + { "sar", OP16(0xb24eLL), MASK_RRE_AR, INSTR_RRE_AR, 3, 0}, + { "cpya", OP16(0xb24dLL), MASK_RRE_AA, INSTR_RRE_AA, 3, 0}, + { "tar", OP16(0xb24cLL), MASK_RRE_AR, INSTR_RRE_AR, 3, 0}, + { "lura", OP16(0xb24bLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "esta", OP16(0xb24aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "ereg", OP16(0xb249LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "palb", OP16(0xb248LL), MASK_RRE_00, INSTR_RRE_00, 3, 0}, + { "msta", OP16(0xb247LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "stura", OP16(0xb246LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "sqer", OP16(0xb245LL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0}, + { "sqdr", OP16(0xb244LL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0}, + { "cksm", OP16(0xb241LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "bakr", OP16(0xb240LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "schm", OP16(0xb23cLL), MASK_S_00, INSTR_S_00, 3, 0}, + { "rchp", OP16(0xb23bLL), MASK_S_00, INSTR_S_00, 3, 0}, + { "stcps", OP16(0xb23aLL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stcrw", OP16(0xb239LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "rsch", OP16(0xb238LL), MASK_S_00, INSTR_S_00, 3, 0}, + { "sal", OP16(0xb237LL), MASK_S_00, INSTR_S_00, 3, 0}, + { "tpi", OP16(0xb236LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "tsch", OP16(0xb235LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stsch", OP16(0xb234LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "ssch", OP16(0xb233LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "msch", OP16(0xb232LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "hsch", OP16(0xb231LL), MASK_S_00, INSTR_S_00, 3, 0}, + { "csch", OP16(0xb230LL), MASK_S_00, INSTR_S_00, 3, 0}, + { "pgout", OP16(0xb22fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "pgin", OP16(0xb22eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "dxr", OP16(0xb22dLL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0}, + { "tb", OP16(0xb22cLL), MASK_RRE_0R, INSTR_RRE_0R, 3, 0}, + { "sske", OP16(0xb22bLL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, + { "sske", OP16(0xb22bLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "rrbe", OP16(0xb22aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "iske", OP16(0xb229LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "pt", OP16(0xb228LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "esar", OP16(0xb227LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "epar", OP16(0xb226LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "ssar", OP16(0xb225LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "iac", OP16(0xb224LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "ivsk", OP16(0xb223LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "ipm", OP16(0xb222LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, + { "ipte", OP16(0xb221LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, + { "cfc", OP16(0xb21aLL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "sac", OP16(0xb219LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "pc", OP16(0xb218LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "sie", OP16(0xb214LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stap", OP16(0xb212LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stpx", OP16(0xb211LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "spx", OP16(0xb210LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "ptlb", OP16(0xb20dLL), MASK_S_00, INSTR_S_00, 3, 0}, + { "ipk", OP16(0xb20bLL), MASK_S_00, INSTR_S_00, 3, 0}, + { "spka", OP16(0xb20aLL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stpt", OP16(0xb209LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "spt", OP16(0xb208LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stckc", OP16(0xb207LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "sckc", OP16(0xb206LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stck", OP16(0xb205LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "sck", OP16(0xb204LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "stidp", OP16(0xb202LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "lra", OP8(0xb1LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "mc", OP8(0xafLL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "sigp", OP8(0xaeLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "stosm", OP8(0xadLL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "stnsm", OP8(0xacLL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "clcle", OP8(0xa9LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "mvcle", OP8(0xa8LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "j", OP16(0xa7f4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jno", OP16(0xa7e4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jnh", OP16(0xa7d4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jnp", OP16(0xa7d4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jle", OP16(0xa7c4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jnl", OP16(0xa7b4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jnm", OP16(0xa7b4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jhe", OP16(0xa7a4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jnlh", OP16(0xa794LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "je", OP16(0xa784LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jz", OP16(0xa784LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jne", OP16(0xa774LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jnz", OP16(0xa774LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jlh", OP16(0xa764LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jnhe", OP16(0xa754LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jl", OP16(0xa744LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jm", OP16(0xa744LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jnle", OP16(0xa734LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jh", OP16(0xa724LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jp", OP16(0xa724LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "jo", OP16(0xa714LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, + { "cghi", OP16(0xa70fLL), MASK_RI_RI, INSTR_RI_RI, 2, 2}, + { "chi", OP16(0xa70eLL), MASK_RI_RI, INSTR_RI_RI, 3, 0}, + { "mghi", OP16(0xa70dLL), MASK_RI_RI, INSTR_RI_RI, 2, 2}, + { "mhi", OP16(0xa70cLL), MASK_RI_RI, INSTR_RI_RI, 3, 0}, + { "aghi", OP16(0xa70bLL), MASK_RI_RI, INSTR_RI_RI, 2, 2}, + { "ahi", OP16(0xa70aLL), MASK_RI_RI, INSTR_RI_RI, 3, 0}, + { "lghi", OP16(0xa709LL), MASK_RI_RI, INSTR_RI_RI, 2, 2}, + { "lhi", OP16(0xa708LL), MASK_RI_RI, INSTR_RI_RI, 3, 0}, + { "brctg", OP16(0xa707LL), MASK_RI_RP, INSTR_RI_RP, 2, 2}, + { "brct", OP16(0xa706LL), MASK_RI_RP, INSTR_RI_RP, 3, 0}, + { "bras", OP16(0xa705LL), MASK_RI_RP, INSTR_RI_RP, 3, 0}, + { "brc", OP16(0xa704LL), MASK_RI_UP, INSTR_RI_UP, 3, 0}, + { "tmhl", OP16(0xa703LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "tmhh", OP16(0xa702LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "tml", OP16(0xa701LL), MASK_RI_RU, INSTR_RI_RU, 3, 0}, + { "tmll", OP16(0xa701LL), MASK_RI_RU, INSTR_RI_RU, 3, 0}, + { "tmh", OP16(0xa700LL), MASK_RI_RU, INSTR_RI_RU, 3, 0}, + { "tmlh", OP16(0xa700LL), MASK_RI_RU, INSTR_RI_RU, 3, 0}, + { "llill", OP16(0xa50fLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "llilh", OP16(0xa50eLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "llihl", OP16(0xa50dLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "llihh", OP16(0xa50cLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "oill", OP16(0xa50bLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "oilh", OP16(0xa50aLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "oihl", OP16(0xa509LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "oihh", OP16(0xa508LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "nill", OP16(0xa507LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "nilh", OP16(0xa506LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "nihl", OP16(0xa505LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "nihh", OP16(0xa504LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "iill", OP16(0xa503LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "iilh", OP16(0xa502LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "iihl", OP16(0xa501LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "iihh", OP16(0xa500LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, + { "stam", OP8(0x9bLL), MASK_RS_AARD, INSTR_RS_AARD, 3, 0}, + { "lam", OP8(0x9aLL), MASK_RS_AARD, INSTR_RS_AARD, 3, 0}, + { "trace", OP8(0x99LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "lm", OP8(0x98LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "xi", OP8(0x97LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "oi", OP8(0x96LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "cli", OP8(0x95LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "ni", OP8(0x94LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "ts", OP8(0x93LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "mvi", OP8(0x92LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "tm", OP8(0x91LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, + { "stm", OP8(0x90LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "slda", OP8(0x8fLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, + { "srda", OP8(0x8eLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, + { "sldl", OP8(0x8dLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, + { "srdl", OP8(0x8cLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, + { "sla", OP8(0x8bLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, + { "sra", OP8(0x8aLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, + { "sll", OP8(0x89LL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, + { "srl", OP8(0x88LL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, + { "bxle", OP8(0x87LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "bxh", OP8(0x86LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "brxle", OP8(0x85LL), MASK_RSI_RRP, INSTR_RSI_RRP, 3, 0}, + { "brxh", OP8(0x84LL), MASK_RSI_RRP, INSTR_RSI_RRP, 3, 0}, + { "diag", OP8(0x83LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, + { "lpsw", OP8(0x82LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "ssm", OP8(0x80LL), MASK_S_RD, INSTR_S_RD, 3, 0}, + { "su", OP8(0x7fLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "au", OP8(0x7eLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "de", OP8(0x7dLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "me", OP8(0x7cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "mde", OP8(0x7cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "se", OP8(0x7bLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "ae", OP8(0x7aLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "ce", OP8(0x79LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "le", OP8(0x78LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "ms", OP8(0x71LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "ste", OP8(0x70LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "sw", OP8(0x6fLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "aw", OP8(0x6eLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "dd", OP8(0x6dLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "md", OP8(0x6cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "sd", OP8(0x6bLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "ad", OP8(0x6aLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "cd", OP8(0x69LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "ld", OP8(0x68LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "mxd", OP8(0x67LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "std", OP8(0x60LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, + { "sl", OP8(0x5fLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "al", OP8(0x5eLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "d", OP8(0x5dLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "m", OP8(0x5cLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "s", OP8(0x5bLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "a", OP8(0x5aLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "c", OP8(0x59LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "l", OP8(0x58LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "x", OP8(0x57LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "o", OP8(0x56LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "cl", OP8(0x55LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "n", OP8(0x54LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "lae", OP8(0x51LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "st", OP8(0x50LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "cvb", OP8(0x4fLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "cvd", OP8(0x4eLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "bas", OP8(0x4dLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "mh", OP8(0x4cLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "sh", OP8(0x4bLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "ah", OP8(0x4aLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "ch", OP8(0x49LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "lh", OP8(0x48LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "b", OP16(0x47f0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bno", OP16(0x47e0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bnh", OP16(0x47d0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bnp", OP16(0x47d0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "ble", OP16(0x47c0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bnl", OP16(0x47b0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bnm", OP16(0x47b0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bhe", OP16(0x47a0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bnlh", OP16(0x4790LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "be", OP16(0x4780LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bz", OP16(0x4780LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bne", OP16(0x4770LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bnz", OP16(0x4770LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "blh", OP16(0x4760LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bnhe", OP16(0x4750LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bl", OP16(0x4740LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bm", OP16(0x4740LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bnle", OP16(0x4730LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bh", OP16(0x4720LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bp", OP16(0x4720LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bo", OP16(0x4710LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bc", OP8(0x47LL), MASK_RX_URRD, INSTR_RX_URRD, 3, 0}, + { "nop", OP16(0x4700LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, + { "bct", OP8(0x46LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "bal", OP8(0x45LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "ex", OP8(0x44LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "ic", OP8(0x43LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "stc", OP8(0x42LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "la", OP8(0x41LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "sth", OP8(0x40LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, + { "sur", OP8(0x3fLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "aur", OP8(0x3eLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "der", OP8(0x3dLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "mer", OP8(0x3cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "mder", OP8(0x3cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "ser", OP8(0x3bLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "aer", OP8(0x3aLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "cer", OP8(0x39LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "ler", OP8(0x38LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "sxr", OP8(0x37LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "axr", OP8(0x36LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lrer", OP8(0x35LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "ledr", OP8(0x35LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "her", OP8(0x34LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lcer", OP8(0x33LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lter", OP8(0x32LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lner", OP8(0x31LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lper", OP8(0x30LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "swr", OP8(0x2fLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "awr", OP8(0x2eLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "ddr", OP8(0x2dLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "mdr", OP8(0x2cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "sdr", OP8(0x2bLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "adr", OP8(0x2aLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "cdr", OP8(0x29LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "ldr", OP8(0x28LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "mxdr", OP8(0x27LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "mxr", OP8(0x26LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lrdr", OP8(0x25LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "ldxr", OP8(0x25LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "hdr", OP8(0x24LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lcdr", OP8(0x23LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "ltdr", OP8(0x22LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lndr", OP8(0x21LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "lpdr", OP8(0x20LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, + { "slr", OP8(0x1fLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "alr", OP8(0x1eLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "dr", OP8(0x1dLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "mr", OP8(0x1cLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "sr", OP8(0x1bLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "ar", OP8(0x1aLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "cr", OP8(0x19LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "lr", OP8(0x18LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "xr", OP8(0x17LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "or", OP8(0x16LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "clr", OP8(0x15LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "nr", OP8(0x14LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "lcr", OP8(0x13LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "ltr", OP8(0x12LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "lnr", OP8(0x11LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "lpr", OP8(0x10LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "clcl", OP8(0x0fLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "mvcl", OP8(0x0eLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "basr", OP8(0x0dLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "bassm", OP8(0x0cLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "bsm", OP8(0x0bLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "svc", OP8(0x0aLL), MASK_RR_U0, INSTR_RR_U0, 3, 0}, + { "br", OP16(0x07f0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnor", OP16(0x07e0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnhr", OP16(0x07d0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnpr", OP16(0x07d0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bler", OP16(0x07c0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnlr", OP16(0x07b0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnmr", OP16(0x07b0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bher", OP16(0x07a0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnlhr", OP16(0x0790LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "ber", OP16(0x0780LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bzr", OP16(0x0780LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bner", OP16(0x0770LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnzr", OP16(0x0770LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "blhr", OP16(0x0760LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnher", OP16(0x0750LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "blr", OP16(0x0740LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bmr", OP16(0x0740LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bnler", OP16(0x0730LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bhr", OP16(0x0720LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bpr", OP16(0x0720LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bor", OP16(0x0710LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bcr", OP8(0x07LL), MASK_RR_UR, INSTR_RR_UR, 3, 0}, + { "nopr", OP16(0x0700LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, + { "bctr", OP8(0x06LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "balr", OP8(0x05LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, + { "spm", OP8(0x04LL), MASK_RR_R0, INSTR_RR_R0, 3, 0}, + { "trap2", OP16(0x01ffLL), MASK_E, INSTR_E, 3, 0}, + { "sam64", OP16(0x010eLL), MASK_E, INSTR_E, 2, 2}, + { "sam31", OP16(0x010dLL), MASK_E, INSTR_E, 3, 2}, + { "sam24", OP16(0x010cLL), MASK_E, INSTR_E, 3, 2}, + { "tam", OP16(0x010bLL), MASK_E, INSTR_E, 3, 2}, + { "pfpo", OP16(0x010aLL), MASK_E, INSTR_E, 2, 5}, + { "sckpf", OP16(0x0107LL), MASK_E, INSTR_E, 3, 0}, + { "upt", OP16(0x0102LL), MASK_E, INSTR_E, 3, 0}, + { "pr", OP16(0x0101LL), MASK_E, INSTR_E, 3, 0}, +}; + +static const int s390_num_opcodes = + sizeof (s390_opcodes) / sizeof (s390_opcodes[0]); diff --git a/disas/sh4.c b/disas/sh4.c new file mode 100644 index 0000000..020f5eb --- /dev/null +++ b/disas/sh4.c @@ -0,0 +1,2077 @@ +/* Disassemble SH instructions. + Copyright 1993, 1994, 1995, 1997, 1998, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include +#include "disas/bfd.h" + +#define DEFINE_TABLE + +typedef enum + { + HEX_0, + HEX_1, + HEX_2, + HEX_3, + HEX_4, + HEX_5, + HEX_6, + HEX_7, + HEX_8, + HEX_9, + HEX_A, + HEX_B, + HEX_C, + HEX_D, + HEX_E, + HEX_F, + HEX_XX00, + HEX_00YY, + REG_N, + REG_N_D, /* nnn0 */ + REG_N_B01, /* nn01 */ + REG_M, + SDT_REG_N, + REG_NM, + REG_B, + BRANCH_12, + BRANCH_8, + IMM0_4, + IMM0_4BY2, + IMM0_4BY4, + IMM1_4, + IMM1_4BY2, + IMM1_4BY4, + PCRELIMM_8BY2, + PCRELIMM_8BY4, + IMM0_8, + IMM0_8BY2, + IMM0_8BY4, + IMM1_8, + IMM1_8BY2, + IMM1_8BY4, + PPI, + NOPX, + NOPY, + MOVX, + MOVY, + MOVX_NOPY, + MOVY_NOPX, + PSH, + PMUL, + PPI3, + PPI3NC, + PDC, + PPIC, + REPEAT, + IMM0_3c, /* xxxx 0iii */ + IMM0_3s, /* xxxx 1iii */ + IMM0_3Uc, /* 0iii xxxx */ + IMM0_3Us, /* 1iii xxxx */ + IMM0_20_4, + IMM0_20, /* follows IMM0_20_4 */ + IMM0_20BY8, /* follows IMM0_20_4 */ + DISP0_12, + DISP0_12BY2, + DISP0_12BY4, + DISP0_12BY8, + DISP1_12, + DISP1_12BY2, + DISP1_12BY4, + DISP1_12BY8 + } +sh_nibble_type; + +typedef enum + { + A_END, + A_BDISP12, + A_BDISP8, + A_DEC_M, + A_DEC_N, + A_DISP_GBR, + A_PC, + A_DISP_PC, + A_DISP_PC_ABS, + A_DISP_REG_M, + A_DISP_REG_N, + A_GBR, + A_IMM, + A_INC_M, + A_INC_N, + A_IND_M, + A_IND_N, + A_IND_R0_REG_M, + A_IND_R0_REG_N, + A_MACH, + A_MACL, + A_PR, + A_R0, + A_R0_GBR, + A_REG_M, + A_REG_N, + A_REG_B, + A_SR, + A_VBR, + A_TBR, + A_DISP_TBR, + A_DISP2_TBR, + A_DEC_R15, + A_INC_R15, + A_MOD, + A_RE, + A_RS, + A_DSR, + DSP_REG_M, + DSP_REG_N, + DSP_REG_X, + DSP_REG_Y, + DSP_REG_E, + DSP_REG_F, + DSP_REG_G, + DSP_REG_A_M, + DSP_REG_AX, + DSP_REG_XY, + DSP_REG_AY, + DSP_REG_YX, + AX_INC_N, + AY_INC_N, + AXY_INC_N, + AYX_INC_N, + AX_IND_N, + AY_IND_N, + AXY_IND_N, + AYX_IND_N, + AX_PMOD_N, + AXY_PMOD_N, + AY_PMOD_N, + AYX_PMOD_N, + AS_DEC_N, + AS_INC_N, + AS_IND_N, + AS_PMOD_N, + A_A0, + A_X0, + A_X1, + A_Y0, + A_Y1, + A_SSR, + A_SPC, + A_SGR, + A_DBR, + F_REG_N, + F_REG_M, + D_REG_N, + D_REG_M, + X_REG_N, /* Only used for argument parsing. */ + X_REG_M, /* Only used for argument parsing. */ + DX_REG_N, + DX_REG_M, + V_REG_N, + V_REG_M, + XMTRX_M4, + F_FR0, + FPUL_N, + FPUL_M, + FPSCR_N, + FPSCR_M + } +sh_arg_type; + +typedef enum + { + A_A1_NUM = 5, + A_A0_NUM = 7, + A_X0_NUM, A_X1_NUM, A_Y0_NUM, A_Y1_NUM, + A_M0_NUM, A_A1G_NUM, A_M1_NUM, A_A0G_NUM + } +sh_dsp_reg_nums; + +#define arch_sh1_base 0x0001 +#define arch_sh2_base 0x0002 +#define arch_sh3_base 0x0004 +#define arch_sh4_base 0x0008 +#define arch_sh4a_base 0x0010 +#define arch_sh2a_base 0x0020 + +/* This is an annotation on instruction types, but we abuse the arch + field in instructions to denote it. */ +#define arch_op32 0x00100000 /* This is a 32-bit opcode. */ + +#define arch_sh_no_mmu 0x04000000 +#define arch_sh_has_mmu 0x08000000 +#define arch_sh_no_co 0x10000000 /* neither FPU nor DSP co-processor */ +#define arch_sh_sp_fpu 0x20000000 /* single precision FPU */ +#define arch_sh_dp_fpu 0x40000000 /* double precision FPU */ +#define arch_sh_has_dsp 0x80000000 + + +#define arch_sh_base_mask 0x0000003f +#define arch_opann_mask 0x00100000 +#define arch_sh_mmu_mask 0x0c000000 +#define arch_sh_co_mask 0xf0000000 + + +#define arch_sh1 (arch_sh1_base|arch_sh_no_mmu|arch_sh_no_co) +#define arch_sh2 (arch_sh2_base|arch_sh_no_mmu|arch_sh_no_co) +#define arch_sh2a (arch_sh2a_base|arch_sh_no_mmu|arch_sh_dp_fpu) +#define arch_sh2a_nofpu (arch_sh2a_base|arch_sh_no_mmu|arch_sh_no_co) +#define arch_sh2e (arch_sh2_base|arch_sh2a_base|arch_sh_no_mmu|arch_sh_sp_fpu) +#define arch_sh_dsp (arch_sh2_base|arch_sh_no_mmu|arch_sh_has_dsp) +#define arch_sh3_nommu (arch_sh3_base|arch_sh_no_mmu|arch_sh_no_co) +#define arch_sh3 (arch_sh3_base|arch_sh_has_mmu|arch_sh_no_co) +#define arch_sh3e (arch_sh3_base|arch_sh_has_mmu|arch_sh_sp_fpu) +#define arch_sh3_dsp (arch_sh3_base|arch_sh_has_mmu|arch_sh_has_dsp) +#define arch_sh4 (arch_sh4_base|arch_sh_has_mmu|arch_sh_dp_fpu) +#define arch_sh4a (arch_sh4a_base|arch_sh_has_mmu|arch_sh_dp_fpu) +#define arch_sh4al_dsp (arch_sh4a_base|arch_sh_has_mmu|arch_sh_has_dsp) +#define arch_sh4_nofpu (arch_sh4_base|arch_sh_has_mmu|arch_sh_no_co) +#define arch_sh4a_nofpu (arch_sh4a_base|arch_sh_has_mmu|arch_sh_no_co) +#define arch_sh4_nommu_nofpu (arch_sh4_base|arch_sh_no_mmu|arch_sh_no_co) + +#define SH_MERGE_ARCH_SET(SET1, SET2) ((SET1) & (SET2)) +#define SH_VALID_BASE_ARCH_SET(SET) (((SET) & arch_sh_base_mask) != 0) +#define SH_VALID_MMU_ARCH_SET(SET) (((SET) & arch_sh_mmu_mask) != 0) +#define SH_VALID_CO_ARCH_SET(SET) (((SET) & arch_sh_co_mask) != 0) +#define SH_VALID_ARCH_SET(SET) \ + (SH_VALID_BASE_ARCH_SET (SET) \ + && SH_VALID_MMU_ARCH_SET (SET) \ + && SH_VALID_CO_ARCH_SET (SET)) +#define SH_MERGE_ARCH_SET_VALID(SET1, SET2) \ + SH_VALID_ARCH_SET (SH_MERGE_ARCH_SET (SET1, SET2)) + +#define SH_ARCH_SET_HAS_FPU(SET) \ + (((SET) & (arch_sh_sp_fpu | arch_sh_dp_fpu)) != 0) +#define SH_ARCH_SET_HAS_DSP(SET) \ + (((SET) & arch_sh_has_dsp) != 0) + +/* This is returned from the functions below when an error occurs + (in addition to a call to BFD_FAIL). The value should allow + the tools to continue to function in most cases - there may + be some confusion between DSP and FPU etc. */ +#define SH_ARCH_UNKNOWN_ARCH 0xffffffff + +/* These are defined in bfd/cpu-sh.c . */ +unsigned int sh_get_arch_from_bfd_mach (unsigned long mach); +unsigned int sh_get_arch_up_from_bfd_mach (unsigned long mach); +unsigned long sh_get_bfd_mach_from_arch_set (unsigned int arch_set); +/* bfd_boolean sh_merge_bfd_arch (bfd *ibfd, bfd *obfd); */ + +/* Below are the 'architecture sets'. + They describe the following inheritance graph: + + SH1 + | + SH2 + .------------'|`--------------------. + / | \ +SH-DSP SH3-nommu SH2E + | |`--------. | + | | \ | + | SH3 SH4-nommu-nofpu | + | | | | + | .------------'|`----------+---------. | + |/ / \| + | | .-------' | + | |/ | +SH3-dsp SH4-nofpu SH3E + | |`--------------------. | + | | \| + | SH4A-nofpu SH4 + | .------------' `--------------------. | + |/ \| +SH4AL-dsp SH4A + +*/ + +/* Central branches */ +#define arch_sh1_up (arch_sh1 | arch_sh2_up) +#define arch_sh2_up (arch_sh2 | arch_sh2e_up | arch_sh2a_nofpu_up | arch_sh3_nommu_up | arch_sh_dsp_up) +#define arch_sh3_nommu_up (arch_sh3_nommu | arch_sh3_up | arch_sh4_nommu_nofpu_up) +#define arch_sh3_up (arch_sh3 | arch_sh3e_up | arch_sh3_dsp_up | arch_sh4_nofp_up) +#define arch_sh4_nommu_nofpu_up (arch_sh4_nommu_nofpu | arch_sh4_nofp_up) +#define arch_sh4_nofp_up (arch_sh4_nofpu | arch_sh4_up | arch_sh4a_nofp_up) +#define arch_sh4a_nofp_up (arch_sh4a_nofpu | arch_sh4a_up | arch_sh4al_dsp_up) + +/* Right branch */ +#define arch_sh2e_up (arch_sh2e | arch_sh2a_up | arch_sh3e_up) +#define arch_sh3e_up (arch_sh3e | arch_sh4_up) +#define arch_sh4_up (arch_sh4 | arch_sh4a_up) +#define arch_sh4a_up (arch_sh4a) + +/* Left branch */ +#define arch_sh_dsp_up (arch_sh_dsp | arch_sh3_dsp_up) +#define arch_sh3_dsp_up (arch_sh3_dsp | arch_sh4al_dsp_up) +#define arch_sh4al_dsp_up (arch_sh4al_dsp) + +/* SH 2a branched off SH2e, adding a lot but not all of SH4 and SH4a. */ +#define arch_sh2a_up (arch_sh2a) +#define arch_sh2a_nofpu_up (arch_sh2a_nofpu | arch_sh2a_up) + + +typedef struct +{ + const char *name; + sh_arg_type arg[4]; + sh_nibble_type nibbles[9]; + unsigned int arch; +} sh_opcode_info; + +#ifdef DEFINE_TABLE + +static const sh_opcode_info sh_table[] = + { +/* 0111nnnni8*1.... add #, */{"add",{A_IMM,A_REG_N},{HEX_7,REG_N,IMM0_8}, arch_sh1_up}, + +/* 0011nnnnmmmm1100 add , */{"add",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_C}, arch_sh1_up}, + +/* 0011nnnnmmmm1110 addc ,*/{"addc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_E}, arch_sh1_up}, + +/* 0011nnnnmmmm1111 addv ,*/{"addv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_F}, arch_sh1_up}, + +/* 11001001i8*1.... and #,R0 */{"and",{A_IMM,A_R0},{HEX_C,HEX_9,IMM0_8}, arch_sh1_up}, + +/* 0010nnnnmmmm1001 and , */{"and",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_9}, arch_sh1_up}, + +/* 11001101i8*1.... and.b #,@(R0,GBR)*/{"and.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_D,IMM0_8}, arch_sh1_up}, + +/* 1010i12......... bra */{"bra",{A_BDISP12},{HEX_A,BRANCH_12}, arch_sh1_up}, + +/* 1011i12......... bsr */{"bsr",{A_BDISP12},{HEX_B,BRANCH_12}, arch_sh1_up}, + +/* 10001001i8p1.... bt */{"bt",{A_BDISP8},{HEX_8,HEX_9,BRANCH_8}, arch_sh1_up}, + +/* 10001011i8p1.... bf */{"bf",{A_BDISP8},{HEX_8,HEX_B,BRANCH_8}, arch_sh1_up}, + +/* 10001101i8p1.... bt.s */{"bt.s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}, arch_sh2_up}, + +/* 10001101i8p1.... bt/s */{"bt/s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}, arch_sh2_up}, + +/* 10001111i8p1.... bf.s */{"bf.s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}, arch_sh2_up}, + +/* 10001111i8p1.... bf/s */{"bf/s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}, arch_sh2_up}, + +/* 0000000010001000 clrdmxy */{"clrdmxy",{0},{HEX_0,HEX_0,HEX_8,HEX_8}, arch_sh4al_dsp_up}, + +/* 0000000000101000 clrmac */{"clrmac",{0},{HEX_0,HEX_0,HEX_2,HEX_8}, arch_sh1_up}, + +/* 0000000001001000 clrs */{"clrs",{0},{HEX_0,HEX_0,HEX_4,HEX_8}, arch_sh1_up}, + +/* 0000000000001000 clrt */{"clrt",{0},{HEX_0,HEX_0,HEX_0,HEX_8}, arch_sh1_up}, + +/* 10001000i8*1.... cmp/eq #,R0 */{"cmp/eq",{A_IMM,A_R0},{HEX_8,HEX_8,IMM0_8}, arch_sh1_up}, + +/* 0011nnnnmmmm0000 cmp/eq ,*/{"cmp/eq",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_0}, arch_sh1_up}, + +/* 0011nnnnmmmm0011 cmp/ge ,*/{"cmp/ge",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_3}, arch_sh1_up}, + +/* 0011nnnnmmmm0111 cmp/gt ,*/{"cmp/gt",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_7}, arch_sh1_up}, + +/* 0011nnnnmmmm0110 cmp/hi ,*/{"cmp/hi",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_6}, arch_sh1_up}, + +/* 0011nnnnmmmm0010 cmp/hs ,*/{"cmp/hs",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_2}, arch_sh1_up}, + +/* 0100nnnn00010101 cmp/pl */{"cmp/pl",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_5}, arch_sh1_up}, + +/* 0100nnnn00010001 cmp/pz */{"cmp/pz",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_1}, arch_sh1_up}, + +/* 0010nnnnmmmm1100 cmp/str ,*/{"cmp/str",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_C}, arch_sh1_up}, + +/* 0010nnnnmmmm0111 div0s ,*/{"div0s",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_7}, arch_sh1_up}, + +/* 0000000000011001 div0u */{"div0u",{0},{HEX_0,HEX_0,HEX_1,HEX_9}, arch_sh1_up}, + +/* 0011nnnnmmmm0100 div1 ,*/{"div1",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_4}, arch_sh1_up}, + +/* 0110nnnnmmmm1110 exts.b ,*/{"exts.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_E}, arch_sh1_up}, + +/* 0110nnnnmmmm1111 exts.w ,*/{"exts.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_F}, arch_sh1_up}, + +/* 0110nnnnmmmm1100 extu.b ,*/{"extu.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_C}, arch_sh1_up}, + +/* 0110nnnnmmmm1101 extu.w ,*/{"extu.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_D}, arch_sh1_up}, + +/* 0000nnnn11100011 icbi @ */{"icbi",{A_IND_N},{HEX_0,REG_N,HEX_E,HEX_3}, arch_sh4a_nofp_up}, + +/* 0100nnnn00101011 jmp @ */{"jmp",{A_IND_N},{HEX_4,REG_N,HEX_2,HEX_B}, arch_sh1_up}, + +/* 0100nnnn00001011 jsr @ */{"jsr",{A_IND_N},{HEX_4,REG_N,HEX_0,HEX_B}, arch_sh1_up}, + +/* 0100nnnn00001110 ldc ,SR */{"ldc",{A_REG_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_E}, arch_sh1_up}, + +/* 0100nnnn00011110 ldc ,GBR */{"ldc",{A_REG_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_E}, arch_sh1_up}, + +/* 0100nnnn00111010 ldc ,SGR */{"ldc",{A_REG_N,A_SGR},{HEX_4,REG_N,HEX_3,HEX_A}, arch_sh4_nommu_nofpu_up}, + +/* 0100mmmm01001010 ldc ,TBR */{"ldc",{A_REG_M,A_TBR},{HEX_4,REG_M,HEX_4,HEX_A}, arch_sh2a_nofpu_up}, + +/* 0100nnnn00101110 ldc ,VBR */{"ldc",{A_REG_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_E}, arch_sh1_up}, + +/* 0100nnnn01011110 ldc ,MOD */{"ldc",{A_REG_N,A_MOD},{HEX_4,REG_N,HEX_5,HEX_E}, arch_sh_dsp_up}, + +/* 0100nnnn01111110 ldc ,RE */{"ldc",{A_REG_N,A_RE},{HEX_4,REG_N,HEX_7,HEX_E}, arch_sh_dsp_up}, + +/* 0100nnnn01101110 ldc ,RS */{"ldc",{A_REG_N,A_RS},{HEX_4,REG_N,HEX_6,HEX_E}, arch_sh_dsp_up}, + +/* 0100nnnn00111110 ldc ,SSR */{"ldc",{A_REG_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_E}, arch_sh3_nommu_up}, + +/* 0100nnnn01001110 ldc ,SPC */{"ldc",{A_REG_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_E}, arch_sh3_nommu_up}, + +/* 0100nnnn11111010 ldc ,DBR */{"ldc",{A_REG_N,A_DBR},{HEX_4,REG_N,HEX_F,HEX_A}, arch_sh4_nommu_nofpu_up}, + +/* 0100nnnn1xxx1110 ldc ,Rn_BANK */{"ldc",{A_REG_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_E}, arch_sh3_nommu_up}, + +/* 0100nnnn00000111 ldc.l @+,SR */{"ldc.l",{A_INC_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_7}, arch_sh1_up}, + +/* 0100nnnn00010111 ldc.l @+,GBR */{"ldc.l",{A_INC_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_7}, arch_sh1_up}, + +/* 0100nnnn00100111 ldc.l @+,VBR */{"ldc.l",{A_INC_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_7}, arch_sh1_up}, + +/* 0100nnnn00110110 ldc.l @+,SGR */{"ldc.l",{A_INC_N,A_SGR},{HEX_4,REG_N,HEX_3,HEX_6}, arch_sh4_nommu_nofpu_up}, + +/* 0100nnnn01010111 ldc.l @+,MOD */{"ldc.l",{A_INC_N,A_MOD},{HEX_4,REG_N,HEX_5,HEX_7}, arch_sh_dsp_up}, + +/* 0100nnnn01110111 ldc.l @+,RE */{"ldc.l",{A_INC_N,A_RE},{HEX_4,REG_N,HEX_7,HEX_7}, arch_sh_dsp_up}, + +/* 0100nnnn01100111 ldc.l @+,RS */{"ldc.l",{A_INC_N,A_RS},{HEX_4,REG_N,HEX_6,HEX_7}, arch_sh_dsp_up}, + +/* 0100nnnn00110111 ldc.l @+,SSR */{"ldc.l",{A_INC_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_7}, arch_sh3_nommu_up}, + +/* 0100nnnn01000111 ldc.l @+,SPC */{"ldc.l",{A_INC_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_7}, arch_sh3_nommu_up}, + +/* 0100nnnn11110110 ldc.l @+,DBR */{"ldc.l",{A_INC_N,A_DBR},{HEX_4,REG_N,HEX_F,HEX_6}, arch_sh4_nommu_nofpu_up}, + +/* 0100nnnn1xxx0111 ldc.l ,Rn_BANK */{"ldc.l",{A_INC_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_7}, arch_sh3_nommu_up}, + +/* 0100mmmm00110100 ldrc */{"ldrc",{A_REG_M},{HEX_4,REG_M,HEX_3,HEX_4}, arch_sh4al_dsp_up}, +/* 10001010i8*1.... ldrc # */{"ldrc",{A_IMM},{HEX_8,HEX_A,IMM0_8}, arch_sh4al_dsp_up}, + +/* 10001110i8p2.... ldre @(,PC) */{"ldre",{A_DISP_PC},{HEX_8,HEX_E,PCRELIMM_8BY2}, arch_sh_dsp_up}, + +/* 10001100i8p2.... ldrs @(,PC) */{"ldrs",{A_DISP_PC},{HEX_8,HEX_C,PCRELIMM_8BY2}, arch_sh_dsp_up}, + +/* 0100nnnn00001010 lds ,MACH */{"lds",{A_REG_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_A}, arch_sh1_up}, + +/* 0100nnnn00011010 lds ,MACL */{"lds",{A_REG_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_A}, arch_sh1_up}, + +/* 0100nnnn00101010 lds ,PR */{"lds",{A_REG_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_A}, arch_sh1_up}, + +/* 0100nnnn01101010 lds ,DSR */{"lds",{A_REG_N,A_DSR},{HEX_4,REG_N,HEX_6,HEX_A}, arch_sh_dsp_up}, + +/* 0100nnnn01111010 lds ,A0 */{"lds",{A_REG_N,A_A0},{HEX_4,REG_N,HEX_7,HEX_A}, arch_sh_dsp_up}, + +/* 0100nnnn10001010 lds ,X0 */{"lds",{A_REG_N,A_X0},{HEX_4,REG_N,HEX_8,HEX_A}, arch_sh_dsp_up}, + +/* 0100nnnn10011010 lds ,X1 */{"lds",{A_REG_N,A_X1},{HEX_4,REG_N,HEX_9,HEX_A}, arch_sh_dsp_up}, + +/* 0100nnnn10101010 lds ,Y0 */{"lds",{A_REG_N,A_Y0},{HEX_4,REG_N,HEX_A,HEX_A}, arch_sh_dsp_up}, + +/* 0100nnnn10111010 lds ,Y1 */{"lds",{A_REG_N,A_Y1},{HEX_4,REG_N,HEX_B,HEX_A}, arch_sh_dsp_up}, + +/* 0100nnnn01011010 lds ,FPUL */{"lds",{A_REG_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_A}, arch_sh2e_up}, + +/* 0100nnnn01101010 lds ,FPSCR */{"lds",{A_REG_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_A}, arch_sh2e_up}, + +/* 0100nnnn00000110 lds.l @+,MACH*/{"lds.l",{A_INC_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_6}, arch_sh1_up}, + +/* 0100nnnn00010110 lds.l @+,MACL*/{"lds.l",{A_INC_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_6}, arch_sh1_up}, + +/* 0100nnnn00100110 lds.l @+,PR */{"lds.l",{A_INC_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_6}, arch_sh1_up}, + +/* 0100nnnn01100110 lds.l @+,DSR */{"lds.l",{A_INC_N,A_DSR},{HEX_4,REG_N,HEX_6,HEX_6}, arch_sh_dsp_up}, + +/* 0100nnnn01110110 lds.l @+,A0 */{"lds.l",{A_INC_N,A_A0},{HEX_4,REG_N,HEX_7,HEX_6}, arch_sh_dsp_up}, + +/* 0100nnnn10000110 lds.l @+,X0 */{"lds.l",{A_INC_N,A_X0},{HEX_4,REG_N,HEX_8,HEX_6}, arch_sh_dsp_up}, + +/* 0100nnnn10010110 lds.l @+,X1 */{"lds.l",{A_INC_N,A_X1},{HEX_4,REG_N,HEX_9,HEX_6}, arch_sh_dsp_up}, + +/* 0100nnnn10100110 lds.l @+,Y0 */{"lds.l",{A_INC_N,A_Y0},{HEX_4,REG_N,HEX_A,HEX_6}, arch_sh_dsp_up}, + +/* 0100nnnn10110110 lds.l @+,Y1 */{"lds.l",{A_INC_N,A_Y1},{HEX_4,REG_N,HEX_B,HEX_6}, arch_sh_dsp_up}, + +/* 0100nnnn01010110 lds.l @+,FPUL*/{"lds.l",{A_INC_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_6}, arch_sh2e_up}, + +/* 0100nnnn01100110 lds.l @+,FPSCR*/{"lds.l",{A_INC_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_6}, arch_sh2e_up}, + +/* 0000000000111000 ldtlb */{"ldtlb",{0},{HEX_0,HEX_0,HEX_3,HEX_8}, arch_sh3_up}, + +/* 0100nnnnmmmm1111 mac.w @+,@+*/{"mac.w",{A_INC_M,A_INC_N},{HEX_4,REG_N,REG_M,HEX_F}, arch_sh1_up}, + +/* 1110nnnni8*1.... mov #, */{"mov",{A_IMM,A_REG_N},{HEX_E,REG_N,IMM0_8}, arch_sh1_up}, + +/* 0110nnnnmmmm0011 mov , */{"mov",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_3}, arch_sh1_up}, + +/* 0000nnnnmmmm0100 mov.b ,@(R0,)*/{"mov.b",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_4}, arch_sh1_up}, + +/* 0010nnnnmmmm0100 mov.b ,@-*/{"mov.b",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_4}, arch_sh1_up}, + +/* 0010nnnnmmmm0000 mov.b ,@*/{"mov.b",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_0}, arch_sh1_up}, + +/* 10000100mmmmi4*1 mov.b @(,),R0*/{"mov.b",{A_DISP_REG_M,A_R0},{HEX_8,HEX_4,REG_M,IMM0_4}, arch_sh1_up}, + +/* 11000100i8*1.... mov.b @(,GBR),R0*/{"mov.b",{A_DISP_GBR,A_R0},{HEX_C,HEX_4,IMM0_8}, arch_sh1_up}, + +/* 0000nnnnmmmm1100 mov.b @(R0,),*/{"mov.b",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_C}, arch_sh1_up}, + +/* 0110nnnnmmmm0100 mov.b @+,*/{"mov.b",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_4}, arch_sh1_up}, + +/* 0110nnnnmmmm0000 mov.b @,*/{"mov.b",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_0}, arch_sh1_up}, + +/* 10000000mmmmi4*1 mov.b R0,@(,)*/{"mov.b",{A_R0,A_DISP_REG_M},{HEX_8,HEX_0,REG_M,IMM1_4}, arch_sh1_up}, + +/* 11000000i8*1.... mov.b R0,@(,GBR)*/{"mov.b",{A_R0,A_DISP_GBR},{HEX_C,HEX_0,IMM1_8}, arch_sh1_up}, + +/* 0100nnnn10001011 mov.b R0,@+ */{"mov.b",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_8,HEX_B}, arch_sh2a_nofpu_up}, +/* 0100nnnn11001011 mov.b @-,R0 */{"mov.b",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_C,HEX_B}, arch_sh2a_nofpu_up}, +/* 0011nnnnmmmm0001 0000dddddddddddd mov.b ,@(,) */ +{"mov.b",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_0,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnnmmmm0001 0100dddddddddddd mov.b @(,), */ +{"mov.b",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_4,DISP0_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0001nnnnmmmmi4*4 mov.l ,@(,)*/{"mov.l",{ A_REG_M,A_DISP_REG_N},{HEX_1,REG_N,REG_M,IMM1_4BY4}, arch_sh1_up}, + +/* 0000nnnnmmmm0110 mov.l ,@(R0,)*/{"mov.l",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_6}, arch_sh1_up}, + +/* 0010nnnnmmmm0110 mov.l ,@-*/{"mov.l",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_6}, arch_sh1_up}, + +/* 0010nnnnmmmm0010 mov.l ,@*/{"mov.l",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_2}, arch_sh1_up}, + +/* 0101nnnnmmmmi4*4 mov.l @(,),*/{"mov.l",{A_DISP_REG_M,A_REG_N},{HEX_5,REG_N,REG_M,IMM0_4BY4}, arch_sh1_up}, + +/* 11000110i8*4.... mov.l @(,GBR),R0*/{"mov.l",{A_DISP_GBR,A_R0},{HEX_C,HEX_6,IMM0_8BY4}, arch_sh1_up}, + +/* 1101nnnni8p4.... mov.l @(,PC),*/{"mov.l",{A_DISP_PC,A_REG_N},{HEX_D,REG_N,PCRELIMM_8BY4}, arch_sh1_up}, + +/* 0000nnnnmmmm1110 mov.l @(R0,),*/{"mov.l",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_E}, arch_sh1_up}, + +/* 0110nnnnmmmm0110 mov.l @+,*/{"mov.l",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_6}, arch_sh1_up}, + +/* 0110nnnnmmmm0010 mov.l @,*/{"mov.l",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_2}, arch_sh1_up}, + +/* 11000010i8*4.... mov.l R0,@(,GBR)*/{"mov.l",{A_R0,A_DISP_GBR},{HEX_C,HEX_2,IMM1_8BY4}, arch_sh1_up}, + +/* 0100nnnn10101011 mov.l R0,@+ */{"mov.l",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_A,HEX_B}, arch_sh2a_nofpu_up}, +/* 0100nnnn11001011 mov.l @-,R0 */{"mov.l",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_B}, arch_sh2a_nofpu_up}, +/* 0011nnnnmmmm0001 0010dddddddddddd mov.l ,@(,) */ +{"mov.l",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_2,DISP1_12BY4}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnnmmmm0001 0110dddddddddddd mov.l @(,), */ +{"mov.l",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_6,DISP0_12BY4}, arch_sh2a_nofpu_up | arch_op32}, +/* 0000nnnnmmmm0101 mov.w ,@(R0,)*/{"mov.w",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_5}, arch_sh1_up}, + +/* 0010nnnnmmmm0101 mov.w ,@-*/{"mov.w",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_5}, arch_sh1_up}, + +/* 0010nnnnmmmm0001 mov.w ,@*/{"mov.w",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_1}, arch_sh1_up}, + +/* 10000101mmmmi4*2 mov.w @(,),R0*/{"mov.w",{A_DISP_REG_M,A_R0},{HEX_8,HEX_5,REG_M,IMM0_4BY2}, arch_sh1_up}, + +/* 11000101i8*2.... mov.w @(,GBR),R0*/{"mov.w",{A_DISP_GBR,A_R0},{HEX_C,HEX_5,IMM0_8BY2}, arch_sh1_up}, + +/* 1001nnnni8p2.... mov.w @(,PC),*/{"mov.w",{A_DISP_PC,A_REG_N},{HEX_9,REG_N,PCRELIMM_8BY2}, arch_sh1_up}, + +/* 0000nnnnmmmm1101 mov.w @(R0,),*/{"mov.w",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_D}, arch_sh1_up}, + +/* 0110nnnnmmmm0101 mov.w @+,*/{"mov.w",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_5}, arch_sh1_up}, + +/* 0110nnnnmmmm0001 mov.w @,*/{"mov.w",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_1}, arch_sh1_up}, + +/* 10000001mmmmi4*2 mov.w R0,@(,)*/{"mov.w",{A_R0,A_DISP_REG_M},{HEX_8,HEX_1,REG_M,IMM1_4BY2}, arch_sh1_up}, + +/* 11000001i8*2.... mov.w R0,@(,GBR)*/{"mov.w",{A_R0,A_DISP_GBR},{HEX_C,HEX_1,IMM1_8BY2}, arch_sh1_up}, + +/* 0100nnnn10011011 mov.w R0,@+ */{"mov.w",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_9,HEX_B}, arch_sh2a_nofpu_up}, +/* 0100nnnn11011011 mov.w @-,R0 */{"mov.w",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_D,HEX_B}, arch_sh2a_nofpu_up}, +/* 0011nnnnmmmm0001 0001dddddddddddd mov.w ,@(,) */ +{"mov.w",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_1,DISP1_12BY2}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnnmmmm0001 0101dddddddddddd mov.w @(,), */ +{"mov.w",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_5,DISP0_12BY2}, arch_sh2a_nofpu_up | arch_op32}, +/* 11000111i8p4.... mova @(,PC),R0*/{"mova",{A_DISP_PC,A_R0},{HEX_C,HEX_7,PCRELIMM_8BY4}, arch_sh1_up}, +/* 0000nnnn11000011 movca.l R0,@ */{"movca.l",{A_R0,A_IND_N},{HEX_0,REG_N,HEX_C,HEX_3}, arch_sh4_nommu_nofpu_up}, + +/* 0000nnnn01110011 movco.l r0,@ */{"movco.l",{A_R0,A_IND_N},{HEX_0,REG_N,HEX_7,HEX_3}, arch_sh4a_nofp_up}, +/* 0000mmmm01100011 movli.l @,r0 */{"movli.l",{A_IND_M,A_R0},{HEX_0,REG_M,HEX_6,HEX_3}, arch_sh4a_nofp_up}, + +/* 0000nnnn00101001 movt */{"movt",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_9}, arch_sh1_up}, + +/* 0100mmmm10101001 movua.l @,r0 */{"movua.l",{A_IND_M,A_R0},{HEX_4,REG_M,HEX_A,HEX_9}, arch_sh4a_nofp_up}, +/* 0100mmmm11101001 movua.l @+,r0 */{"movua.l",{A_INC_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_9}, arch_sh4a_nofp_up}, + +/* 0010nnnnmmmm1111 muls.w ,*/{"muls.w",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_F}, arch_sh1_up}, +/* 0010nnnnmmmm1111 muls ,*/{"muls",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_F}, arch_sh1_up}, + +/* 0000nnnnmmmm0111 mul.l ,*/{"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}, arch_sh2_up}, + +/* 0010nnnnmmmm1110 mulu.w ,*/{"mulu.w",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_E}, arch_sh1_up}, +/* 0010nnnnmmmm1110 mulu ,*/{"mulu",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_E}, arch_sh1_up}, + +/* 0110nnnnmmmm1011 neg , */{"neg",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_B}, arch_sh1_up}, + +/* 0110nnnnmmmm1010 negc ,*/{"negc",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_A}, arch_sh1_up}, + +/* 0000000000001001 nop */{"nop",{0},{HEX_0,HEX_0,HEX_0,HEX_9}, arch_sh1_up}, + +/* 0110nnnnmmmm0111 not , */{"not",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_7}, arch_sh1_up}, +/* 0000nnnn10010011 ocbi @ */{"ocbi",{A_IND_N},{HEX_0,REG_N,HEX_9,HEX_3}, arch_sh4_nommu_nofpu_up}, + +/* 0000nnnn10100011 ocbp @ */{"ocbp",{A_IND_N},{HEX_0,REG_N,HEX_A,HEX_3}, arch_sh4_nommu_nofpu_up}, + +/* 0000nnnn10110011 ocbwb @ */{"ocbwb",{A_IND_N},{HEX_0,REG_N,HEX_B,HEX_3}, arch_sh4_nommu_nofpu_up}, + + +/* 11001011i8*1.... or #,R0 */{"or",{A_IMM,A_R0},{HEX_C,HEX_B,IMM0_8}, arch_sh1_up}, + +/* 0010nnnnmmmm1011 or , */{"or",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_B}, arch_sh1_up}, + +/* 11001111i8*1.... or.b #,@(R0,GBR)*/{"or.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_F,IMM0_8}, arch_sh1_up}, + +/* 0000nnnn10000011 pref @ */{"pref",{A_IND_N},{HEX_0,REG_N,HEX_8,HEX_3}, arch_sh4_nommu_nofpu_up | arch_sh2a_nofpu_up}, + +/* 0000nnnn11010011 prefi @ */{"prefi",{A_IND_N},{HEX_0,REG_N,HEX_D,HEX_3}, arch_sh4a_nofp_up}, + +/* 0100nnnn00100100 rotcl */{"rotcl",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_4}, arch_sh1_up}, + +/* 0100nnnn00100101 rotcr */{"rotcr",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_5}, arch_sh1_up}, + +/* 0100nnnn00000100 rotl */{"rotl",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_4}, arch_sh1_up}, + +/* 0100nnnn00000101 rotr */{"rotr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_5}, arch_sh1_up}, + +/* 0000000000101011 rte */{"rte",{0},{HEX_0,HEX_0,HEX_2,HEX_B}, arch_sh1_up}, + +/* 0000000000001011 rts */{"rts",{0},{HEX_0,HEX_0,HEX_0,HEX_B}, arch_sh1_up}, + +/* 0000000010011000 setdmx */{"setdmx",{0},{HEX_0,HEX_0,HEX_9,HEX_8}, arch_sh4al_dsp_up}, +/* 0000000011001000 setdmy */{"setdmy",{0},{HEX_0,HEX_0,HEX_C,HEX_8}, arch_sh4al_dsp_up}, + +/* 0000000001011000 sets */{"sets",{0},{HEX_0,HEX_0,HEX_5,HEX_8}, arch_sh1_up}, +/* 0000000000011000 sett */{"sett",{0},{HEX_0,HEX_0,HEX_1,HEX_8}, arch_sh1_up}, + +/* 0100nnnn00010100 setrc */{"setrc",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_4}, arch_sh_dsp_up}, + +/* 10000010i8*1.... setrc # */{"setrc",{A_IMM},{HEX_8,HEX_2,IMM0_8}, arch_sh_dsp_up}, + +/* repeat start end */{"repeat",{A_DISP_PC,A_DISP_PC,A_REG_N},{REPEAT,REG_N,HEX_1,HEX_4}, arch_sh_dsp_up}, + +/* repeat start end # */{"repeat",{A_DISP_PC,A_DISP_PC,A_IMM},{REPEAT,HEX_2,IMM0_8,HEX_8}, arch_sh_dsp_up}, + +/* 0100nnnnmmmm1100 shad ,*/{"shad",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_C}, arch_sh3_nommu_up | arch_sh2a_nofpu_up}, + +/* 0100nnnnmmmm1101 shld ,*/{"shld",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_D}, arch_sh3_nommu_up | arch_sh2a_nofpu_up}, + +/* 0100nnnn00100000 shal */{"shal",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_0}, arch_sh1_up}, + +/* 0100nnnn00100001 shar */{"shar",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_1}, arch_sh1_up}, + +/* 0100nnnn00000000 shll */{"shll",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_0}, arch_sh1_up}, + +/* 0100nnnn00101000 shll16 */{"shll16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_8}, arch_sh1_up}, + +/* 0100nnnn00001000 shll2 */{"shll2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_8}, arch_sh1_up}, + +/* 0100nnnn00011000 shll8 */{"shll8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_8}, arch_sh1_up}, + +/* 0100nnnn00000001 shlr */{"shlr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_1}, arch_sh1_up}, + +/* 0100nnnn00101001 shlr16 */{"shlr16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_9}, arch_sh1_up}, + +/* 0100nnnn00001001 shlr2 */{"shlr2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_9}, arch_sh1_up}, + +/* 0100nnnn00011001 shlr8 */{"shlr8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_9}, arch_sh1_up}, + +/* 0000000000011011 sleep */{"sleep",{0},{HEX_0,HEX_0,HEX_1,HEX_B}, arch_sh1_up}, + +/* 0000nnnn00000010 stc SR, */{"stc",{A_SR,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_2}, arch_sh1_up}, + +/* 0000nnnn00010010 stc GBR, */{"stc",{A_GBR,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_2}, arch_sh1_up}, + +/* 0000nnnn00100010 stc VBR, */{"stc",{A_VBR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_2}, arch_sh1_up}, + +/* 0000nnnn01010010 stc MOD, */{"stc",{A_MOD,A_REG_N},{HEX_0,REG_N,HEX_5,HEX_2}, arch_sh_dsp_up}, + +/* 0000nnnn01110010 stc RE, */{"stc",{A_RE,A_REG_N},{HEX_0,REG_N,HEX_7,HEX_2}, arch_sh_dsp_up}, + +/* 0000nnnn01100010 stc RS, */{"stc",{A_RS,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_2}, arch_sh_dsp_up}, + +/* 0000nnnn00110010 stc SSR, */{"stc",{A_SSR,A_REG_N},{HEX_0,REG_N,HEX_3,HEX_2}, arch_sh3_nommu_up}, + +/* 0000nnnn01000010 stc SPC, */{"stc",{A_SPC,A_REG_N},{HEX_0,REG_N,HEX_4,HEX_2}, arch_sh3_nommu_up}, + +/* 0000nnnn00111010 stc SGR, */{"stc",{A_SGR,A_REG_N},{HEX_0,REG_N,HEX_3,HEX_A}, arch_sh4_nommu_nofpu_up}, + +/* 0000nnnn11111010 stc DBR, */{"stc",{A_DBR,A_REG_N},{HEX_0,REG_N,HEX_F,HEX_A}, arch_sh4_nommu_nofpu_up}, + +/* 0000nnnn1xxx0010 stc Rn_BANK, */{"stc",{A_REG_B,A_REG_N},{HEX_0,REG_N,REG_B,HEX_2}, arch_sh3_nommu_up}, + +/* 0000nnnn01001010 stc TBR, */ {"stc",{A_TBR,A_REG_N},{HEX_0,REG_N,HEX_4,HEX_A}, arch_sh2a_nofpu_up}, + +/* 0100nnnn00000011 stc.l SR,@- */{"stc.l",{A_SR,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_3}, arch_sh1_up}, + +/* 0100nnnn00100011 stc.l VBR,@- */{"stc.l",{A_VBR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_3}, arch_sh1_up}, + +/* 0100nnnn01010011 stc.l MOD,@- */{"stc.l",{A_MOD,A_DEC_N},{HEX_4,REG_N,HEX_5,HEX_3}, arch_sh_dsp_up}, + +/* 0100nnnn01110011 stc.l RE,@- */{"stc.l",{A_RE,A_DEC_N},{HEX_4,REG_N,HEX_7,HEX_3}, arch_sh_dsp_up}, + +/* 0100nnnn01100011 stc.l RS,@- */{"stc.l",{A_RS,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_3}, arch_sh_dsp_up}, + +/* 0100nnnn00110011 stc.l SSR,@- */{"stc.l",{A_SSR,A_DEC_N},{HEX_4,REG_N,HEX_3,HEX_3}, arch_sh3_nommu_up}, + +/* 0100nnnn01000011 stc.l SPC,@- */{"stc.l",{A_SPC,A_DEC_N},{HEX_4,REG_N,HEX_4,HEX_3}, arch_sh3_nommu_up}, + +/* 0100nnnn00010011 stc.l GBR,@- */{"stc.l",{A_GBR,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_3}, arch_sh1_up}, + +/* 0100nnnn00110010 stc.l SGR,@- */{"stc.l",{A_SGR,A_DEC_N},{HEX_4,REG_N,HEX_3,HEX_2}, arch_sh4_nommu_nofpu_up}, + +/* 0100nnnn11110010 stc.l DBR,@- */{"stc.l",{A_DBR,A_DEC_N},{HEX_4,REG_N,HEX_F,HEX_2}, arch_sh4_nommu_nofpu_up}, + +/* 0100nnnn1xxx0011 stc.l Rn_BANK,@- */{"stc.l",{A_REG_B,A_DEC_N},{HEX_4,REG_N,REG_B,HEX_3}, arch_sh3_nommu_up}, + +/* 0000nnnn00001010 sts MACH, */{"sts",{A_MACH,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_A}, arch_sh1_up}, + +/* 0000nnnn00011010 sts MACL, */{"sts",{A_MACL,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_A}, arch_sh1_up}, + +/* 0000nnnn00101010 sts PR, */{"sts",{A_PR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_A}, arch_sh1_up}, + +/* 0000nnnn01101010 sts DSR, */{"sts",{A_DSR,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_A}, arch_sh_dsp_up}, + +/* 0000nnnn01111010 sts A0, */{"sts",{A_A0,A_REG_N},{HEX_0,REG_N,HEX_7,HEX_A}, arch_sh_dsp_up}, + +/* 0000nnnn10001010 sts X0, */{"sts",{A_X0,A_REG_N},{HEX_0,REG_N,HEX_8,HEX_A}, arch_sh_dsp_up}, + +/* 0000nnnn10011010 sts X1, */{"sts",{A_X1,A_REG_N},{HEX_0,REG_N,HEX_9,HEX_A}, arch_sh_dsp_up}, + +/* 0000nnnn10101010 sts Y0, */{"sts",{A_Y0,A_REG_N},{HEX_0,REG_N,HEX_A,HEX_A}, arch_sh_dsp_up}, + +/* 0000nnnn10111010 sts Y1, */{"sts",{A_Y1,A_REG_N},{HEX_0,REG_N,HEX_B,HEX_A}, arch_sh_dsp_up}, + +/* 0000nnnn01011010 sts FPUL, */{"sts",{FPUL_M,A_REG_N},{HEX_0,REG_N,HEX_5,HEX_A}, arch_sh2e_up}, + +/* 0000nnnn01101010 sts FPSCR, */{"sts",{FPSCR_M,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_A}, arch_sh2e_up}, + +/* 0100nnnn00000010 sts.l MACH,@-*/{"sts.l",{A_MACH,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_2}, arch_sh1_up}, + +/* 0100nnnn00010010 sts.l MACL,@-*/{"sts.l",{A_MACL,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_2}, arch_sh1_up}, + +/* 0100nnnn00100010 sts.l PR,@- */{"sts.l",{A_PR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_2}, arch_sh1_up}, + +/* 0100nnnn01100110 sts.l DSR,@- */{"sts.l",{A_DSR,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_2}, arch_sh_dsp_up}, + +/* 0100nnnn01110110 sts.l A0,@- */{"sts.l",{A_A0,A_DEC_N},{HEX_4,REG_N,HEX_7,HEX_2}, arch_sh_dsp_up}, + +/* 0100nnnn10000110 sts.l X0,@- */{"sts.l",{A_X0,A_DEC_N},{HEX_4,REG_N,HEX_8,HEX_2}, arch_sh_dsp_up}, + +/* 0100nnnn10010110 sts.l X1,@- */{"sts.l",{A_X1,A_DEC_N},{HEX_4,REG_N,HEX_9,HEX_2}, arch_sh_dsp_up}, + +/* 0100nnnn10100110 sts.l Y0,@- */{"sts.l",{A_Y0,A_DEC_N},{HEX_4,REG_N,HEX_A,HEX_2}, arch_sh_dsp_up}, + +/* 0100nnnn10110110 sts.l Y1,@- */{"sts.l",{A_Y1,A_DEC_N},{HEX_4,REG_N,HEX_B,HEX_2}, arch_sh_dsp_up}, + +/* 0100nnnn01010010 sts.l FPUL,@-*/{"sts.l",{FPUL_M,A_DEC_N},{HEX_4,REG_N,HEX_5,HEX_2}, arch_sh2e_up}, + +/* 0100nnnn01100010 sts.l FPSCR,@-*/{"sts.l",{FPSCR_M,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_2}, arch_sh2e_up}, + +/* 0011nnnnmmmm1000 sub , */{"sub",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_8}, arch_sh1_up}, + +/* 0011nnnnmmmm1010 subc ,*/{"subc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_A}, arch_sh1_up}, + +/* 0011nnnnmmmm1011 subv ,*/{"subv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_B}, arch_sh1_up}, + +/* 0110nnnnmmmm1000 swap.b ,*/{"swap.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_8}, arch_sh1_up}, + +/* 0110nnnnmmmm1001 swap.w ,*/{"swap.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_9}, arch_sh1_up}, + +/* 0000000010101011 synco */{"synco",{0},{HEX_0,HEX_0,HEX_A,HEX_B}, arch_sh4a_nofp_up}, + +/* 0100nnnn00011011 tas.b @ */{"tas.b",{A_IND_N},{HEX_4,REG_N,HEX_1,HEX_B}, arch_sh1_up}, + +/* 11000011i8*1.... trapa # */{"trapa",{A_IMM},{HEX_C,HEX_3,IMM0_8}, arch_sh1_up}, + +/* 11001000i8*1.... tst #,R0 */{"tst",{A_IMM,A_R0},{HEX_C,HEX_8,IMM0_8}, arch_sh1_up}, + +/* 0010nnnnmmmm1000 tst , */{"tst",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_8}, arch_sh1_up}, + +/* 11001100i8*1.... tst.b #,@(R0,GBR)*/{"tst.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_C,IMM0_8}, arch_sh1_up}, + +/* 11001010i8*1.... xor #,R0 */{"xor",{A_IMM,A_R0},{HEX_C,HEX_A,IMM0_8}, arch_sh1_up}, + +/* 0010nnnnmmmm1010 xor , */{"xor",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_A}, arch_sh1_up}, + +/* 11001110i8*1.... xor.b #,@(R0,GBR)*/{"xor.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_E,IMM0_8}, arch_sh1_up}, + +/* 0010nnnnmmmm1101 xtrct ,*/{"xtrct",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_D}, arch_sh1_up}, + +/* 0000nnnnmmmm0111 mul.l ,*/{"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}, arch_sh1_up}, + +/* 0100nnnn00010000 dt */{"dt",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_0}, arch_sh2_up}, + +/* 0011nnnnmmmm1101 dmuls.l ,*/{"dmuls.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_D}, arch_sh2_up}, + +/* 0011nnnnmmmm0101 dmulu.l ,*/{"dmulu.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_5}, arch_sh2_up}, + +/* 0000nnnnmmmm1111 mac.l @+,@+*/{"mac.l",{A_INC_M,A_INC_N},{HEX_0,REG_N,REG_M,HEX_F}, arch_sh2_up}, + +/* 0000nnnn00100011 braf */{"braf",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_3}, arch_sh2_up}, + +/* 0000nnnn00000011 bsrf */{"bsrf",{A_REG_N},{HEX_0,REG_N,HEX_0,HEX_3}, arch_sh2_up}, + +/* 111101nnmmmm0000 movs.w @-, */ {"movs.w",{A_DEC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_0}, arch_sh_dsp_up}, + +/* 111101nnmmmm0001 movs.w @, */ {"movs.w",{A_IND_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_4}, arch_sh_dsp_up}, + +/* 111101nnmmmm0010 movs.w @+, */ {"movs.w",{A_INC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_8}, arch_sh_dsp_up}, + +/* 111101nnmmmm0011 movs.w @+r8, */ {"movs.w",{AS_PMOD_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_C}, arch_sh_dsp_up}, + +/* 111101nnmmmm0100 movs.w ,@- */ {"movs.w",{DSP_REG_M,A_DEC_N},{HEX_F,SDT_REG_N,REG_M,HEX_1}, arch_sh_dsp_up}, + +/* 111101nnmmmm0101 movs.w ,@ */ {"movs.w",{DSP_REG_M,A_IND_N},{HEX_F,SDT_REG_N,REG_M,HEX_5}, arch_sh_dsp_up}, + +/* 111101nnmmmm0110 movs.w ,@+ */ {"movs.w",{DSP_REG_M,A_INC_N},{HEX_F,SDT_REG_N,REG_M,HEX_9}, arch_sh_dsp_up}, + +/* 111101nnmmmm0111 movs.w ,@+r8 */ {"movs.w",{DSP_REG_M,AS_PMOD_N},{HEX_F,SDT_REG_N,REG_M,HEX_D}, arch_sh_dsp_up}, + +/* 111101nnmmmm1000 movs.l @-, */ {"movs.l",{A_DEC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_2}, arch_sh_dsp_up}, + +/* 111101nnmmmm1001 movs.l @, */ {"movs.l",{A_IND_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_6}, arch_sh_dsp_up}, + +/* 111101nnmmmm1010 movs.l @+, */ {"movs.l",{A_INC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_A}, arch_sh_dsp_up}, + +/* 111101nnmmmm1011 movs.l @+r8, */ {"movs.l",{AS_PMOD_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_E}, arch_sh_dsp_up}, + +/* 111101nnmmmm1100 movs.l ,@- */ {"movs.l",{DSP_REG_M,A_DEC_N},{HEX_F,SDT_REG_N,REG_M,HEX_3}, arch_sh_dsp_up}, + +/* 111101nnmmmm1101 movs.l ,@ */ {"movs.l",{DSP_REG_M,A_IND_N},{HEX_F,SDT_REG_N,REG_M,HEX_7}, arch_sh_dsp_up}, + +/* 111101nnmmmm1110 movs.l ,@+ */ {"movs.l",{DSP_REG_M,A_INC_N},{HEX_F,SDT_REG_N,REG_M,HEX_B}, arch_sh_dsp_up}, + +/* 111101nnmmmm1111 movs.l ,@+r8 */ {"movs.l",{DSP_REG_M,AS_PMOD_N},{HEX_F,SDT_REG_N,REG_M,HEX_F}, arch_sh_dsp_up}, + +/* 0*0*0*00** nopx */ {"nopx",{0},{PPI,NOPX}, arch_sh_dsp_up}, +/* *0*0*0**00 nopy */ {"nopy",{0},{PPI,NOPY}, arch_sh_dsp_up}, +/* n*m*0*01** movx.w @, */ {"movx.w",{AX_IND_N,DSP_REG_X},{PPI,MOVX,HEX_1}, arch_sh_dsp_up}, +/* n*m*0*10** movx.w @+, */ {"movx.w",{AX_INC_N,DSP_REG_X},{PPI,MOVX,HEX_2}, arch_sh_dsp_up}, +/* n*m*0*11** movx.w @+r8, */ {"movx.w",{AX_PMOD_N,DSP_REG_X},{PPI,MOVX,HEX_3}, arch_sh_dsp_up}, +/* n*m*1*01** movx.w ,@ */ {"movx.w",{DSP_REG_A_M,AX_IND_N},{PPI,MOVX,HEX_9}, arch_sh_dsp_up}, +/* n*m*1*10** movx.w ,@+ */ {"movx.w",{DSP_REG_A_M,AX_INC_N},{PPI,MOVX,HEX_A}, arch_sh_dsp_up}, +/* n*m*1*11** movx.w ,@+r8 */ {"movx.w",{DSP_REG_A_M,AX_PMOD_N},{PPI,MOVX,HEX_B}, arch_sh_dsp_up}, + +/* nnmm000100 movx.w @, */ {"movx.w",{AXY_IND_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_4}, arch_sh4al_dsp_up}, +/* nnmm001000 movx.w @+, */{"movx.w",{AXY_INC_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_8}, arch_sh4al_dsp_up}, +/* nnmm001100 movx.w @+r8, */{"movx.w",{AXY_PMOD_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_C}, arch_sh4al_dsp_up}, +/* nnmm100100 movx.w ,@ */ {"movx.w",{DSP_REG_AX,AXY_IND_N},{PPI,MOVX_NOPY,HEX_2,HEX_4}, arch_sh4al_dsp_up}, +/* nnmm101000 movx.w ,@+ */{"movx.w",{DSP_REG_AX,AXY_INC_N},{PPI,MOVX_NOPY,HEX_2,HEX_8}, arch_sh4al_dsp_up}, +/* nnmm101100 movx.w ,@+r8 */{"movx.w",{DSP_REG_AX,AXY_PMOD_N},{PPI,MOVX_NOPY,HEX_2,HEX_C}, arch_sh4al_dsp_up}, + +/* nnmm010100 movx.l @, */ {"movx.l",{AXY_IND_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_4}, arch_sh4al_dsp_up}, +/* nnmm011000 movx.l @+, */{"movx.l",{AXY_INC_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_8}, arch_sh4al_dsp_up}, +/* nnmm011100 movx.l @+r8, */{"movx.l",{AXY_PMOD_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_C}, arch_sh4al_dsp_up}, +/* nnmm110100 movx.l ,@ */ {"movx.l",{DSP_REG_AX,AXY_IND_N},{PPI,MOVX_NOPY,HEX_3,HEX_4}, arch_sh4al_dsp_up}, +/* nnmm111000 movx.l ,@+ */{"movx.l",{DSP_REG_AX,AXY_INC_N},{PPI,MOVX_NOPY,HEX_3,HEX_8}, arch_sh4al_dsp_up}, +/* nnmm111100 movx.l ,@+r8 */{"movx.l",{DSP_REG_AX,AXY_PMOD_N},{PPI,MOVX_NOPY,HEX_3,HEX_C}, arch_sh4al_dsp_up}, + +/* *n*m*0**01 movy.w @, */ {"movy.w",{AY_IND_N,DSP_REG_Y},{PPI,MOVY,HEX_1}, arch_sh_dsp_up}, +/* *n*m*0**10 movy.w @+, */ {"movy.w",{AY_INC_N,DSP_REG_Y},{PPI,MOVY,HEX_2}, arch_sh_dsp_up}, +/* *n*m*0**11 movy.w @+r9, */ {"movy.w",{AY_PMOD_N,DSP_REG_Y},{PPI,MOVY,HEX_3}, arch_sh_dsp_up}, +/* *n*m*1**01 movy.w ,@ */ {"movy.w",{DSP_REG_A_M,AY_IND_N},{PPI,MOVY,HEX_9}, arch_sh_dsp_up}, +/* *n*m*1**10 movy.w ,@+ */ {"movy.w",{DSP_REG_A_M,AY_INC_N},{PPI,MOVY,HEX_A}, arch_sh_dsp_up}, +/* *n*m*1**11 movy.w ,@+r9 */ {"movy.w",{DSP_REG_A_M,AY_PMOD_N},{PPI,MOVY,HEX_B}, arch_sh_dsp_up}, + +/* nnmm000001 movy.w @, */ {"movy.w",{AYX_IND_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_1}, arch_sh4al_dsp_up}, +/* nnmm000010 movy.w @+, */{"movy.w",{AYX_INC_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_2}, arch_sh4al_dsp_up}, +/* nnmm000011 movy.w @+r8, */{"movy.w",{AYX_PMOD_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_3}, arch_sh4al_dsp_up}, +/* nnmm010001 movy.w ,@ */ {"movy.w",{DSP_REG_AY,AYX_IND_N},{PPI,MOVY_NOPX,HEX_1,HEX_1}, arch_sh4al_dsp_up}, +/* nnmm010010 movy.w ,@+ */{"movy.w",{DSP_REG_AY,AYX_INC_N},{PPI,MOVY_NOPX,HEX_1,HEX_2}, arch_sh4al_dsp_up}, +/* nnmm010011 movy.w ,@+r8 */{"movy.w",{DSP_REG_AY,AYX_PMOD_N},{PPI,MOVY_NOPX,HEX_1,HEX_3}, arch_sh4al_dsp_up}, + +/* nnmm100001 movy.l @, */ {"movy.l",{AYX_IND_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_1}, arch_sh4al_dsp_up}, +/* nnmm100010 movy.l @+, */{"movy.l",{AYX_INC_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_2}, arch_sh4al_dsp_up}, +/* nnmm100011 movy.l @+r8, */{"movy.l",{AYX_PMOD_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_3}, arch_sh4al_dsp_up}, +/* nnmm110001 movy.l ,@ */ {"movy.l",{DSP_REG_AY,AYX_IND_N},{PPI,MOVY_NOPX,HEX_3,HEX_1}, arch_sh4al_dsp_up}, +/* nnmm110010 movy.l ,@+ */{"movy.l",{DSP_REG_AY,AYX_INC_N},{PPI,MOVY_NOPX,HEX_3,HEX_2}, arch_sh4al_dsp_up}, +/* nnmm110011 movy.l ,@+r8 */{"movy.l",{DSP_REG_AY,AYX_PMOD_N},{PPI,MOVY_NOPX,HEX_3,HEX_3}, arch_sh4al_dsp_up}, + +/* 01aaeeffxxyyggnn pmuls Se,Sf,Dg */ {"pmuls",{DSP_REG_E,DSP_REG_F,DSP_REG_G},{PPI,PMUL}, arch_sh_dsp_up}, +/* 10100000xxyynnnn psubc ,, */ +{"psubc",{DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_A,HEX_0}, arch_sh_dsp_up}, +/* 10110000xxyynnnn paddc ,, */ +{"paddc",{DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_B,HEX_0}, arch_sh_dsp_up}, +/* 10000100xxyynnnn pcmp , */ +{"pcmp", {DSP_REG_X,DSP_REG_Y},{PPI,PPI3,HEX_8,HEX_4}, arch_sh_dsp_up}, +/* 10100100xxyynnnn pwsb ,, */ +{"pwsb", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_A,HEX_4}, arch_sh_dsp_up}, +/* 10110100xxyynnnn pwad ,, */ +{"pwad", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_B,HEX_4}, arch_sh_dsp_up}, +/* 10001000xxyynnnn pabs , */ +{"pabs", {DSP_REG_X,DSP_REG_N},{PPI,PPI3NC,HEX_8,HEX_8}, arch_sh_dsp_up}, +/* 1000100!xx01nnnn pabs , */ +{"pabs", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_9,HEX_1}, arch_sh4al_dsp_up}, +/* 10101000xxyynnnn pabs , */ +{"pabs", {DSP_REG_Y,DSP_REG_N},{PPI,PPI3NC,HEX_A,HEX_8}, arch_sh_dsp_up}, +/* 1010100!01yynnnn pabs , */ +{"pabs", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_9,HEX_4}, arch_sh4al_dsp_up}, +/* 10011000xxyynnnn prnd , */ +{"prnd", {DSP_REG_X,DSP_REG_N},{PPI,PPI3NC,HEX_9,HEX_8}, arch_sh_dsp_up}, +/* 1001100!xx01nnnn prnd , */ +{"prnd", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_9,HEX_1}, arch_sh4al_dsp_up}, +/* 10111000xxyynnnn prnd , */ +{"prnd", {DSP_REG_Y,DSP_REG_N},{PPI,PPI3NC,HEX_B,HEX_8}, arch_sh_dsp_up}, +/* 1011100!01yynnnn prnd , */ +{"prnd", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_9,HEX_4}, arch_sh4al_dsp_up}, + +{"dct",{0},{PPI,PDC,HEX_1}, arch_sh_dsp_up}, +{"dcf",{0},{PPI,PDC,HEX_2}, arch_sh_dsp_up}, + +/* 10000001xxyynnnn pshl ,, */ +{"pshl", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_1}, arch_sh_dsp_up}, +/* 00000iiiiiiinnnn pshl #, */ {"pshl",{A_IMM,DSP_REG_N},{PPI,PSH,HEX_0}, arch_sh_dsp_up}, +/* 10010001xxyynnnn psha ,, */ +{"psha", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_1}, arch_sh_dsp_up}, +/* 00010iiiiiiinnnn psha #, */ {"psha",{A_IMM,DSP_REG_N},{PPI,PSH,HEX_1}, arch_sh_dsp_up}, +/* 10100001xxyynnnn psub ,, */ +{"psub", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_1}, arch_sh_dsp_up}, +/* 10000101xxyynnnn psub ,, */ +{"psub", {DSP_REG_Y,DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_5}, arch_sh4al_dsp_up}, +/* 10110001xxyynnnn padd ,, */ +{"padd", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_1}, arch_sh_dsp_up}, +/* 10010101xxyynnnn pand ,, */ +{"pand", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_5}, arch_sh_dsp_up}, +/* 10100101xxyynnnn pxor ,, */ +{"pxor", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_5}, arch_sh_dsp_up}, +/* 10110101xxyynnnn por ,, */ +{"por", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_5}, arch_sh_dsp_up}, +/* 10001001xxyynnnn pdec , */ +{"pdec", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_9}, arch_sh_dsp_up}, +/* 10101001xxyynnnn pdec , */ +{"pdec", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_9}, arch_sh_dsp_up}, +/* 10011001xx00nnnn pinc , */ +{"pinc", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_9,HEX_XX00}, arch_sh_dsp_up}, +/* 1011100100yynnnn pinc , */ +{"pinc", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_9,HEX_00YY}, arch_sh_dsp_up}, +/* 10001101xxyynnnn pclr */ +{"pclr", {DSP_REG_N},{PPI,PPIC,HEX_8,HEX_D}, arch_sh_dsp_up}, +/* 10011101xx00nnnn pdmsb , */ +{"pdmsb", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_D,HEX_XX00}, arch_sh_dsp_up}, +/* 1011110100yynnnn pdmsb , */ +{"pdmsb", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_D,HEX_00YY}, arch_sh_dsp_up}, +/* 11001001xxyynnnn pneg , */ +{"pneg", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_C,HEX_9}, arch_sh_dsp_up}, +/* 11101001xxyynnnn pneg , */ +{"pneg", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_E,HEX_9}, arch_sh_dsp_up}, +/* 11011001xxyynnnn pcopy , */ +{"pcopy", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_D,HEX_9}, arch_sh_dsp_up}, +/* 11111001xxyynnnn pcopy , */ +{"pcopy", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_F,HEX_9}, arch_sh_dsp_up}, +/* 11001101xxyynnnn psts MACH, */ +{"psts", {A_MACH,DSP_REG_N},{PPI,PPIC,HEX_C,HEX_D}, arch_sh_dsp_up}, +/* 11011101xxyynnnn psts MACL, */ +{"psts", {A_MACL,DSP_REG_N},{PPI,PPIC,HEX_D,HEX_D}, arch_sh_dsp_up}, +/* 11101101xxyynnnn plds ,MACH */ +{"plds", {DSP_REG_N,A_MACH},{PPI,PPIC,HEX_E,HEX_D}, arch_sh_dsp_up}, +/* 11111101xxyynnnn plds ,MACL */ +{"plds", {DSP_REG_N,A_MACL},{PPI,PPIC,HEX_F,HEX_D}, arch_sh_dsp_up}, +/* 10011101xx01zzzz pswap , */ +{"pswap", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_D,HEX_1}, arch_sh4al_dsp_up}, +/* 1011110101yyzzzz pswap , */ +{"pswap", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_D,HEX_4}, arch_sh4al_dsp_up}, + +/* 1111nnnn01011101 fabs */{"fabs",{F_REG_N},{HEX_F,REG_N,HEX_5,HEX_D}, arch_sh2e_up}, +/* 1111nnn001011101 fabs */{"fabs",{D_REG_N},{HEX_F,REG_N,HEX_5,HEX_D}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm0000 fadd ,*/{"fadd",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_0}, arch_sh2e_up}, +/* 1111nnn0mmm00000 fadd ,*/{"fadd",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_0}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm0100 fcmp/eq ,*/{"fcmp/eq",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_4}, arch_sh2e_up}, +/* 1111nnn0mmm00100 fcmp/eq ,*/{"fcmp/eq",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_4}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm0101 fcmp/gt ,*/{"fcmp/gt",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_5}, arch_sh2e_up}, +/* 1111nnn0mmm00101 fcmp/gt ,*/{"fcmp/gt",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_5}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnn010111101 fcnvds ,FPUL*/{"fcnvds",{D_REG_N,FPUL_M},{HEX_F,REG_N_D,HEX_B,HEX_D}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnn010101101 fcnvsd FPUL,*/{"fcnvsd",{FPUL_M,D_REG_N},{HEX_F,REG_N_D,HEX_A,HEX_D}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm0011 fdiv ,*/{"fdiv",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_3}, arch_sh2e_up}, +/* 1111nnn0mmm00011 fdiv ,*/{"fdiv",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_3}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnmm11101101 fipr ,*/{"fipr",{V_REG_M,V_REG_N},{HEX_F,REG_NM,HEX_E,HEX_D}, arch_sh4_up}, + +/* 1111nnnn10001101 fldi0 */{"fldi0",{F_REG_N},{HEX_F,REG_N,HEX_8,HEX_D}, arch_sh2e_up}, + +/* 1111nnnn10011101 fldi1 */{"fldi1",{F_REG_N},{HEX_F,REG_N,HEX_9,HEX_D}, arch_sh2e_up}, + +/* 1111nnnn00011101 flds ,FPUL*/{"flds",{F_REG_N,FPUL_M},{HEX_F,REG_N,HEX_1,HEX_D}, arch_sh2e_up}, + +/* 1111nnnn00101101 float FPUL,*/{"float",{FPUL_M,F_REG_N},{HEX_F,REG_N,HEX_2,HEX_D}, arch_sh2e_up}, +/* 1111nnn000101101 float FPUL,*/{"float",{FPUL_M,D_REG_N},{HEX_F,REG_N,HEX_2,HEX_D}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm1110 fmac FR0,,*/{"fmac",{F_FR0,F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_E}, arch_sh2e_up}, + +/* 1111nnnnmmmm1100 fmov ,*/{"fmov",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_C}, arch_sh2e_up}, +/* 1111nnn1mmmm1100 fmov ,*/{"fmov",{DX_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_C}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm1000 fmov @,*/{"fmov",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh2e_up}, +/* 1111nnn1mmmm1000 fmov @,*/{"fmov",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm1010 fmov ,@*/{"fmov",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh2e_up}, +/* 1111nnnnmmm11010 fmov ,@*/{"fmov",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm1001 fmov @+,*/{"fmov",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh2e_up}, +/* 1111nnn1mmmm1001 fmov @+,*/{"fmov",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm1011 fmov ,@-*/{"fmov",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh2e_up}, +/* 1111nnnnmmm11011 fmov ,@-*/{"fmov",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm0110 fmov @(R0,),*/{"fmov",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh2e_up}, +/* 1111nnn1mmmm0110 fmov @(R0,),*/{"fmov",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmmm0111 fmov ,@(R0,)*/{"fmov",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh2e_up}, +/* 1111nnnnmmm10111 fmov ,@(R0,)*/{"fmov",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnn1mmmm1000 fmov.d @,*/{"fmov.d",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmm11010 fmov.d ,@*/{"fmov.d",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnn1mmmm1001 fmov.d @+,*/{"fmov.d",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmm11011 fmov.d ,@-*/{"fmov.d",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnn1mmmm0110 fmov.d @(R0,),*/{"fmov.d",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnnmmm10111 fmov.d ,@(R0,)*/{"fmov.d",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh4_up | arch_sh2a_up}, +/* 0011nnnnmmmm0001 0011dddddddddddd fmov.d ,@(,) */ +{"fmov.d",{DX_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_3,DISP1_12BY8}, arch_sh2a_up | arch_op32}, +/* 0011nnnnmmmm0001 0111dddddddddddd fmov.d @(,),F_REG_N */ +{"fmov.d",{A_DISP_REG_M,DX_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_7,DISP0_12BY8}, arch_sh2a_up | arch_op32}, + +/* 1111nnnnmmmm1000 fmov.s @,*/{"fmov.s",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh2e_up}, + +/* 1111nnnnmmmm1010 fmov.s ,@*/{"fmov.s",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh2e_up}, + +/* 1111nnnnmmmm1001 fmov.s @+,*/{"fmov.s",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh2e_up}, + +/* 1111nnnnmmmm1011 fmov.s ,@-*/{"fmov.s",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh2e_up}, + +/* 1111nnnnmmmm0110 fmov.s @(R0,),*/{"fmov.s",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh2e_up}, + +/* 1111nnnnmmmm0111 fmov.s ,@(R0,)*/{"fmov.s",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh2e_up}, +/* 0011nnnnmmmm0001 0011dddddddddddd fmov.s ,@(,) */ +{"fmov.s",{F_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_3,DISP1_12BY4}, arch_sh2a_up | arch_op32}, +/* 0011nnnnmmmm0001 0111dddddddddddd fmov.s @(,),F_REG_N */ +{"fmov.s",{A_DISP_REG_M,F_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_7,DISP0_12BY4}, arch_sh2a_up | arch_op32}, + +/* 1111nnnnmmmm0010 fmul ,*/{"fmul",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_2}, arch_sh2e_up}, +/* 1111nnn0mmm00010 fmul ,*/{"fmul",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_2}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnn01001101 fneg */{"fneg",{F_REG_N},{HEX_F,REG_N,HEX_4,HEX_D}, arch_sh2e_up}, +/* 1111nnn001001101 fneg */{"fneg",{D_REG_N},{HEX_F,REG_N,HEX_4,HEX_D}, arch_sh4_up | arch_sh2a_up}, + +/* 1111011111111101 fpchg */{"fpchg",{0},{HEX_F,HEX_7,HEX_F,HEX_D}, arch_sh4a_up}, + +/* 1111101111111101 frchg */{"frchg",{0},{HEX_F,HEX_B,HEX_F,HEX_D}, arch_sh4_up}, + +/* 1111nnn011111101 fsca FPUL, */{"fsca",{FPUL_M,D_REG_N},{HEX_F,REG_N_D,HEX_F,HEX_D}, arch_sh4_up}, + +/* 1111001111111101 fschg */{"fschg",{0},{HEX_F,HEX_3,HEX_F,HEX_D}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnn01101101 fsqrt */{"fsqrt",{F_REG_N},{HEX_F,REG_N,HEX_6,HEX_D}, arch_sh3e_up | arch_sh2a_up}, +/* 1111nnn001101101 fsqrt */{"fsqrt",{D_REG_N},{HEX_F,REG_N,HEX_6,HEX_D}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnn01111101 fsrra */{"fsrra",{F_REG_N},{HEX_F,REG_N,HEX_7,HEX_D}, arch_sh4_up}, + +/* 1111nnnn00001101 fsts FPUL,*/{"fsts",{FPUL_M,F_REG_N},{HEX_F,REG_N,HEX_0,HEX_D}, arch_sh2e_up}, + +/* 1111nnnnmmmm0001 fsub ,*/{"fsub",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_1}, arch_sh2e_up}, +/* 1111nnn0mmm00001 fsub ,*/{"fsub",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_1}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nnnn00111101 ftrc ,FPUL*/{"ftrc",{F_REG_N,FPUL_M},{HEX_F,REG_N,HEX_3,HEX_D}, arch_sh2e_up}, +/* 1111nnnn00111101 ftrc ,FPUL*/{"ftrc",{D_REG_N,FPUL_M},{HEX_F,REG_N,HEX_3,HEX_D}, arch_sh4_up | arch_sh2a_up}, + +/* 1111nn0111111101 ftrv XMTRX_M4,*/{"ftrv",{XMTRX_M4,V_REG_N},{HEX_F,REG_N_B01,HEX_F,HEX_D}, arch_sh4_up}, + + /* 10000110nnnn0iii bclr #, */ {"bclr",{A_IMM, A_REG_N},{HEX_8,HEX_6,REG_N,IMM0_3c}, arch_sh2a_nofpu_up}, + /* 0011nnnn0iii1001 0000dddddddddddd bclr.b #,@(,) */ +{"bclr.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_0,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, + /* 10000111nnnn1iii bld #, */ {"bld",{A_IMM, A_REG_N},{HEX_8,HEX_7,REG_N,IMM0_3s}, arch_sh2a_nofpu_up}, + /* 0011nnnn0iii1001 0011dddddddddddd bld.b #,@(,) */ +{"bld.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_3,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, + /* 10000110nnnn1iii bset #, */ {"bset",{A_IMM, A_REG_N},{HEX_8,HEX_6,REG_N,IMM0_3s}, arch_sh2a_nofpu_up}, + /* 0011nnnn0iii1001 0001dddddddddddd bset.b #,@(,) */ +{"bset.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_1,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, + /* 10000111nnnn0iii bst #, */ {"bst",{A_IMM, A_REG_N},{HEX_8,HEX_7,REG_N,IMM0_3c}, arch_sh2a_nofpu_up}, + /* 0011nnnn0iii1001 0010dddddddddddd bst.b #,@(,) */ +{"bst.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_2,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, + /* 0100nnnn10010001 clips.b */ {"clips.b",{A_REG_N},{HEX_4,REG_N,HEX_9,HEX_1}, arch_sh2a_nofpu_up}, + /* 0100nnnn10010101 clips.w */ {"clips.w",{A_REG_N},{HEX_4,REG_N,HEX_9,HEX_5}, arch_sh2a_nofpu_up}, + /* 0100nnnn10000001 clipu.b */ {"clipu.b",{A_REG_N},{HEX_4,REG_N,HEX_8,HEX_1}, arch_sh2a_nofpu_up}, + /* 0100nnnn10000101 clipu.w */ {"clipu.w",{A_REG_N},{HEX_4,REG_N,HEX_8,HEX_5}, arch_sh2a_nofpu_up}, + /* 0100nnnn10010100 divs R0, */ {"divs",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_9,HEX_4}, arch_sh2a_nofpu_up}, + /* 0100nnnn10000100 divu R0, */ {"divu",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_8,HEX_4}, arch_sh2a_nofpu_up}, + /* 0100mmmm01001011 jsr/n @ */ {"jsr/n",{A_IND_M},{HEX_4,REG_M,HEX_4,HEX_B}, arch_sh2a_nofpu_up}, + /* 10000011dddddddd jsr/n @@(,TBR) */ {"jsr/n",{A_DISP2_TBR},{HEX_8,HEX_3,IMM0_8BY4}, arch_sh2a_nofpu_up}, + /* 0100mmmm11100101 ldbank @,R0 */ {"ldbank",{A_IND_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_5}, arch_sh2a_nofpu_up}, + /* 0100mmmm11110001 movml.l ,@-R15 */ {"movml.l",{A_REG_M,A_DEC_R15},{HEX_4,REG_M,HEX_F,HEX_1}, arch_sh2a_nofpu_up}, + /* 0100mmmm11110101 movml.l @R15+, */ {"movml.l",{A_INC_R15,A_REG_M},{HEX_4,REG_M,HEX_F,HEX_5}, arch_sh2a_nofpu_up}, + /* 0100mmmm11110000 movml.l ,@-R15 */ {"movmu.l",{A_REG_M,A_DEC_R15},{HEX_4,REG_M,HEX_F,HEX_0}, arch_sh2a_nofpu_up}, + /* 0100mmmm11110100 movml.l @R15+, */ {"movmu.l",{A_INC_R15,A_REG_M},{HEX_4,REG_M,HEX_F,HEX_4}, arch_sh2a_nofpu_up}, + /* 0000nnnn00111001 movrt */ {"movrt",{A_REG_N},{HEX_0,REG_N,HEX_3,HEX_9}, arch_sh2a_nofpu_up}, + /* 0100nnnn10000000 mulr R0, */ {"mulr",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_8,HEX_0}, arch_sh2a_nofpu_up}, + /* 0000000001101000 nott */ {"nott",{A_END},{HEX_0,HEX_0,HEX_6,HEX_8}, arch_sh2a_nofpu_up}, + /* 0000000001011011 resbank */ {"resbank",{A_END},{HEX_0,HEX_0,HEX_5,HEX_B}, arch_sh2a_nofpu_up}, + /* 0000000001101011 rts/n */ {"rts/n",{A_END},{HEX_0,HEX_0,HEX_6,HEX_B}, arch_sh2a_nofpu_up}, + /* 0000mmmm01111011 rtv/n */ {"rtv/n",{A_REG_M},{HEX_0,REG_M,HEX_7,HEX_B}, arch_sh2a_nofpu_up}, + /* 0100nnnn11100001 stbank R0,@*/ {"stbank",{A_R0,A_IND_N},{HEX_4,REG_N,HEX_E,HEX_1}, arch_sh2a_nofpu_up}, + +/* 0011nnnn0iii1001 0100dddddddddddd band.b #,@(,) */ +{"band.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_4,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnn0iii1001 1100dddddddddddd bandnot.b #,@(,) */ +{"bandnot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_C,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnn0iii1001 1011dddddddddddd bldnot.b #,@(,) */ +{"bldnot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_B,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnn0iii1001 0101dddddddddddd bor.b #,@(,) */ +{"bor.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_5,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnn0iii1001 1101dddddddddddd bornot.b #,@(,) */ +{"bornot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_D,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnn0iii1001 0110dddddddddddd bxor.b #,@(,) */ +{"bxor.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_6,DISP1_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0000nnnniiii0000 iiiiiiiiiiiiiiii movi20 #, */ +{"movi20",{A_IMM,A_REG_N},{HEX_0,REG_N,IMM0_20_4,HEX_0,IMM0_20}, arch_sh2a_nofpu_up | arch_op32}, +/* 0000nnnniiii0001 iiiiiiiiiiiiiiii movi20s #, */ +{"movi20s",{A_IMM,A_REG_N},{HEX_0,REG_N,IMM0_20_4,HEX_1,IMM0_20BY8}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnnmmmm0001 1000dddddddddddd movu.b @(,), */ +{"movu.b",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_8,DISP0_12}, arch_sh2a_nofpu_up | arch_op32}, +/* 0011nnnnmmmm0001 1001dddddddddddd movu.w @(,), */ +{"movu.w",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_9,DISP0_12BY2}, arch_sh2a_nofpu_up | arch_op32}, + +{ 0, {0}, {0}, 0 } +}; + +#endif + +#ifdef ARCH_all +#define INCLUDE_SHMEDIA +#endif + +static void +print_movxy (const sh_opcode_info *op, int rn, int rm, + fprintf_function fprintf_fn, void *stream) +{ + int n; + + fprintf_fn (stream, "%s\t", op->name); + for (n = 0; n < 2; n++) + { + switch (op->arg[n]) + { + case A_IND_N: + case AX_IND_N: + case AXY_IND_N: + case AY_IND_N: + case AYX_IND_N: + fprintf_fn (stream, "@r%d", rn); + break; + case A_INC_N: + case AX_INC_N: + case AXY_INC_N: + case AY_INC_N: + case AYX_INC_N: + fprintf_fn (stream, "@r%d+", rn); + break; + case AX_PMOD_N: + case AXY_PMOD_N: + fprintf_fn (stream, "@r%d+r8", rn); + break; + case AY_PMOD_N: + case AYX_PMOD_N: + fprintf_fn (stream, "@r%d+r9", rn); + break; + case DSP_REG_A_M: + fprintf_fn (stream, "a%c", '0' + rm); + break; + case DSP_REG_X: + fprintf_fn (stream, "x%c", '0' + rm); + break; + case DSP_REG_Y: + fprintf_fn (stream, "y%c", '0' + rm); + break; + case DSP_REG_AX: + fprintf_fn (stream, "%c%c", + (rm & 1) ? 'x' : 'a', + (rm & 2) ? '1' : '0'); + break; + case DSP_REG_XY: + fprintf_fn (stream, "%c%c", + (rm & 1) ? 'y' : 'x', + (rm & 2) ? '1' : '0'); + break; + case DSP_REG_AY: + fprintf_fn (stream, "%c%c", + (rm & 2) ? 'y' : 'a', + (rm & 1) ? '1' : '0'); + break; + case DSP_REG_YX: + fprintf_fn (stream, "%c%c", + (rm & 2) ? 'x' : 'y', + (rm & 1) ? '1' : '0'); + break; + default: + abort (); + } + if (n == 0) + fprintf_fn (stream, ","); + } +} + +/* Print a double data transfer insn. INSN is just the lower three + nibbles of the insn, i.e. field a and the bit that indicates if + a parallel processing insn follows. + Return nonzero if a field b of a parallel processing insns follows. */ + +static void +print_insn_ddt (int insn, struct disassemble_info *info) +{ + fprintf_function fprintf_fn = info->fprintf_func; + void *stream = info->stream; + + /* If this is just a nop, make sure to emit something. */ + if (insn == 0x000) + fprintf_fn (stream, "nopx\tnopy"); + + /* If a parallel processing insn was printed before, + and we got a non-nop, emit a tab. */ + if ((insn & 0x800) && (insn & 0x3ff)) + fprintf_fn (stream, "\t"); + + /* Check if either the x or y part is invalid. */ + if (((insn & 0xc) == 0 && (insn & 0x2a0)) + || ((insn & 3) == 0 && (insn & 0x150))) + if (info->mach != bfd_mach_sh_dsp + && info->mach != bfd_mach_sh3_dsp) + { + static const sh_opcode_info *first_movx, *first_movy; + const sh_opcode_info *op; + int is_movy; + + if (! first_movx) + { + for (first_movx = sh_table; first_movx->nibbles[1] != MOVX_NOPY;) + first_movx++; + for (first_movy = first_movx; first_movy->nibbles[1] != MOVY_NOPX;) + first_movy++; + } + + is_movy = ((insn & 3) != 0); + + if (is_movy) + op = first_movy; + else + op = first_movx; + + while (op->nibbles[2] != (unsigned) ((insn >> 4) & 3) + || op->nibbles[3] != (unsigned) (insn & 0xf)) + op++; + + print_movxy (op, + (4 * ((insn & (is_movy ? 0x200 : 0x100)) == 0) + + 2 * is_movy + + 1 * ((insn & (is_movy ? 0x100 : 0x200)) != 0)), + (insn >> 6) & 3, + fprintf_fn, stream); + } + else + fprintf_fn (stream, ".word 0x%x", insn); + else + { + static const sh_opcode_info *first_movx, *first_movy; + const sh_opcode_info *opx, *opy; + unsigned int insn_x, insn_y; + + if (! first_movx) + { + for (first_movx = sh_table; first_movx->nibbles[1] != MOVX;) + first_movx++; + for (first_movy = first_movx; first_movy->nibbles[1] != MOVY;) + first_movy++; + } + insn_x = (insn >> 2) & 0xb; + if (insn_x) + { + for (opx = first_movx; opx->nibbles[2] != insn_x;) + opx++; + print_movxy (opx, ((insn >> 9) & 1) + 4, (insn >> 7) & 1, + fprintf_fn, stream); + } + insn_y = (insn & 3) | ((insn >> 1) & 8); + if (insn_y) + { + if (insn_x) + fprintf_fn (stream, "\t"); + for (opy = first_movy; opy->nibbles[2] != insn_y;) + opy++; + print_movxy (opy, ((insn >> 8) & 1) + 6, (insn >> 6) & 1, + fprintf_fn, stream); + } + } +} + +static void +print_dsp_reg (int rm, fprintf_function fprintf_fn, void *stream) +{ + switch (rm) + { + case A_A1_NUM: + fprintf_fn (stream, "a1"); + break; + case A_A0_NUM: + fprintf_fn (stream, "a0"); + break; + case A_X0_NUM: + fprintf_fn (stream, "x0"); + break; + case A_X1_NUM: + fprintf_fn (stream, "x1"); + break; + case A_Y0_NUM: + fprintf_fn (stream, "y0"); + break; + case A_Y1_NUM: + fprintf_fn (stream, "y1"); + break; + case A_M0_NUM: + fprintf_fn (stream, "m0"); + break; + case A_A1G_NUM: + fprintf_fn (stream, "a1g"); + break; + case A_M1_NUM: + fprintf_fn (stream, "m1"); + break; + case A_A0G_NUM: + fprintf_fn (stream, "a0g"); + break; + default: + fprintf_fn (stream, "0x%x", rm); + break; + } +} + +static void +print_insn_ppi (int field_b, struct disassemble_info *info) +{ + static const char *sx_tab[] = { "x0", "x1", "a0", "a1" }; + static const char *sy_tab[] = { "y0", "y1", "m0", "m1" }; + fprintf_function fprintf_fn = info->fprintf_func; + void *stream = info->stream; + unsigned int nib1, nib2, nib3; + unsigned int altnib1, nib4; + const char *dc = NULL; + const sh_opcode_info *op; + + if ((field_b & 0xe800) == 0) + { + fprintf_fn (stream, "psh%c\t#%d,", + field_b & 0x1000 ? 'a' : 'l', + (field_b >> 4) & 127); + print_dsp_reg (field_b & 0xf, fprintf_fn, stream); + return; + } + if ((field_b & 0xc000) == 0x4000 && (field_b & 0x3000) != 0x1000) + { + static const char *du_tab[] = { "x0", "y0", "a0", "a1" }; + static const char *se_tab[] = { "x0", "x1", "y0", "a1" }; + static const char *sf_tab[] = { "y0", "y1", "x0", "a1" }; + static const char *sg_tab[] = { "m0", "m1", "a0", "a1" }; + + if (field_b & 0x2000) + { + fprintf_fn (stream, "p%s %s,%s,%s\t", + (field_b & 0x1000) ? "add" : "sub", + sx_tab[(field_b >> 6) & 3], + sy_tab[(field_b >> 4) & 3], + du_tab[(field_b >> 0) & 3]); + } + else if ((field_b & 0xf0) == 0x10 + && info->mach != bfd_mach_sh_dsp + && info->mach != bfd_mach_sh3_dsp) + { + fprintf_fn (stream, "pclr %s \t", du_tab[(field_b >> 0) & 3]); + } + else if ((field_b & 0xf3) != 0) + { + fprintf_fn (stream, ".word 0x%x\t", field_b); + } + fprintf_fn (stream, "pmuls%c%s,%s,%s", + field_b & 0x2000 ? ' ' : '\t', + se_tab[(field_b >> 10) & 3], + sf_tab[(field_b >> 8) & 3], + sg_tab[(field_b >> 2) & 3]); + return; + } + + nib1 = PPIC; + nib2 = field_b >> 12 & 0xf; + nib3 = field_b >> 8 & 0xf; + nib4 = field_b >> 4 & 0xf; + switch (nib3 & 0x3) + { + case 0: + dc = ""; + nib1 = PPI3; + break; + case 1: + dc = ""; + break; + case 2: + dc = "dct "; + nib3 -= 1; + break; + case 3: + dc = "dcf "; + nib3 -= 2; + break; + } + if (nib1 == PPI3) + altnib1 = PPI3NC; + else + altnib1 = nib1; + for (op = sh_table; op->name; op++) + { + if ((op->nibbles[1] == nib1 || op->nibbles[1] == altnib1) + && op->nibbles[2] == nib2 + && op->nibbles[3] == nib3) + { + int n; + + switch (op->nibbles[4]) + { + case HEX_0: + break; + case HEX_XX00: + if ((nib4 & 3) != 0) + continue; + break; + case HEX_1: + if ((nib4 & 3) != 1) + continue; + break; + case HEX_00YY: + if ((nib4 & 0xc) != 0) + continue; + break; + case HEX_4: + if ((nib4 & 0xc) != 4) + continue; + break; + default: + abort (); + } + fprintf_fn (stream, "%s%s\t", dc, op->name); + for (n = 0; n < 3 && op->arg[n] != A_END; n++) + { + if (n && op->arg[1] != A_END) + fprintf_fn (stream, ","); + switch (op->arg[n]) + { + case DSP_REG_N: + print_dsp_reg (field_b & 0xf, fprintf_fn, stream); + break; + case DSP_REG_X: + fprintf_fn (stream, "%s", sx_tab[(field_b >> 6) & 3]); + break; + case DSP_REG_Y: + fprintf_fn (stream, "%s", sy_tab[(field_b >> 4) & 3]); + break; + case A_MACH: + fprintf_fn (stream, "mach"); + break; + case A_MACL: + fprintf_fn (stream, "macl"); + break; + default: + abort (); + } + } + return; + } + } + /* Not found. */ + fprintf_fn (stream, ".word 0x%x", field_b); +} + +/* FIXME mvs: movx insns print as ".word 0x%03x", insn & 0xfff + (ie. the upper nibble is missing). */ +int +print_insn_sh (bfd_vma memaddr, struct disassemble_info *info) +{ + fprintf_function fprintf_fn = info->fprintf_func; + void *stream = info->stream; + unsigned char insn[4]; + unsigned char nibs[8]; + int status; + bfd_vma relmask = ~(bfd_vma) 0; + const sh_opcode_info *op; + unsigned int target_arch; + int allow_op32; + + switch (info->mach) + { + case bfd_mach_sh: + target_arch = arch_sh1; + break; + case bfd_mach_sh4: + target_arch = arch_sh4; + break; + case bfd_mach_sh5: +#ifdef INCLUDE_SHMEDIA + status = print_insn_sh64 (memaddr, info); + if (status != -2) + return status; +#endif + /* When we get here for sh64, it's because we want to disassemble + SHcompact, i.e. arch_sh4. */ + target_arch = arch_sh4; + break; + default: + fprintf (stderr, "sh architecture not supported\n"); + return -1; + } + + status = info->read_memory_func (memaddr, insn, 2, info); + + if (status != 0) + { + info->memory_error_func (status, memaddr, info); + return -1; + } + + if (info->endian == BFD_ENDIAN_LITTLE) + { + nibs[0] = (insn[1] >> 4) & 0xf; + nibs[1] = insn[1] & 0xf; + + nibs[2] = (insn[0] >> 4) & 0xf; + nibs[3] = insn[0] & 0xf; + } + else + { + nibs[0] = (insn[0] >> 4) & 0xf; + nibs[1] = insn[0] & 0xf; + + nibs[2] = (insn[1] >> 4) & 0xf; + nibs[3] = insn[1] & 0xf; + } + status = info->read_memory_func (memaddr + 2, insn + 2, 2, info); + if (status != 0) + allow_op32 = 0; + else + { + allow_op32 = 1; + + if (info->endian == BFD_ENDIAN_LITTLE) + { + nibs[4] = (insn[3] >> 4) & 0xf; + nibs[5] = insn[3] & 0xf; + + nibs[6] = (insn[2] >> 4) & 0xf; + nibs[7] = insn[2] & 0xf; + } + else + { + nibs[4] = (insn[2] >> 4) & 0xf; + nibs[5] = insn[2] & 0xf; + + nibs[6] = (insn[3] >> 4) & 0xf; + nibs[7] = insn[3] & 0xf; + } + } + + if (nibs[0] == 0xf && (nibs[1] & 4) == 0 + && SH_MERGE_ARCH_SET_VALID (target_arch, arch_sh_dsp_up)) + { + if (nibs[1] & 8) + { + int field_b; + + status = info->read_memory_func (memaddr + 2, insn, 2, info); + + if (status != 0) + { + info->memory_error_func (status, memaddr + 2, info); + return -1; + } + + if (info->endian == BFD_ENDIAN_LITTLE) + field_b = insn[1] << 8 | insn[0]; + else + field_b = insn[0] << 8 | insn[1]; + + print_insn_ppi (field_b, info); + print_insn_ddt ((nibs[1] << 8) | (nibs[2] << 4) | nibs[3], info); + return 4; + } + print_insn_ddt ((nibs[1] << 8) | (nibs[2] << 4) | nibs[3], info); + return 2; + } + for (op = sh_table; op->name; op++) + { + int n; + int imm = 0; + int rn = 0; + int rm = 0; + int rb = 0; + int disp_pc; + bfd_vma disp_pc_addr = 0; + int disp = 0; + int has_disp = 0; + int max_n = SH_MERGE_ARCH_SET (op->arch, arch_op32) ? 8 : 4; + + if (!allow_op32 + && SH_MERGE_ARCH_SET (op->arch, arch_op32)) + goto fail; + + if (!SH_MERGE_ARCH_SET_VALID (op->arch, target_arch)) + goto fail; + for (n = 0; n < max_n; n++) + { + int i = op->nibbles[n]; + + if (i < 16) + { + if (nibs[n] == i) + continue; + goto fail; + } + switch (i) + { + case BRANCH_8: + imm = (nibs[2] << 4) | (nibs[3]); + if (imm & 0x80) + imm |= ~0xff; + imm = ((char) imm) * 2 + 4; + goto ok; + case BRANCH_12: + imm = ((nibs[1]) << 8) | (nibs[2] << 4) | (nibs[3]); + if (imm & 0x800) + imm |= ~0xfff; + imm = imm * 2 + 4; + goto ok; + case IMM0_3c: + if (nibs[3] & 0x8) + goto fail; + imm = nibs[3] & 0x7; + break; + case IMM0_3s: + if (!(nibs[3] & 0x8)) + goto fail; + imm = nibs[3] & 0x7; + break; + case IMM0_3Uc: + if (nibs[2] & 0x8) + goto fail; + imm = nibs[2] & 0x7; + break; + case IMM0_3Us: + if (!(nibs[2] & 0x8)) + goto fail; + imm = nibs[2] & 0x7; + break; + case DISP0_12: + case DISP1_12: + disp = (nibs[5] << 8) | (nibs[6] << 4) | nibs[7]; + has_disp = 1; + goto ok; + case DISP0_12BY2: + case DISP1_12BY2: + disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 1; + relmask = ~(bfd_vma) 1; + has_disp = 1; + goto ok; + case DISP0_12BY4: + case DISP1_12BY4: + disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 2; + relmask = ~(bfd_vma) 3; + has_disp = 1; + goto ok; + case DISP0_12BY8: + case DISP1_12BY8: + disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 3; + relmask = ~(bfd_vma) 7; + has_disp = 1; + goto ok; + case IMM0_20_4: + break; + case IMM0_20: + imm = ((nibs[2] << 16) | (nibs[4] << 12) | (nibs[5] << 8) + | (nibs[6] << 4) | nibs[7]); + if (imm & 0x80000) + imm -= 0x100000; + goto ok; + case IMM0_20BY8: + imm = ((nibs[2] << 16) | (nibs[4] << 12) | (nibs[5] << 8) + | (nibs[6] << 4) | nibs[7]); + imm <<= 8; + if (imm & 0x8000000) + imm -= 0x10000000; + goto ok; + case IMM0_4: + case IMM1_4: + imm = nibs[3]; + goto ok; + case IMM0_4BY2: + case IMM1_4BY2: + imm = nibs[3] << 1; + goto ok; + case IMM0_4BY4: + case IMM1_4BY4: + imm = nibs[3] << 2; + goto ok; + case IMM0_8: + case IMM1_8: + imm = (nibs[2] << 4) | nibs[3]; + disp = imm; + has_disp = 1; + if (imm & 0x80) + imm -= 0x100; + goto ok; + case PCRELIMM_8BY2: + imm = ((nibs[2] << 4) | nibs[3]) << 1; + relmask = ~(bfd_vma) 1; + goto ok; + case PCRELIMM_8BY4: + imm = ((nibs[2] << 4) | nibs[3]) << 2; + relmask = ~(bfd_vma) 3; + goto ok; + case IMM0_8BY2: + case IMM1_8BY2: + imm = ((nibs[2] << 4) | nibs[3]) << 1; + goto ok; + case IMM0_8BY4: + case IMM1_8BY4: + imm = ((nibs[2] << 4) | nibs[3]) << 2; + goto ok; + case REG_N_D: + if ((nibs[n] & 1) != 0) + goto fail; + /* fall through */ + case REG_N: + rn = nibs[n]; + break; + case REG_M: + rm = nibs[n]; + break; + case REG_N_B01: + if ((nibs[n] & 0x3) != 1 /* binary 01 */) + goto fail; + rn = (nibs[n] & 0xc) >> 2; + break; + case REG_NM: + rn = (nibs[n] & 0xc) >> 2; + rm = (nibs[n] & 0x3); + break; + case REG_B: + rb = nibs[n] & 0x07; + break; + case SDT_REG_N: + /* sh-dsp: single data transfer. */ + rn = nibs[n]; + if ((rn & 0xc) != 4) + goto fail; + rn = rn & 0x3; + rn |= (!(rn & 2)) << 2; + break; + case PPI: + case REPEAT: + goto fail; + default: + abort (); + } + } + + ok: + /* sh2a has D_REG but not X_REG. We don't know the pattern + doesn't match unless we check the output args to see if they + make sense. */ + if (target_arch == arch_sh2a + && ((op->arg[0] == DX_REG_M && (rm & 1) != 0) + || (op->arg[1] == DX_REG_N && (rn & 1) != 0))) + goto fail; + + fprintf_fn (stream, "%s\t", op->name); + disp_pc = 0; + for (n = 0; n < 3 && op->arg[n] != A_END; n++) + { + if (n && op->arg[1] != A_END) + fprintf_fn (stream, ","); + switch (op->arg[n]) + { + case A_IMM: + fprintf_fn (stream, "#%d", imm); + break; + case A_R0: + fprintf_fn (stream, "r0"); + break; + case A_REG_N: + fprintf_fn (stream, "r%d", rn); + break; + case A_INC_N: + case AS_INC_N: + fprintf_fn (stream, "@r%d+", rn); + break; + case A_DEC_N: + case AS_DEC_N: + fprintf_fn (stream, "@-r%d", rn); + break; + case A_IND_N: + case AS_IND_N: + fprintf_fn (stream, "@r%d", rn); + break; + case A_DISP_REG_N: + fprintf_fn (stream, "@(%d,r%d)", has_disp?disp:imm, rn); + break; + case AS_PMOD_N: + fprintf_fn (stream, "@r%d+r8", rn); + break; + case A_REG_M: + fprintf_fn (stream, "r%d", rm); + break; + case A_INC_M: + fprintf_fn (stream, "@r%d+", rm); + break; + case A_DEC_M: + fprintf_fn (stream, "@-r%d", rm); + break; + case A_IND_M: + fprintf_fn (stream, "@r%d", rm); + break; + case A_DISP_REG_M: + fprintf_fn (stream, "@(%d,r%d)", has_disp?disp:imm, rm); + break; + case A_REG_B: + fprintf_fn (stream, "r%d_bank", rb); + break; + case A_DISP_PC: + disp_pc = 1; + disp_pc_addr = imm + 4 + (memaddr & relmask); + (*info->print_address_func) (disp_pc_addr, info); + break; + case A_IND_R0_REG_N: + fprintf_fn (stream, "@(r0,r%d)", rn); + break; + case A_IND_R0_REG_M: + fprintf_fn (stream, "@(r0,r%d)", rm); + break; + case A_DISP_GBR: + fprintf_fn (stream, "@(%d,gbr)", has_disp?disp:imm); + break; + case A_TBR: + fprintf_fn (stream, "tbr"); + break; + case A_DISP2_TBR: + fprintf_fn (stream, "@@(%d,tbr)", has_disp?disp:imm); + break; + case A_INC_R15: + fprintf_fn (stream, "@r15+"); + break; + case A_DEC_R15: + fprintf_fn (stream, "@-r15"); + break; + case A_R0_GBR: + fprintf_fn (stream, "@(r0,gbr)"); + break; + case A_BDISP12: + case A_BDISP8: + { + bfd_vma addr; + addr = imm + memaddr; + (*info->print_address_func) (addr, info); + } + break; + case A_SR: + fprintf_fn (stream, "sr"); + break; + case A_GBR: + fprintf_fn (stream, "gbr"); + break; + case A_VBR: + fprintf_fn (stream, "vbr"); + break; + case A_DSR: + fprintf_fn (stream, "dsr"); + break; + case A_MOD: + fprintf_fn (stream, "mod"); + break; + case A_RE: + fprintf_fn (stream, "re"); + break; + case A_RS: + fprintf_fn (stream, "rs"); + break; + case A_A0: + fprintf_fn (stream, "a0"); + break; + case A_X0: + fprintf_fn (stream, "x0"); + break; + case A_X1: + fprintf_fn (stream, "x1"); + break; + case A_Y0: + fprintf_fn (stream, "y0"); + break; + case A_Y1: + fprintf_fn (stream, "y1"); + break; + case DSP_REG_M: + print_dsp_reg (rm, fprintf_fn, stream); + break; + case A_SSR: + fprintf_fn (stream, "ssr"); + break; + case A_SPC: + fprintf_fn (stream, "spc"); + break; + case A_MACH: + fprintf_fn (stream, "mach"); + break; + case A_MACL: + fprintf_fn (stream, "macl"); + break; + case A_PR: + fprintf_fn (stream, "pr"); + break; + case A_SGR: + fprintf_fn (stream, "sgr"); + break; + case A_DBR: + fprintf_fn (stream, "dbr"); + break; + case F_REG_N: + fprintf_fn (stream, "fr%d", rn); + break; + case F_REG_M: + fprintf_fn (stream, "fr%d", rm); + break; + case DX_REG_N: + if (rn & 1) + { + fprintf_fn (stream, "xd%d", rn & ~1); + break; + } + case D_REG_N: + fprintf_fn (stream, "dr%d", rn); + break; + case DX_REG_M: + if (rm & 1) + { + fprintf_fn (stream, "xd%d", rm & ~1); + break; + } + case D_REG_M: + fprintf_fn (stream, "dr%d", rm); + break; + case FPSCR_M: + case FPSCR_N: + fprintf_fn (stream, "fpscr"); + break; + case FPUL_M: + case FPUL_N: + fprintf_fn (stream, "fpul"); + break; + case F_FR0: + fprintf_fn (stream, "fr0"); + break; + case V_REG_N: + fprintf_fn (stream, "fv%d", rn * 4); + break; + case V_REG_M: + fprintf_fn (stream, "fv%d", rm * 4); + break; + case XMTRX_M4: + fprintf_fn (stream, "xmtrx"); + break; + default: + abort (); + } + } + +#if 0 + /* This code prints instructions in delay slots on the same line + as the instruction which needs the delay slots. This can be + confusing, since other disassembler don't work this way, and + it means that the instructions are not all in a line. So I + disabled it. Ian. */ + if (!(info->flags & 1) + && (op->name[0] == 'j' + || (op->name[0] == 'b' + && (op->name[1] == 'r' + || op->name[1] == 's')) + || (op->name[0] == 'r' && op->name[1] == 't') + || (op->name[0] == 'b' && op->name[2] == '.'))) + { + info->flags |= 1; + fprintf_fn (stream, "\t(slot "); + print_insn_sh (memaddr + 2, info); + info->flags &= ~1; + fprintf_fn (stream, ")"); + return 4; + } +#endif + + if (disp_pc && strcmp (op->name, "mova") != 0) + { + int size; + bfd_byte bytes[4]; + + if (relmask == ~(bfd_vma) 1) + size = 2; + else + size = 4; + status = info->read_memory_func (disp_pc_addr, bytes, size, info); + if (status == 0) + { + unsigned int val; + + if (size == 2) + { + if (info->endian == BFD_ENDIAN_LITTLE) + val = bfd_getl16 (bytes); + else + val = bfd_getb16 (bytes); + } + else + { + if (info->endian == BFD_ENDIAN_LITTLE) + val = bfd_getl32 (bytes); + else + val = bfd_getb32 (bytes); + } + if ((*info->symbol_at_address_func) (val, info)) + { + fprintf_fn (stream, "\t! "); + (*info->print_address_func) (val, info); + } + else + fprintf_fn (stream, "\t! 0x%x", val); + } + } + + return SH_MERGE_ARCH_SET (op->arch, arch_op32) ? 4 : 2; + fail: + ; + + } + fprintf_fn (stream, ".word 0x%x%x%x%x", nibs[0], nibs[1], nibs[2], nibs[3]); + return 2; +} diff --git a/disas/sparc.c b/disas/sparc.c new file mode 100644 index 0000000..f4e3565 --- /dev/null +++ b/disas/sparc.c @@ -0,0 +1,3237 @@ +/* + * These files from binutils are concatenated: + * include/opcode/sparc.h, opcodes/sparc-opc.c, opcodes/sparc-dis.c + */ + +/* include/opcode/sparc.h */ + +/* Definitions for opcode table for the sparc. + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000, 2002, + 2003, 2005 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler, GDB, the GNU debugger, and + the GNU Binutils. + + GAS/GDB is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GAS/GDB is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS or GDB; see the file COPYING. If not, + see . */ + +#include +#include "disas/bfd.h" + +/* The SPARC opcode table (and other related data) is defined in + the opcodes library in sparc-opc.c. If you change anything here, make + sure you fix up that file, and vice versa. */ + + /* FIXME-someday: perhaps the ,a's and such should be embedded in the + instruction's name rather than the args. This would make gas faster, pinsn + slower, but would mess up some macros a bit. xoxorich. */ + +/* List of instruction sets variations. + These values are such that each element is either a superset of a + preceding each one or they conflict in which case SPARC_OPCODE_CONFLICT_P + returns non-zero. + The values are indices into `sparc_opcode_archs' defined in sparc-opc.c. + Don't change this without updating sparc-opc.c. */ + +enum sparc_opcode_arch_val +{ + SPARC_OPCODE_ARCH_V6 = 0, + SPARC_OPCODE_ARCH_V7, + SPARC_OPCODE_ARCH_V8, + SPARC_OPCODE_ARCH_SPARCLET, + SPARC_OPCODE_ARCH_SPARCLITE, + /* V9 variants must appear last. */ + SPARC_OPCODE_ARCH_V9, + SPARC_OPCODE_ARCH_V9A, /* V9 with ultrasparc additions. */ + SPARC_OPCODE_ARCH_V9B, /* V9 with ultrasparc and cheetah additions. */ + SPARC_OPCODE_ARCH_BAD /* Error return from sparc_opcode_lookup_arch. */ +}; + +/* The highest architecture in the table. */ +#define SPARC_OPCODE_ARCH_MAX (SPARC_OPCODE_ARCH_BAD - 1) + +/* Given an enum sparc_opcode_arch_val, return the bitmask to use in + insn encoding/decoding. */ +#define SPARC_OPCODE_ARCH_MASK(arch) (1 << (arch)) + +/* Given a valid sparc_opcode_arch_val, return non-zero if it's v9. */ +#define SPARC_OPCODE_ARCH_V9_P(arch) ((arch) >= SPARC_OPCODE_ARCH_V9) + +/* Table of cpu variants. */ + +typedef struct sparc_opcode_arch +{ + const char *name; + /* Mask of sparc_opcode_arch_val's supported. + EG: For v7 this would be + (SPARC_OPCODE_ARCH_MASK (..._V6) | SPARC_OPCODE_ARCH_MASK (..._V7)). + These are short's because sparc_opcode.architecture is. */ + short supported; +} sparc_opcode_arch; + +/* Structure of an opcode table entry. */ + +typedef struct sparc_opcode +{ + const char *name; + unsigned long match; /* Bits that must be set. */ + unsigned long lose; /* Bits that must not be set. */ + const char *args; + /* This was called "delayed" in versions before the flags. */ + char flags; + short architecture; /* Bitmask of sparc_opcode_arch_val's. */ +} sparc_opcode; + +#define F_DELAYED 1 /* Delayed branch. */ +#define F_ALIAS 2 /* Alias for a "real" instruction. */ +#define F_UNBR 4 /* Unconditional branch. */ +#define F_CONDBR 8 /* Conditional branch. */ +#define F_JSR 16 /* Subroutine call. */ +#define F_FLOAT 32 /* Floating point instruction (not a branch). */ +#define F_FBR 64 /* Floating point branch. */ +/* FIXME: Add F_ANACHRONISTIC flag for v9. */ + +/* All sparc opcodes are 32 bits, except for the `set' instruction (really a + macro), which is 64 bits. It is handled as a special case. + + The match component is a mask saying which bits must match a particular + opcode in order for an instruction to be an instance of that opcode. + + The args component is a string containing one character for each operand of the + instruction. + + Kinds of operands: + # Number used by optimizer. It is ignored. + 1 rs1 register. + 2 rs2 register. + d rd register. + e frs1 floating point register. + v frs1 floating point register (double/even). + V frs1 floating point register (quad/multiple of 4). + f frs2 floating point register. + B frs2 floating point register (double/even). + R frs2 floating point register (quad/multiple of 4). + g frsd floating point register. + H frsd floating point register (double/even). + J frsd floating point register (quad/multiple of 4). + b crs1 coprocessor register + c crs2 coprocessor register + D crsd coprocessor register + m alternate space register (asr) in rd + M alternate space register (asr) in rs1 + h 22 high bits. + X 5 bit unsigned immediate + Y 6 bit unsigned immediate + 3 SIAM mode (3 bits). (v9b) + K MEMBAR mask (7 bits). (v9) + j 10 bit Immediate. (v9) + I 11 bit Immediate. (v9) + i 13 bit Immediate. + n 22 bit immediate. + k 2+14 bit PC relative immediate. (v9) + G 19 bit PC relative immediate. (v9) + l 22 bit PC relative immediate. + L 30 bit PC relative immediate. + a Annul. The annul bit is set. + A Alternate address space. Stored as 8 bits. + C Coprocessor state register. + F floating point state register. + p Processor state register. + N Branch predict clear ",pn" (v9) + T Branch predict set ",pt" (v9) + z %icc. (v9) + Z %xcc. (v9) + q Floating point queue. + r Single register that is both rs1 and rd. + O Single register that is both rs2 and rd. + Q Coprocessor queue. + S Special case. + t Trap base register. + w Window invalid mask register. + y Y register. + u sparclet coprocessor registers in rd position + U sparclet coprocessor registers in rs1 position + E %ccr. (v9) + s %fprs. (v9) + P %pc. (v9) + W %tick. (v9) + o %asi. (v9) + 6 %fcc0. (v9) + 7 %fcc1. (v9) + 8 %fcc2. (v9) + 9 %fcc3. (v9) + ! Privileged Register in rd (v9) + ? Privileged Register in rs1 (v9) + * Prefetch function constant. (v9) + x OPF field (v9 impdep). + 0 32/64 bit immediate for set or setx (v9) insns + _ Ancillary state register in rd (v9a) + / Ancillary state register in rs1 (v9a) + + The following chars are unused: (note: ,[] are used as punctuation) + [45]. */ + +#define OP2(x) (((x) & 0x7) << 22) /* Op2 field of format2 insns. */ +#define OP3(x) (((x) & 0x3f) << 19) /* Op3 field of format3 insns. */ +#define OP(x) ((unsigned) ((x) & 0x3) << 30) /* Op field of all insns. */ +#define OPF(x) (((x) & 0x1ff) << 5) /* Opf field of float insns. */ +#define OPF_LOW5(x) OPF ((x) & 0x1f) /* V9. */ +#define F3F(x, y, z) (OP (x) | OP3 (y) | OPF (z)) /* Format3 float insns. */ +#define F3I(x) (((x) & 0x1) << 13) /* Immediate field of format 3 insns. */ +#define F2(x, y) (OP (x) | OP2(y)) /* Format 2 insns. */ +#define F3(x, y, z) (OP (x) | OP3(y) | F3I(z)) /* Format3 insns. */ +#define F1(x) (OP (x)) +#define DISP30(x) ((x) & 0x3fffffff) +#define ASI(x) (((x) & 0xff) << 5) /* Asi field of format3 insns. */ +#define RS2(x) ((x) & 0x1f) /* Rs2 field. */ +#define SIMM13(x) ((x) & 0x1fff) /* Simm13 field. */ +#define RD(x) (((x) & 0x1f) << 25) /* Destination register field. */ +#define RS1(x) (((x) & 0x1f) << 14) /* Rs1 field. */ +#define ASI_RS2(x) (SIMM13 (x)) +#define MEMBAR(x) ((x) & 0x7f) +#define SLCPOP(x) (((x) & 0x7f) << 6) /* Sparclet cpop. */ + +#define ANNUL (1 << 29) +#define BPRED (1 << 19) /* V9. */ +#define IMMED F3I (1) +#define RD_G0 RD (~0) +#define RS1_G0 RS1 (~0) +#define RS2_G0 RS2 (~0) + +static const struct sparc_opcode sparc_opcodes[]; + +static const char *sparc_decode_asi_v8 (int); +static const char *sparc_decode_asi_v9 (int); +static const char *sparc_decode_membar (int); +static const char *sparc_decode_prefetch (int); +static const char *sparc_decode_sparclet_cpreg (int); + +/* Local Variables: + fill-column: 131 + comment-column: 0 + End: */ + +/* opcodes/sparc-opc.c */ + +/* Table of opcodes for the sparc. + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2002, 2004, 2005 + Free Software Foundation, Inc. + + This file is part of the BFD library. + + BFD is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, or (at your option) any later + version. + + BFD is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with this software; see the file COPYING. If not, + see . */ + +/* FIXME-someday: perhaps the ,a's and such should be embedded in the + instruction's name rather than the args. This would make gas faster, pinsn + slower, but would mess up some macros a bit. xoxorich. */ + +/* Some defines to make life easy. */ +#define MASK_V6 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V6) +#define MASK_V7 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V7) +#define MASK_V8 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8) +#define MASK_SPARCLET SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLET) +#define MASK_SPARCLITE SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLITE) +#define MASK_V9 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9) +#define MASK_V9A SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A) +#define MASK_V9B SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9B) + +/* Bit masks of architectures supporting the insn. */ + +#define v6 (MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLET \ + | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B) +/* v6 insns not supported on the sparclet. */ +#define v6notlet (MASK_V6 | MASK_V7 | MASK_V8 \ + | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B) +#define v7 (MASK_V7 | MASK_V8 | MASK_SPARCLET \ + | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B) +/* Although not all insns are implemented in hardware, sparclite is defined + to be a superset of v8. Unimplemented insns trap and are then theoretically + implemented in software. + It's not clear that the same is true for sparclet, although the docs + suggest it is. Rather than complicating things, the sparclet assembler + recognizes all v8 insns. */ +#define v8 (MASK_V8 | MASK_SPARCLET | MASK_SPARCLITE \ + | MASK_V9 | MASK_V9A | MASK_V9B) +#define sparclet (MASK_SPARCLET) +#define sparclite (MASK_SPARCLITE) +#define v9 (MASK_V9 | MASK_V9A | MASK_V9B) +#define v9a (MASK_V9A | MASK_V9B) +#define v9b (MASK_V9B) +/* v6 insns not supported by v9. */ +#define v6notv9 (MASK_V6 | MASK_V7 | MASK_V8 \ + | MASK_SPARCLET | MASK_SPARCLITE) +/* v9a instructions which would appear to be aliases to v9's impdep's + otherwise. */ +#define v9notv9a (MASK_V9) + +/* Branch condition field. */ +#define COND(x) (((x) & 0xf) << 25) + +/* v9: Move (MOVcc and FMOVcc) condition field. */ +#define MCOND(x,i_or_f) ((((i_or_f) & 1) << 18) | (((x) >> 11) & (0xf << 14))) /* v9 */ + +/* v9: Move register (MOVRcc and FMOVRcc) condition field. */ +#define RCOND(x) (((x) & 0x7) << 10) /* v9 */ + +#define CONDA (COND (0x8)) +#define CONDCC (COND (0xd)) +#define CONDCS (COND (0x5)) +#define CONDE (COND (0x1)) +#define CONDG (COND (0xa)) +#define CONDGE (COND (0xb)) +#define CONDGU (COND (0xc)) +#define CONDL (COND (0x3)) +#define CONDLE (COND (0x2)) +#define CONDLEU (COND (0x4)) +#define CONDN (COND (0x0)) +#define CONDNE (COND (0x9)) +#define CONDNEG (COND (0x6)) +#define CONDPOS (COND (0xe)) +#define CONDVC (COND (0xf)) +#define CONDVS (COND (0x7)) + +#define CONDNZ CONDNE +#define CONDZ CONDE +#define CONDGEU CONDCC +#define CONDLU CONDCS + +#define FCONDA (COND (0x8)) +#define FCONDE (COND (0x9)) +#define FCONDG (COND (0x6)) +#define FCONDGE (COND (0xb)) +#define FCONDL (COND (0x4)) +#define FCONDLE (COND (0xd)) +#define FCONDLG (COND (0x2)) +#define FCONDN (COND (0x0)) +#define FCONDNE (COND (0x1)) +#define FCONDO (COND (0xf)) +#define FCONDU (COND (0x7)) +#define FCONDUE (COND (0xa)) +#define FCONDUG (COND (0x5)) +#define FCONDUGE (COND (0xc)) +#define FCONDUL (COND (0x3)) +#define FCONDULE (COND (0xe)) + +#define FCONDNZ FCONDNE +#define FCONDZ FCONDE + +#define ICC (0) /* v9 */ +#define XCC (1 << 12) /* v9 */ +#define FCC(x) (((x) & 0x3) << 11) /* v9 */ +#define FBFCC(x) (((x) & 0x3) << 20) /* v9 */ + +/* The order of the opcodes in the table is significant: + + * The assembler requires that all instances of the same mnemonic must + be consecutive. If they aren't, the assembler will bomb at runtime. + + * The disassembler should not care about the order of the opcodes. */ + +/* Entries for commutative arithmetic operations. */ +/* ??? More entries can make use of this. */ +#define COMMUTEOP(opcode, op3, arch_mask) \ +{ opcode, F3(2, op3, 0), F3(~2, ~op3, ~0)|ASI(~0), "1,2,d", 0, arch_mask }, \ +{ opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "1,i,d", 0, arch_mask }, \ +{ opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "i,1,d", 0, arch_mask } + +static const struct sparc_opcode sparc_opcodes[] = { + +{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", 0, v6 }, +{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", 0, v6 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", 0, v6 }, +{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", 0, v6 }, +{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ld [rs1+0],d */ +{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0), "[1+2],g", 0, v6 }, +{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0)|RS2_G0, "[1],g", 0, v6 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[1+i],g", 0, v6 }, +{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[i+1],g", 0, v6 }, +{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|RS1_G0, "[i],g", 0, v6 }, +{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|SIMM13(~0), "[1],g", 0, v6 }, /* ld [rs1+0],d */ + +{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RD(~0), "[1+2],F", 0, v6 }, +{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RS2_G0|RD(~0),"[1],F", 0, v6 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RD(~0), "[1+i],F", 0, v6 }, +{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RD(~0), "[i+1],F", 0, v6 }, +{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RS1_G0|RD(~0),"[i],F", 0, v6 }, +{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|SIMM13(~0)|RD(~0),"[1],F", 0, v6 }, /* ld [rs1+0],d */ + +{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2],D", 0, v6notv9 }, +{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1],D", 0, v6notv9 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i],D", 0, v6notv9 }, +{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1],D", 0, v6notv9 }, +{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i],D", 0, v6notv9 }, +{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1],D", 0, v6notv9 }, /* ld [rs1+0],d */ +{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0), "[1+2],C", 0, v6notv9 }, +{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0)|RS2_G0, "[1],C", 0, v6notv9 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[1+i],C", 0, v6notv9 }, +{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[i+1],C", 0, v6notv9 }, +{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|RS1_G0, "[i],C", 0, v6notv9 }, +{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|SIMM13(~0), "[1],C", 0, v6notv9 }, /* ld [rs1+0],d */ + +/* The v9 LDUW is the same as the old 'ld' opcode, it is not the same as the + 'ld' pseudo-op in v9. */ +{ "lduw", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", F_ALIAS, v9 }, +{ "lduw", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", F_ALIAS, v9 }, /* ld [rs1+%g0],d */ +{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", F_ALIAS, v9 }, +{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", F_ALIAS, v9 }, +{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", F_ALIAS, v9 }, +{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", F_ALIAS, v9 }, /* ld [rs1+0],d */ + +{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldd [rs1+%g0],d */ +{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[1+i],d", 0, v6 }, +{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[i+1],d", 0, v6 }, +{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldd [rs1+0],d */ +{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI(~0), "[1+2],H", 0, v6 }, +{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI_RS2(~0), "[1],H", 0, v6 }, /* ldd [rs1+%g0],d */ +{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[1+i],H", 0, v6 }, +{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[i+1],H", 0, v6 }, +{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|RS1_G0, "[i],H", 0, v6 }, +{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|SIMM13(~0), "[1],H", 0, v6 }, /* ldd [rs1+0],d */ + +{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI(~0), "[1+2],D", 0, v6notv9 }, +{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI_RS2(~0), "[1],D", 0, v6notv9 }, /* ldd [rs1+%g0],d */ +{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i],D", 0, v6notv9 }, +{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1],D", 0, v6notv9 }, +{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i],D", 0, v6notv9 }, +{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1],D", 0, v6notv9 }, /* ldd [rs1+0],d */ + +{ "ldq", F3(3, 0x22, 0), F3(~3, ~0x22, ~0)|ASI(~0), "[1+2],J", 0, v9 }, +{ "ldq", F3(3, 0x22, 0), F3(~3, ~0x22, ~0)|ASI_RS2(~0), "[1],J", 0, v9 }, /* ldd [rs1+%g0],d */ +{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1), "[1+i],J", 0, v9 }, +{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1), "[i+1],J", 0, v9 }, +{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1)|RS1_G0, "[i],J", 0, v9 }, +{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1)|SIMM13(~0), "[1],J", 0, v9 }, /* ldd [rs1+0],d */ + +{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsb [rs1+%g0],d */ +{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[1+i],d", 0, v6 }, +{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[i+1],d", 0, v6 }, +{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsb [rs1+0],d */ + +{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsh [rs1+%g0],d */ +{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[1+i],d", 0, v6 }, +{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[i+1],d", 0, v6 }, +{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsh [rs1+0],d */ + +{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldstub [rs1+%g0],d */ +{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[1+i],d", 0, v6 }, +{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[i+1],d", 0, v6 }, +{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldstub [rs1+0],d */ + +{ "ldsw", F3(3, 0x08, 0), F3(~3, ~0x08, ~0)|ASI(~0), "[1+2],d", 0, v9 }, +{ "ldsw", F3(3, 0x08, 0), F3(~3, ~0x08, ~0)|ASI_RS2(~0), "[1],d", 0, v9 }, /* ldsw [rs1+%g0],d */ +{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1), "[1+i],d", 0, v9 }, +{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1), "[i+1],d", 0, v9 }, +{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1)|RS1_G0, "[i],d", 0, v9 }, +{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1)|SIMM13(~0), "[1],d", 0, v9 }, /* ldsw [rs1+0],d */ + +{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldub [rs1+%g0],d */ +{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[1+i],d", 0, v6 }, +{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[i+1],d", 0, v6 }, +{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldub [rs1+0],d */ + +{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* lduh [rs1+%g0],d */ +{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[1+i],d", 0, v6 }, +{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[i+1],d", 0, v6 }, +{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* lduh [rs1+0],d */ + +{ "ldx", F3(3, 0x0b, 0), F3(~3, ~0x0b, ~0)|ASI(~0), "[1+2],d", 0, v9 }, +{ "ldx", F3(3, 0x0b, 0), F3(~3, ~0x0b, ~0)|ASI_RS2(~0), "[1],d", 0, v9 }, /* ldx [rs1+%g0],d */ +{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1), "[1+i],d", 0, v9 }, +{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1), "[i+1],d", 0, v9 }, +{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1)|RS1_G0, "[i],d", 0, v9 }, +{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1)|SIMM13(~0), "[1],d", 0, v9 }, /* ldx [rs1+0],d */ + +{ "ldx", F3(3, 0x21, 0)|RD(1), F3(~3, ~0x21, ~0)|RD(~1), "[1+2],F", 0, v9 }, +{ "ldx", F3(3, 0x21, 0)|RD(1), F3(~3, ~0x21, ~0)|RS2_G0|RD(~1), "[1],F", 0, v9 }, /* ld [rs1+%g0],d */ +{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RD(~1), "[1+i],F", 0, v9 }, +{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RD(~1), "[i+1],F", 0, v9 }, +{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RS1_G0|RD(~1), "[i],F", 0, v9 }, +{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|SIMM13(~0)|RD(~1),"[1],F", 0, v9 }, /* ld [rs1+0],d */ + +{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", 0, v6 }, +{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lda [rs1+%g0],d */ +{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[1+i]o,d", 0, v9 }, +{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[i+1]o,d", 0, v9 }, +{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ +{ "lda", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2]A,g", 0, v9 }, +{ "lda", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1]A,g", 0, v9 }, /* lda [rs1+%g0],d */ +{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i]o,g", 0, v9 }, +{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1]o,g", 0, v9 }, +{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i]o,g", 0, v9 }, +{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1]o,g", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0), "[1+2]A,d", 0, v6 }, +{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldda [rs1+%g0],d */ +{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1), "[1+i]o,d", 0, v9 }, +{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1), "[i+1]o,d", 0, v9 }, +{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldda", F3(3, 0x33, 0), F3(~3, ~0x33, ~0), "[1+2]A,H", 0, v9 }, +{ "ldda", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|RS2_G0, "[1]A,H", 0, v9 }, /* ldda [rs1+%g0],d */ +{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i]o,H", 0, v9 }, +{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1]o,H", 0, v9 }, +{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i]o,H", 0, v9 }, +{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1]o,H", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldqa", F3(3, 0x32, 0), F3(~3, ~0x32, ~0), "[1+2]A,J", 0, v9 }, +{ "ldqa", F3(3, 0x32, 0), F3(~3, ~0x32, ~0)|RS2_G0, "[1]A,J", 0, v9 }, /* ldd [rs1+%g0],d */ +{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1), "[1+i]o,J", 0, v9 }, +{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1), "[i+1]o,J", 0, v9 }, +{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1)|RS1_G0, "[i]o,J", 0, v9 }, +{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1)|SIMM13(~0), "[1]o,J", 0, v9 }, /* ldd [rs1+0],d */ + +{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0), "[1+2]A,d", 0, v6 }, +{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsba [rs1+%g0],d */ +{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1), "[1+i]o,d", 0, v9 }, +{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1), "[i+1]o,d", 0, v9 }, +{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0), "[1+2]A,d", 0, v6 }, +{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsha [rs1+%g0],d */ +{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1), "[1+i]o,d", 0, v9 }, +{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1), "[i+1]o,d", 0, v9 }, +{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0), "[1+2]A,d", 0, v6 }, +{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldstuba [rs1+%g0],d */ +{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1), "[1+i]o,d", 0, v9 }, +{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1), "[i+1]o,d", 0, v9 }, +{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldswa", F3(3, 0x18, 0), F3(~3, ~0x18, ~0), "[1+2]A,d", 0, v9 }, +{ "ldswa", F3(3, 0x18, 0), F3(~3, ~0x18, ~0)|RS2_G0, "[1]A,d", 0, v9 }, /* lda [rs1+%g0],d */ +{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1), "[1+i]o,d", 0, v9 }, +{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1), "[i+1]o,d", 0, v9 }, +{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0), "[1+2]A,d", 0, v6 }, +{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduba [rs1+%g0],d */ +{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1), "[1+i]o,d", 0, v9 }, +{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1), "[i+1]o,d", 0, v9 }, +{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0), "[1+2]A,d", 0, v6 }, +{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduha [rs1+%g0],d */ +{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1), "[1+i]o,d", 0, v9 }, +{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1), "[i+1]o,d", 0, v9 }, +{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "lduwa", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", F_ALIAS, v9 }, /* lduwa === lda */ +{ "lduwa", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", F_ALIAS, v9 }, /* lda [rs1+%g0],d */ +{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[1+i]o,d", F_ALIAS, v9 }, +{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[i+1]o,d", F_ALIAS, v9 }, +{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|RS1_G0, "[i]o,d", F_ALIAS, v9 }, +{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|SIMM13(~0), "[1]o,d", F_ALIAS, v9 }, /* ld [rs1+0],d */ + +{ "ldxa", F3(3, 0x1b, 0), F3(~3, ~0x1b, ~0), "[1+2]A,d", 0, v9 }, +{ "ldxa", F3(3, 0x1b, 0), F3(~3, ~0x1b, ~0)|RS2_G0, "[1]A,d", 0, v9 }, /* lda [rs1+%g0],d */ +{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1), "[1+i]o,d", 0, v9 }, +{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1), "[i+1]o,d", 0, v9 }, +{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", 0, v6 }, +{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* st d,[rs1+%g0] */ +{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", 0, v6 }, +{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", 0, v6 }, +{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", 0, v6 }, +{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* st d,[rs1+0] */ +{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI(~0), "g,[1+2]", 0, v6 }, +{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI_RS2(~0), "g,[1]", 0, v6 }, /* st d[rs1+%g0] */ +{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[1+i]", 0, v6 }, +{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[i+1]", 0, v6 }, +{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|RS1_G0, "g,[i]", 0, v6 }, +{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|SIMM13(~0), "g,[1]", 0, v6 }, /* st d,[rs1+0] */ + +{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI(~0), "D,[1+2]", 0, v6notv9 }, +{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI_RS2(~0), "D,[1]", 0, v6notv9 }, /* st d,[rs1+%g0] */ +{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[1+i]", 0, v6notv9 }, +{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[i+1]", 0, v6notv9 }, +{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "D,[i]", 0, v6notv9 }, +{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "D,[1]", 0, v6notv9 }, /* st d,[rs1+0] */ +{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI(~0), "C,[1+2]", 0, v6notv9 }, +{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI_RS2(~0), "C,[1]", 0, v6notv9 }, /* st d,[rs1+%g0] */ +{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[1+i]", 0, v6notv9 }, +{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[i+1]", 0, v6notv9 }, +{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|RS1_G0, "C,[i]", 0, v6notv9 }, +{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|SIMM13(~0), "C,[1]", 0, v6notv9 }, /* st d,[rs1+0] */ + +{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI(~0), "F,[1+2]", 0, v6 }, +{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI_RS2(~0), "F,[1]", 0, v6 }, /* st d,[rs1+%g0] */ +{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[1+i]", 0, v6 }, +{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[i+1]", 0, v6 }, +{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|RS1_G0, "F,[i]", 0, v6 }, +{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|SIMM13(~0), "F,[1]", 0, v6 }, /* st d,[rs1+0] */ + +{ "stw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 }, +{ "stw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */ +{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 }, +{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 }, +{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 }, +{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */ +{ "stsw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 }, +{ "stsw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */ +{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 }, +{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 }, +{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 }, +{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */ +{ "stuw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 }, +{ "stuw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */ +{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 }, +{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 }, +{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 }, +{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */ + +{ "spill", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 }, +{ "spill", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* st d,[rs1+%g0] */ +{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v6 }, +{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v6 }, +{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 }, +{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* st d,[rs1+0] */ + +{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", 0, v6 }, +{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* sta d,[rs1+%g0] */ +{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", 0, v9 }, +{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", 0, v9 }, +{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* st d,[rs1+0] */ + +{ "sta", F3(3, 0x34, 0), F3(~3, ~0x34, ~0), "g,[1+2]A", 0, v9 }, +{ "sta", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|RS2(~0), "g,[1]A", 0, v9 }, /* sta d,[rs1+%g0] */ +{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "g,[1+i]o", 0, v9 }, +{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "g,[i+1]o", 0, v9 }, +{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "g,[i]o", 0, v9 }, +{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "g,[1]o", 0, v9 }, /* st d,[rs1+0] */ + +{ "stwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 }, +{ "stwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */ +{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 }, +{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 }, +{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 }, +{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */ +{ "stswa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 }, +{ "stswa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */ +{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 }, +{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 }, +{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 }, +{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */ +{ "stuwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 }, +{ "stuwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */ +{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 }, +{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 }, +{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 }, +{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */ + +{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", 0, v6 }, +{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+%g0] */ +{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", 0, v6 }, +{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", 0, v6 }, +{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", 0, v6 }, +{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+0] */ + +{ "stsb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 }, +{ "stsb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+%g0] */ +{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", F_ALIAS, v6 }, +{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", F_ALIAS, v6 }, +{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 }, +{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+0] */ +{ "stub", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 }, +{ "stub", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+%g0] */ +{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", F_ALIAS, v6 }, +{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", F_ALIAS, v6 }, +{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 }, +{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+0] */ + +{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", 0, v6 }, +{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stba d,[rs1+%g0] */ +{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", 0, v9 }, +{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", 0, v9 }, +{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* stb d,[rs1+0] */ + +{ "stsba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", F_ALIAS, v6 }, +{ "stsba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stba d,[rs1+%g0] */ +{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", F_ALIAS, v9 }, +{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", F_ALIAS, v9 }, +{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 }, +{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* stb d,[rs1+0] */ +{ "stuba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", F_ALIAS, v6 }, +{ "stuba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stba d,[rs1+%g0] */ +{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", F_ALIAS, v9 }, +{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", F_ALIAS, v9 }, +{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 }, +{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* stb d,[rs1+0] */ + +{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI(~0), "d,[1+2]", 0, v6 }, +{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[1+i]", 0, v6 }, +{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[i+1]", 0, v6 }, +{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|RS1_G0, "d,[i]", 0, v6 }, +{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* std d,[rs1+0] */ + +{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "q,[1+2]", 0, v6notv9 }, +{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "q,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[1+i]", 0, v6notv9 }, +{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[i+1]", 0, v6notv9 }, +{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "q,[i]", 0, v6notv9 }, +{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "q,[1]", 0, v6notv9 }, /* std d,[rs1+0] */ +{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI(~0), "H,[1+2]", 0, v6 }, +{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI_RS2(~0), "H,[1]", 0, v6 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "H,[1+i]", 0, v6 }, +{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "H,[i+1]", 0, v6 }, +{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|RS1_G0, "H,[i]", 0, v6 }, +{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|SIMM13(~0), "H,[1]", 0, v6 }, /* std d,[rs1+0] */ + +{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "Q,[1+2]", 0, v6notv9 }, +{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "Q,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[1+i]", 0, v6notv9 }, +{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[i+1]", 0, v6notv9 }, +{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "Q,[i]", 0, v6notv9 }, +{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "Q,[1]", 0, v6notv9 }, /* std d,[rs1+0] */ +{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI(~0), "D,[1+2]", 0, v6notv9 }, +{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI_RS2(~0), "D,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[1+i]", 0, v6notv9 }, +{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[i+1]", 0, v6notv9 }, +{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "D,[i]", 0, v6notv9 }, +{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "D,[1]", 0, v6notv9 }, /* std d,[rs1+0] */ + +{ "spilld", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 }, +{ "spilld", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* std d,[rs1+%g0] */ +{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[1+i]", F_ALIAS, v6 }, +{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[i+1]", F_ALIAS, v6 }, +{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 }, +{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* std d,[rs1+0] */ + +{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0), "d,[1+2]A", 0, v6 }, +{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stda d,[rs1+%g0] */ +{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1), "d,[1+i]o", 0, v9 }, +{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1), "d,[i+1]o", 0, v9 }, +{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* std d,[rs1+0] */ +{ "stda", F3(3, 0x37, 0), F3(~3, ~0x37, ~0), "H,[1+2]A", 0, v9 }, +{ "stda", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|RS2(~0), "H,[1]A", 0, v9 }, /* stda d,[rs1+%g0] */ +{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "H,[1+i]o", 0, v9 }, +{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "H,[i+1]o", 0, v9 }, +{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "H,[i]o", 0, v9 }, +{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "H,[1]o", 0, v9 }, /* std d,[rs1+0] */ + +{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", 0, v6 }, +{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+%g0] */ +{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", 0, v6 }, +{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", 0, v6 }, +{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", 0, v6 }, +{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+0] */ + +{ "stsh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 }, +{ "stsh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+%g0] */ +{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", F_ALIAS, v6 }, +{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", F_ALIAS, v6 }, +{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 }, +{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+0] */ +{ "stuh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 }, +{ "stuh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+%g0] */ +{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", F_ALIAS, v6 }, +{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", F_ALIAS, v6 }, +{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 }, +{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+0] */ + +{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", 0, v6 }, +{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stha ,[rs1+%g0] */ +{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", 0, v9 }, +{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", 0, v9 }, +{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* sth d,[rs1+0] */ + +{ "stsha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", F_ALIAS, v6 }, +{ "stsha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stha ,[rs1+%g0] */ +{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", F_ALIAS, v9 }, +{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", F_ALIAS, v9 }, +{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 }, +{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* sth d,[rs1+0] */ +{ "stuha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", F_ALIAS, v6 }, +{ "stuha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stha ,[rs1+%g0] */ +{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", F_ALIAS, v9 }, +{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", F_ALIAS, v9 }, +{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 }, +{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* sth d,[rs1+0] */ + +{ "stx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|ASI(~0), "d,[1+2]", 0, v9 }, +{ "stx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|ASI_RS2(~0), "d,[1]", 0, v9 }, /* stx d,[rs1+%g0] */ +{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1), "d,[1+i]", 0, v9 }, +{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1), "d,[i+1]", 0, v9 }, +{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RS1_G0, "d,[i]", 0, v9 }, +{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|SIMM13(~0), "d,[1]", 0, v9 }, /* stx d,[rs1+0] */ + +{ "stx", F3(3, 0x25, 0)|RD(1), F3(~3, ~0x25, ~0)|ASI(~0)|RD(~1), "F,[1+2]", 0, v9 }, +{ "stx", F3(3, 0x25, 0)|RD(1), F3(~3, ~0x25, ~0)|ASI_RS2(~0)|RD(~1),"F,[1]", 0, v9 }, /* stx d,[rs1+%g0] */ +{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RD(~1), "F,[1+i]", 0, v9 }, +{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RD(~1), "F,[i+1]", 0, v9 }, +{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RS1_G0|RD(~1), "F,[i]", 0, v9 }, +{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|SIMM13(~0)|RD(~1),"F,[1]", 0, v9 }, /* stx d,[rs1+0] */ + +{ "stxa", F3(3, 0x1e, 0), F3(~3, ~0x1e, ~0), "d,[1+2]A", 0, v9 }, +{ "stxa", F3(3, 0x1e, 0), F3(~3, ~0x1e, ~0)|RS2(~0), "d,[1]A", 0, v9 }, /* stxa d,[rs1+%g0] */ +{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1), "d,[1+i]o", 0, v9 }, +{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1), "d,[i+1]o", 0, v9 }, +{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* stx d,[rs1+0] */ + +{ "stq", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "J,[1+2]", 0, v9 }, +{ "stq", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "J,[1]", 0, v9 }, /* stq [rs1+%g0] */ +{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "J,[1+i]", 0, v9 }, +{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "J,[i+1]", 0, v9 }, +{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "J,[i]", 0, v9 }, +{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "J,[1]", 0, v9 }, /* stq [rs1+0] */ + +{ "stqa", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "J,[1+2]A", 0, v9 }, +{ "stqa", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "J,[1]A", 0, v9 }, /* stqa [rs1+%g0] */ +{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "J,[1+i]o", 0, v9 }, +{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "J,[i+1]o", 0, v9 }, +{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "J,[i]o", 0, v9 }, +{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "J,[1]o", 0, v9 }, /* stqa [rs1+0] */ + +{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI(~0), "[1+2],d", 0, v7 }, +{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI_RS2(~0), "[1],d", 0, v7 }, /* swap [rs1+%g0],d */ +{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[1+i],d", 0, v7 }, +{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[i+1],d", 0, v7 }, +{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|RS1_G0, "[i],d", 0, v7 }, +{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|SIMM13(~0), "[1],d", 0, v7 }, /* swap [rs1+0],d */ + +{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0), "[1+2]A,d", 0, v7 }, +{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0)|RS2(~0), "[1]A,d", 0, v7 }, /* swapa [rs1+%g0],d */ +{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1), "[1+i]o,d", 0, v9 }, +{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1), "[i+1]o,d", 0, v9 }, +{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* swap [rs1+0],d */ + +{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v6 }, /* restore %g0,%g0,%g0 */ +{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1), "1,i,d", 0, v6 }, +{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1)|RD_G0|RS1_G0|SIMM13(~0), "", 0, v6 }, /* restore %g0,0,%g0 */ + +{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI(~0), "1+2", F_UNBR|F_DELAYED, v6 }, /* rett rs1+rs2 */ +{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI_RS2(~0), "1", F_UNBR|F_DELAYED, v6 }, /* rett rs1,%g0 */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "1+i", F_UNBR|F_DELAYED, v6 }, /* rett rs1+X */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "i+1", F_UNBR|F_DELAYED, v6 }, /* rett X+rs1 */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* rett X+rs1 */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* rett X */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|SIMM13(~0), "1", F_UNBR|F_DELAYED, v6 }, /* rett rs1+0 */ + +{ "save", F3(2, 0x3c, 0), F3(~2, ~0x3c, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "save", F3(2, 0x3c, 1), F3(~2, ~0x3c, ~1), "1,i,d", 0, v6 }, +{ "save", 0x81e00000, ~0x81e00000, "", F_ALIAS, v6 }, + +{ "ret", F3(2, 0x38, 1)|RS1(0x1f)|SIMM13(8), F3(~2, ~0x38, ~1)|SIMM13(~8), "", F_UNBR|F_DELAYED, v6 }, /* jmpl %i7+8,%g0 */ +{ "retl", F3(2, 0x38, 1)|RS1(0x0f)|SIMM13(8), F3(~2, ~0x38, ~1)|RS1(~0x0f)|SIMM13(~8), "", F_UNBR|F_DELAYED, v6 }, /* jmpl %o7+8,%g0 */ + +{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI(~0), "1+2,d", F_JSR|F_DELAYED, v6 }, +{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI_RS2(~0), "1,d", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+%g0,d */ +{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|SIMM13(~0), "1,d", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+0,d */ +{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RS1_G0, "i,d", F_JSR|F_DELAYED, v6 }, /* jmpl %g0+i,d */ +{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "1+i,d", F_JSR|F_DELAYED, v6 }, +{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "i+1,d", F_JSR|F_DELAYED, v6 }, + +{ "done", F3(2, 0x3e, 0)|RD(0), F3(~2, ~0x3e, ~0)|RD(~0)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "retry", F3(2, 0x3e, 0)|RD(1), F3(~2, ~0x3e, ~0)|RD(~1)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "saved", F3(2, 0x31, 0)|RD(0), F3(~2, ~0x31, ~0)|RD(~0)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "restored", F3(2, 0x31, 0)|RD(1), F3(~2, ~0x31, ~0)|RD(~1)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "allclean", F3(2, 0x31, 0)|RD(2), F3(~2, ~0x31, ~0)|RD(~2)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "otherw", F3(2, 0x31, 0)|RD(3), F3(~2, ~0x31, ~0)|RD(~3)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "normalw", F3(2, 0x31, 0)|RD(4), F3(~2, ~0x31, ~0)|RD(~4)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "invalw", F3(2, 0x31, 0)|RD(5), F3(~2, ~0x31, ~0)|RD(~5)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "sir", F3(2, 0x30, 1)|RD(0xf), F3(~2, ~0x30, ~1)|RD(~0xf)|RS1_G0, "i", 0, v9 }, + +{ "flush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", 0, v8 }, +{ "flush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI_RS2(~0), "1", 0, v8 }, /* flush rs1+%g0 */ +{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|SIMM13(~0), "1", 0, v8 }, /* flush rs1+0 */ +{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", 0, v8 }, /* flush %g0+i */ +{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", 0, v8 }, +{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", 0, v8 }, + +/* IFLUSH was renamed to FLUSH in v8. */ +{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", F_ALIAS, v6 }, +{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI_RS2(~0), "1", F_ALIAS, v6 }, /* flush rs1+%g0 */ +{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|SIMM13(~0), "1", F_ALIAS, v6 }, /* flush rs1+0 */ +{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", F_ALIAS, v6 }, +{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", F_ALIAS, v6 }, +{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", F_ALIAS, v6 }, + +{ "return", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|ASI(~0), "1+2", 0, v9 }, +{ "return", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|ASI_RS2(~0), "1", 0, v9 }, /* return rs1+%g0 */ +{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|SIMM13(~0), "1", 0, v9 }, /* return rs1+0 */ +{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RS1_G0, "i", 0, v9 }, /* return %g0+i */ +{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1), "1+i", 0, v9 }, +{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1), "i+1", 0, v9 }, + +{ "flushw", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v9 }, + +{ "membar", F3(2, 0x28, 1)|RS1(0xf), F3(~2, ~0x28, ~1)|RD_G0|RS1(~0xf)|SIMM13(~127), "K", 0, v9 }, +{ "stbar", F3(2, 0x28, 0)|RS1(0xf), F3(~2, ~0x28, ~0)|RD_G0|RS1(~0xf)|SIMM13(~0), "", 0, v8 }, + +{ "prefetch", F3(3, 0x2d, 0), F3(~3, ~0x2d, ~0), "[1+2],*", 0, v9 }, +{ "prefetch", F3(3, 0x2d, 0), F3(~3, ~0x2d, ~0)|RS2_G0, "[1],*", 0, v9 }, /* prefetch [rs1+%g0],prefetch_fcn */ +{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1), "[1+i],*", 0, v9 }, +{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1), "[i+1],*", 0, v9 }, +{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1)|RS1_G0, "[i],*", 0, v9 }, +{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1)|SIMM13(~0), "[1],*", 0, v9 }, /* prefetch [rs1+0],prefetch_fcn */ +{ "prefetcha", F3(3, 0x3d, 0), F3(~3, ~0x3d, ~0), "[1+2]A,*", 0, v9 }, +{ "prefetcha", F3(3, 0x3d, 0), F3(~3, ~0x3d, ~0)|RS2_G0, "[1]A,*", 0, v9 }, /* prefetcha [rs1+%g0],prefetch_fcn */ +{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1), "[1+i]o,*", 0, v9 }, +{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1), "[i+1]o,*", 0, v9 }, +{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1)|RS1_G0, "[i]o,*", 0, v9 }, +{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1)|SIMM13(~0), "[1]o,*", 0, v9 }, /* prefetcha [rs1+0],d */ + +{ "sll", F3(2, 0x25, 0), F3(~2, ~0x25, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 }, +{ "sll", F3(2, 0x25, 1), F3(~2, ~0x25, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 }, +{ "sra", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 }, +{ "sra", F3(2, 0x27, 1), F3(~2, ~0x27, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 }, +{ "srl", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 }, +{ "srl", F3(2, 0x26, 1), F3(~2, ~0x26, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 }, + +{ "sllx", F3(2, 0x25, 0)|(1<<12), F3(~2, ~0x25, ~0)|(0x7f<<5), "1,2,d", 0, v9 }, +{ "sllx", F3(2, 0x25, 1)|(1<<12), F3(~2, ~0x25, ~1)|(0x3f<<6), "1,Y,d", 0, v9 }, +{ "srax", F3(2, 0x27, 0)|(1<<12), F3(~2, ~0x27, ~0)|(0x7f<<5), "1,2,d", 0, v9 }, +{ "srax", F3(2, 0x27, 1)|(1<<12), F3(~2, ~0x27, ~1)|(0x3f<<6), "1,Y,d", 0, v9 }, +{ "srlx", F3(2, 0x26, 0)|(1<<12), F3(~2, ~0x26, ~0)|(0x7f<<5), "1,2,d", 0, v9 }, +{ "srlx", F3(2, 0x26, 1)|(1<<12), F3(~2, ~0x26, ~1)|(0x3f<<6), "1,Y,d", 0, v9 }, + +{ "mulscc", F3(2, 0x24, 0), F3(~2, ~0x24, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "mulscc", F3(2, 0x24, 1), F3(~2, ~0x24, ~1), "1,i,d", 0, v6 }, + +{ "divscc", F3(2, 0x1d, 0), F3(~2, ~0x1d, ~0)|ASI(~0), "1,2,d", 0, sparclite }, +{ "divscc", F3(2, 0x1d, 1), F3(~2, ~0x1d, ~1), "1,i,d", 0, sparclite }, + +{ "scan", F3(2, 0x2c, 0), F3(~2, ~0x2c, ~0)|ASI(~0), "1,2,d", 0, sparclet|sparclite }, +{ "scan", F3(2, 0x2c, 1), F3(~2, ~0x2c, ~1), "1,i,d", 0, sparclet|sparclite }, + +{ "popc", F3(2, 0x2e, 0), F3(~2, ~0x2e, ~0)|RS1_G0|ASI(~0),"2,d", 0, v9 }, +{ "popc", F3(2, 0x2e, 1), F3(~2, ~0x2e, ~1)|RS1_G0, "i,d", 0, v9 }, + +{ "clr", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "d", F_ALIAS, v6 }, /* or %g0,%g0,d */ +{ "clr", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0|SIMM13(~0), "d", F_ALIAS, v6 }, /* or %g0,0,d */ +{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 }, +{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+%g0] */ +{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 }, +{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 }, +{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 }, +{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+0] */ + +{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 }, +{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+%g0] */ +{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 }, +{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 }, +{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 }, +{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+0] */ + +{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 }, +{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+%g0] */ +{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 }, +{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 }, +{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 }, +{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+0] */ + +{ "clrx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v9 }, +{ "clrx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v9 }, /* stx %g0,[rs1+%g0] */ +{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0, "[1+i]", F_ALIAS, v9 }, +{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0, "[i+1]", F_ALIAS, v9 }, +{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v9 }, +{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v9 }, /* stx %g0,[rs1+0] */ + +{ "orcc", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "1,i,d", 0, v6 }, +{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "i,1,d", 0, v6 }, + +/* This is not a commutative instruction. */ +{ "orncc", F3(2, 0x16, 0), F3(~2, ~0x16, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "orncc", F3(2, 0x16, 1), F3(~2, ~0x16, ~1), "1,i,d", 0, v6 }, + +/* This is not a commutative instruction. */ +{ "orn", F3(2, 0x06, 0), F3(~2, ~0x06, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "orn", F3(2, 0x06, 1), F3(~2, ~0x06, ~1), "1,i,d", 0, v6 }, + +{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|ASI_RS2(~0), "1", 0, v6 }, /* orcc rs1, %g0, %g0 */ +{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|RS1_G0|ASI(~0), "2", 0, v6 }, /* orcc %g0, rs2, %g0 */ +{ "tst", F3(2, 0x12, 1), F3(~2, ~0x12, ~1)|RD_G0|SIMM13(~0), "1", 0, v6 }, /* orcc rs1, 0, %g0 */ + +{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", 0, v8 }, /* wr r,r,%asrX */ +{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", 0, v8 }, /* wr r,i,%asrX */ +{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,%g0,%asrX */ +{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", 0, v6 }, /* wr r,r,%y */ +{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", 0, v6 }, /* wr r,i,%y */ +{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */ +{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", 0, v6notv9 }, /* wr r,r,%psr */ +{ "wr", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", 0, v6notv9 }, /* wr r,i,%psr */ +{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI_RS2(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%psr */ +{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", 0, v6notv9 }, /* wr r,r,%wim */ +{ "wr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", 0, v6notv9 }, /* wr r,i,%wim */ +{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI_RS2(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%wim */ +{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", 0, v6notv9 }, /* wr r,r,%tbr */ +{ "wr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", 0, v6notv9 }, /* wr r,i,%tbr */ +{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI_RS2(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%tbr */ + +{ "wr", F3(2, 0x30, 0)|RD(2), F3(~2, ~0x30, ~0)|RD(~2)|ASI(~0), "1,2,E", 0, v9 }, /* wr r,r,%ccr */ +{ "wr", F3(2, 0x30, 1)|RD(2), F3(~2, ~0x30, ~1)|RD(~2), "1,i,E", 0, v9 }, /* wr r,i,%ccr */ +{ "wr", F3(2, 0x30, 0)|RD(3), F3(~2, ~0x30, ~0)|RD(~3)|ASI(~0), "1,2,o", 0, v9 }, /* wr r,r,%asi */ +{ "wr", F3(2, 0x30, 1)|RD(3), F3(~2, ~0x30, ~1)|RD(~3), "1,i,o", 0, v9 }, /* wr r,i,%asi */ +{ "wr", F3(2, 0x30, 0)|RD(6), F3(~2, ~0x30, ~0)|RD(~6)|ASI(~0), "1,2,s", 0, v9 }, /* wr r,r,%fprs */ +{ "wr", F3(2, 0x30, 1)|RD(6), F3(~2, ~0x30, ~1)|RD(~6), "1,i,s", 0, v9 }, /* wr r,i,%fprs */ + +{ "wr", F3(2, 0x30, 0)|RD(16), F3(~2, ~0x30, ~0)|RD(~16)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%pcr */ +{ "wr", F3(2, 0x30, 1)|RD(16), F3(~2, ~0x30, ~1)|RD(~16), "1,i,_", 0, v9a }, /* wr r,i,%pcr */ +{ "wr", F3(2, 0x30, 0)|RD(17), F3(~2, ~0x30, ~0)|RD(~17)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%pic */ +{ "wr", F3(2, 0x30, 1)|RD(17), F3(~2, ~0x30, ~1)|RD(~17), "1,i,_", 0, v9a }, /* wr r,i,%pic */ +{ "wr", F3(2, 0x30, 0)|RD(18), F3(~2, ~0x30, ~0)|RD(~18)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%dcr */ +{ "wr", F3(2, 0x30, 1)|RD(18), F3(~2, ~0x30, ~1)|RD(~18), "1,i,_", 0, v9a }, /* wr r,i,%dcr */ +{ "wr", F3(2, 0x30, 0)|RD(19), F3(~2, ~0x30, ~0)|RD(~19)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%gsr */ +{ "wr", F3(2, 0x30, 1)|RD(19), F3(~2, ~0x30, ~1)|RD(~19), "1,i,_", 0, v9a }, /* wr r,i,%gsr */ +{ "wr", F3(2, 0x30, 0)|RD(20), F3(~2, ~0x30, ~0)|RD(~20)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%set_softint */ +{ "wr", F3(2, 0x30, 1)|RD(20), F3(~2, ~0x30, ~1)|RD(~20), "1,i,_", 0, v9a }, /* wr r,i,%set_softint */ +{ "wr", F3(2, 0x30, 0)|RD(21), F3(~2, ~0x30, ~0)|RD(~21)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%clear_softint */ +{ "wr", F3(2, 0x30, 1)|RD(21), F3(~2, ~0x30, ~1)|RD(~21), "1,i,_", 0, v9a }, /* wr r,i,%clear_softint */ +{ "wr", F3(2, 0x30, 0)|RD(22), F3(~2, ~0x30, ~0)|RD(~22)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%softint */ +{ "wr", F3(2, 0x30, 1)|RD(22), F3(~2, ~0x30, ~1)|RD(~22), "1,i,_", 0, v9a }, /* wr r,i,%softint */ +{ "wr", F3(2, 0x30, 0)|RD(23), F3(~2, ~0x30, ~0)|RD(~23)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%tick_cmpr */ +{ "wr", F3(2, 0x30, 1)|RD(23), F3(~2, ~0x30, ~1)|RD(~23), "1,i,_", 0, v9a }, /* wr r,i,%tick_cmpr */ +{ "wr", F3(2, 0x30, 0)|RD(24), F3(~2, ~0x30, ~0)|RD(~24)|ASI(~0), "1,2,_", 0, v9b }, /* wr r,r,%sys_tick */ +{ "wr", F3(2, 0x30, 1)|RD(24), F3(~2, ~0x30, ~1)|RD(~24), "1,i,_", 0, v9b }, /* wr r,i,%sys_tick */ +{ "wr", F3(2, 0x30, 0)|RD(25), F3(~2, ~0x30, ~0)|RD(~25)|ASI(~0), "1,2,_", 0, v9b }, /* wr r,r,%sys_tick_cmpr */ +{ "wr", F3(2, 0x30, 1)|RD(25), F3(~2, ~0x30, ~1)|RD(~25), "1,i,_", 0, v9b }, /* wr r,i,%sys_tick_cmpr */ + +{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", 0, v8 }, /* rd %asrX,r */ +{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", 0, v6 }, /* rd %y,r */ +{ "rd", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", 0, v6notv9 }, /* rd %psr,r */ +{ "rd", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", 0, v6notv9 }, /* rd %wim,r */ +{ "rd", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", 0, v6notv9 }, /* rd %tbr,r */ + +{ "rd", F3(2, 0x28, 0)|RS1(2), F3(~2, ~0x28, ~0)|RS1(~2)|SIMM13(~0), "E,d", 0, v9 }, /* rd %ccr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(3), F3(~2, ~0x28, ~0)|RS1(~3)|SIMM13(~0), "o,d", 0, v9 }, /* rd %asi,r */ +{ "rd", F3(2, 0x28, 0)|RS1(4), F3(~2, ~0x28, ~0)|RS1(~4)|SIMM13(~0), "W,d", 0, v9 }, /* rd %tick,r */ +{ "rd", F3(2, 0x28, 0)|RS1(5), F3(~2, ~0x28, ~0)|RS1(~5)|SIMM13(~0), "P,d", 0, v9 }, /* rd %pc,r */ +{ "rd", F3(2, 0x28, 0)|RS1(6), F3(~2, ~0x28, ~0)|RS1(~6)|SIMM13(~0), "s,d", 0, v9 }, /* rd %fprs,r */ + +{ "rd", F3(2, 0x28, 0)|RS1(16), F3(~2, ~0x28, ~0)|RS1(~16)|SIMM13(~0), "/,d", 0, v9a }, /* rd %pcr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(17), F3(~2, ~0x28, ~0)|RS1(~17)|SIMM13(~0), "/,d", 0, v9a }, /* rd %pic,r */ +{ "rd", F3(2, 0x28, 0)|RS1(18), F3(~2, ~0x28, ~0)|RS1(~18)|SIMM13(~0), "/,d", 0, v9a }, /* rd %dcr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(19), F3(~2, ~0x28, ~0)|RS1(~19)|SIMM13(~0), "/,d", 0, v9a }, /* rd %gsr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(22), F3(~2, ~0x28, ~0)|RS1(~22)|SIMM13(~0), "/,d", 0, v9a }, /* rd %softint,r */ +{ "rd", F3(2, 0x28, 0)|RS1(23), F3(~2, ~0x28, ~0)|RS1(~23)|SIMM13(~0), "/,d", 0, v9a }, /* rd %tick_cmpr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(24), F3(~2, ~0x28, ~0)|RS1(~24)|SIMM13(~0), "/,d", 0, v9b }, /* rd %sys_tick,r */ +{ "rd", F3(2, 0x28, 0)|RS1(25), F3(~2, ~0x28, ~0)|RS1(~25)|SIMM13(~0), "/,d", 0, v9b }, /* rd %sys_tick_cmpr,r */ + +{ "rdpr", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|SIMM13(~0), "?,d", 0, v9 }, /* rdpr %priv,r */ +{ "wrpr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0), "1,2,!", 0, v9 }, /* wrpr r1,r2,%priv */ +{ "wrpr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|SIMM13(~0), "1,!", 0, v9 }, /* wrpr r1,%priv */ +{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "1,i,!", 0, v9 }, /* wrpr r1,i,%priv */ +{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "i,1,!", F_ALIAS, v9 }, /* wrpr i,r1,%priv */ +{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RS1(~0), "i,!", 0, v9 }, /* wrpr i,%priv */ + +{ "rdhpr", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|SIMM13(~0), "$,d", 0, v9 }, /* rdhpr %hpriv,r */ +{ "wrhpr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0), "1,2,%", 0, v9 }, /* wrhpr r1,r2,%hpriv */ +{ "wrhpr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|SIMM13(~0), "1,%", 0, v9 }, /* wrhpr r1,%hpriv */ +{ "wrhpr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1), "1,i,%", 0, v9 }, /* wrhpr r1,i,%hpriv */ +{ "wrhpr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1), "i,1,%", F_ALIAS, v9 }, /* wrhpr i,r1,%hpriv */ +{ "wrhpr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RS1(~0), "i,%", 0, v9 }, /* wrhpr i,%hpriv */ + +/* ??? This group seems wrong. A three operand move? */ +{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", F_ALIAS, v8 }, /* wr r,r,%asrX */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", F_ALIAS, v8 }, /* wr r,i,%asrX */ +{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", F_ALIAS, v6 }, /* wr r,r,%y */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", F_ALIAS, v6 }, /* wr r,i,%y */ +{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", F_ALIAS, v6notv9 }, /* wr r,r,%psr */ +{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", F_ALIAS, v6notv9 }, /* wr r,i,%psr */ +{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", F_ALIAS, v6notv9 }, /* wr r,r,%wim */ +{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", F_ALIAS, v6notv9 }, /* wr r,i,%wim */ +{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", F_ALIAS, v6notv9 }, /* wr r,r,%tbr */ +{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", F_ALIAS, v6notv9 }, /* wr r,i,%tbr */ + +{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", F_ALIAS, v8 }, /* rd %asr1,r */ +{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", F_ALIAS, v6 }, /* rd %y,r */ +{ "mov", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", F_ALIAS, v6notv9 }, /* rd %psr,r */ +{ "mov", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", F_ALIAS, v6notv9 }, /* rd %wim,r */ +{ "mov", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", F_ALIAS, v6notv9 }, /* rd %tbr,r */ + +{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,%g0,%asrX */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "i,m", F_ALIAS, v8 }, /* wr %g0,i,%asrX */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|SIMM13(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,0,%asrX */ +{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "i,y", F_ALIAS, v6 }, /* wr %g0,i,%y */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0|SIMM13(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,0,%y */ +{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI_RS2(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%psr */ +{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "i,p", F_ALIAS, v6notv9 }, /* wr %g0,i,%psr */ +{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0|SIMM13(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,0,%psr */ +{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI_RS2(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%wim */ +{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "i,w", F_ALIAS, v6notv9 }, /* wr %g0,i,%wim */ +{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0|SIMM13(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,0,%wim */ +{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI_RS2(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%tbr */ +{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "i,t", F_ALIAS, v6notv9 }, /* wr %g0,i,%tbr */ +{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0|SIMM13(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,0,%tbr */ + +{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RS1_G0|ASI(~0), "2,d", 0, v6 }, /* or %g0,rs2,d */ +{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0, "i,d", 0, v6 }, /* or %g0,i,d */ +{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI_RS2(~0), "1,d", 0, v6 }, /* or rs1,%g0,d */ +{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|SIMM13(~0), "1,d", 0, v6 }, /* or rs1,0,d */ + +{ "or", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "1,i,d", 0, v6 }, +{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,1,d", 0, v6 }, + +{ "bset", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* or rd,rs2,rd */ +{ "bset", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,r", F_ALIAS, v6 }, /* or rd,i,rd */ + +/* This is not a commutative instruction. */ +{ "andn", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "andn", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "1,i,d", 0, v6 }, + +/* This is not a commutative instruction. */ +{ "andncc", F3(2, 0x15, 0), F3(~2, ~0x15, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "andncc", F3(2, 0x15, 1), F3(~2, ~0x15, ~1), "1,i,d", 0, v6 }, + +{ "bclr", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* andn rd,rs2,rd */ +{ "bclr", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "i,r", F_ALIAS, v6 }, /* andn rd,i,rd */ + +{ "cmp", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|RD_G0|ASI(~0), "1,2", 0, v6 }, /* subcc rs1,rs2,%g0 */ +{ "cmp", F3(2, 0x14, 1), F3(~2, ~0x14, ~1)|RD_G0, "1,i", 0, v6 }, /* subcc rs1,i,%g0 */ + +{ "sub", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "sub", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "1,i,d", 0, v6 }, + +{ "subcc", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "subcc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "1,i,d", 0, v6 }, + +{ "subc", F3(2, 0x0c, 0), F3(~2, ~0x0c, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "subc", F3(2, 0x0c, 1), F3(~2, ~0x0c, ~1), "1,i,d", 0, v6 }, + +{ "subccc", F3(2, 0x1c, 0), F3(~2, ~0x1c, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "subccc", F3(2, 0x1c, 1), F3(~2, ~0x1c, ~1), "1,i,d", 0, v6 }, + +{ "and", F3(2, 0x01, 0), F3(~2, ~0x01, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "1,i,d", 0, v6 }, +{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "i,1,d", 0, v6 }, + +{ "andcc", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "1,i,d", 0, v6 }, +{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "i,1,d", 0, v6 }, + +{ "dec", F3(2, 0x04, 1)|SIMM13(0x1), F3(~2, ~0x04, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* sub rd,1,rd */ +{ "dec", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "i,r", F_ALIAS, v8 }, /* sub rd,imm,rd */ +{ "deccc", F3(2, 0x14, 1)|SIMM13(0x1), F3(~2, ~0x14, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* subcc rd,1,rd */ +{ "deccc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "i,r", F_ALIAS, v8 }, /* subcc rd,imm,rd */ +{ "inc", F3(2, 0x00, 1)|SIMM13(0x1), F3(~2, ~0x00, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* add rd,1,rd */ +{ "inc", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,r", F_ALIAS, v8 }, /* add rd,imm,rd */ +{ "inccc", F3(2, 0x10, 1)|SIMM13(0x1), F3(~2, ~0x10, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* addcc rd,1,rd */ +{ "inccc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,r", F_ALIAS, v8 }, /* addcc rd,imm,rd */ + +{ "btst", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|RD_G0|ASI(~0), "1,2", F_ALIAS, v6 }, /* andcc rs1,rs2,%g0 */ +{ "btst", F3(2, 0x11, 1), F3(~2, ~0x11, ~1)|RD_G0, "i,1", F_ALIAS, v6 }, /* andcc rs1,i,%g0 */ + +{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "2,d", F_ALIAS, v6 }, /* sub %g0,rs2,rd */ +{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "O", F_ALIAS, v6 }, /* sub %g0,rd,rd */ + +{ "add", F3(2, 0x00, 0), F3(~2, ~0x00, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "1,i,d", 0, v6 }, +{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,1,d", 0, v6 }, +{ "addcc", F3(2, 0x10, 0), F3(~2, ~0x10, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "1,i,d", 0, v6 }, +{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,1,d", 0, v6 }, + +{ "addc", F3(2, 0x08, 0), F3(~2, ~0x08, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "addc", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "1,i,d", 0, v6 }, +{ "addc", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "i,1,d", 0, v6 }, + +{ "addccc", F3(2, 0x18, 0), F3(~2, ~0x18, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "addccc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "1,i,d", 0, v6 }, +{ "addccc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "i,1,d", 0, v6 }, + +{ "smul", F3(2, 0x0b, 0), F3(~2, ~0x0b, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "1,i,d", 0, v8 }, +{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "i,1,d", 0, v8 }, +{ "smulcc", F3(2, 0x1b, 0), F3(~2, ~0x1b, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "1,i,d", 0, v8 }, +{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "i,1,d", 0, v8 }, +{ "umul", F3(2, 0x0a, 0), F3(~2, ~0x0a, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "1,i,d", 0, v8 }, +{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "i,1,d", 0, v8 }, +{ "umulcc", F3(2, 0x1a, 0), F3(~2, ~0x1a, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "1,i,d", 0, v8 }, +{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "i,1,d", 0, v8 }, +{ "sdiv", F3(2, 0x0f, 0), F3(~2, ~0x0f, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "1,i,d", 0, v8 }, +{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "i,1,d", 0, v8 }, +{ "sdivcc", F3(2, 0x1f, 0), F3(~2, ~0x1f, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "1,i,d", 0, v8 }, +{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "i,1,d", 0, v8 }, +{ "udiv", F3(2, 0x0e, 0), F3(~2, ~0x0e, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "1,i,d", 0, v8 }, +{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "i,1,d", 0, v8 }, +{ "udivcc", F3(2, 0x1e, 0), F3(~2, ~0x1e, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "1,i,d", 0, v8 }, +{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "i,1,d", 0, v8 }, + +{ "mulx", F3(2, 0x09, 0), F3(~2, ~0x09, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "mulx", F3(2, 0x09, 1), F3(~2, ~0x09, ~1), "1,i,d", 0, v9 }, +{ "sdivx", F3(2, 0x2d, 0), F3(~2, ~0x2d, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "sdivx", F3(2, 0x2d, 1), F3(~2, ~0x2d, ~1), "1,i,d", 0, v9 }, +{ "udivx", F3(2, 0x0d, 0), F3(~2, ~0x0d, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "udivx", F3(2, 0x0d, 1), F3(~2, ~0x0d, ~1), "1,i,d", 0, v9 }, + +{ "call", F1(0x1), F1(~0x1), "L", F_JSR|F_DELAYED, v6 }, +{ "call", F1(0x1), F1(~0x1), "L,#", F_JSR|F_DELAYED, v6 }, + +{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI(~0), "1+2", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+rs2,%o7 */ +{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI(~0), "1+2,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+%g0,%o7 */ +{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "1+i", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+i,%o7 */ +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "1+i,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "i+1", F_JSR|F_DELAYED, v6 }, /* jmpl i+rs1,%o7 */ +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "i+1,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|RS1_G0, "i", F_JSR|F_DELAYED, v6 }, /* jmpl %g0+i,%o7 */ +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|RS1_G0, "i,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|SIMM13(~0), "1", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+0,%o7 */ +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|SIMM13(~0), "1,#", F_JSR|F_DELAYED, v6 }, + + +/* Conditional instructions. + + Because this part of the table was such a mess earlier, I have + macrofied it so that all the branches and traps are generated from + a single-line description of each condition value. John Gilmore. */ + +/* Define branches -- one annulled, one without, etc. */ +#define br(opcode, mask, lose, flags) \ + { opcode, (mask)|ANNUL, (lose), ",a l", (flags), v6 }, \ + { opcode, (mask) , (lose)|ANNUL, "l", (flags), v6 } + +#define brx(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask)|(2<<20)|BPRED, ANNUL|(lose), "Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20)|BPRED, ANNUL|(lose), ",T Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20)|BPRED|ANNUL, (lose), ",a Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20)|BPRED|ANNUL, (lose), ",a,T Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20), ANNUL|BPRED|(lose), ",N Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20)|ANNUL, BPRED|(lose), ",a,N Z,G", (flags), v9 }, \ + { opcode, (mask)|BPRED, ANNUL|(lose)|(2<<20), "z,G", (flags), v9 }, \ + { opcode, (mask)|BPRED, ANNUL|(lose)|(2<<20), ",T z,G", (flags), v9 }, \ + { opcode, (mask)|BPRED|ANNUL, (lose)|(2<<20), ",a z,G", (flags), v9 }, \ + { opcode, (mask)|BPRED|ANNUL, (lose)|(2<<20), ",a,T z,G", (flags), v9 }, \ + { opcode, (mask), ANNUL|BPRED|(lose)|(2<<20), ",N z,G", (flags), v9 }, \ + { opcode, (mask)|ANNUL, BPRED|(lose)|(2<<20), ",a,N z,G", (flags), v9 } + +/* Define four traps: reg+reg, reg + immediate, immediate alone, reg alone. */ +#define tr(opcode, mask, lose, flags) \ + { opcode, (mask)|(2<<11)|IMMED, (lose)|RS1_G0, "Z,i", (flags), v9 }, /* %g0 + imm */ \ + { opcode, (mask)|(2<<11)|IMMED, (lose), "Z,1+i", (flags), v9 }, /* rs1 + imm */ \ + { opcode, (mask)|(2<<11), IMMED|(lose), "Z,1+2", (flags), v9 }, /* rs1 + rs2 */ \ + { opcode, (mask)|(2<<11), IMMED|(lose)|RS2_G0, "Z,1", (flags), v9 }, /* rs1 + %g0 */ \ + { opcode, (mask)|IMMED, (lose)|RS1_G0, "z,i", (flags)|F_ALIAS, v9 }, /* %g0 + imm */ \ + { opcode, (mask)|IMMED, (lose), "z,1+i", (flags)|F_ALIAS, v9 }, /* rs1 + imm */ \ + { opcode, (mask), IMMED|(lose), "z,1+2", (flags)|F_ALIAS, v9 }, /* rs1 + rs2 */ \ + { opcode, (mask), IMMED|(lose)|RS2_G0, "z,1", (flags)|F_ALIAS, v9 }, /* rs1 + %g0 */ \ + { opcode, (mask)|IMMED, (lose)|RS1_G0, "i", (flags), v6 }, /* %g0 + imm */ \ + { opcode, (mask)|IMMED, (lose), "1+i", (flags), v6 }, /* rs1 + imm */ \ + { opcode, (mask), IMMED|(lose), "1+2", (flags), v6 }, /* rs1 + rs2 */ \ + { opcode, (mask), IMMED|(lose)|RS2_G0, "1", (flags), v6 } /* rs1 + %g0 */ + +/* v9: We must put `brx' before `br', to ensure that we never match something + v9: against an expression unless it is an expression. Otherwise, we end + v9: up with undefined symbol tables entries, because they get added, but + v9: are not deleted if the pattern fails to match. */ + +/* Define both branches and traps based on condition mask */ +#define cond(bop, top, mask, flags) \ + brx(bop, F2(0, 1)|(mask), F2(~0, ~1)|((~mask)&COND(~0)), F_DELAYED|(flags)), /* v9 */ \ + br(bop, F2(0, 2)|(mask), F2(~0, ~2)|((~mask)&COND(~0)), F_DELAYED|(flags)), \ + tr(top, F3(2, 0x3a, 0)|(mask), F3(~2, ~0x3a, 0)|((~mask)&COND(~0)), ((flags) & ~(F_UNBR|F_CONDBR))) + +/* Define all the conditions, all the branches, all the traps. */ + +/* Standard branch, trap mnemonics */ +cond ("b", "ta", CONDA, F_UNBR), +/* Alternative form (just for assembly, not for disassembly) */ +cond ("ba", "t", CONDA, F_UNBR|F_ALIAS), + +cond ("bcc", "tcc", CONDCC, F_CONDBR), +cond ("bcs", "tcs", CONDCS, F_CONDBR), +cond ("be", "te", CONDE, F_CONDBR), +cond ("beq", "teq", CONDE, F_CONDBR|F_ALIAS), +cond ("bg", "tg", CONDG, F_CONDBR), +cond ("bgt", "tgt", CONDG, F_CONDBR|F_ALIAS), +cond ("bge", "tge", CONDGE, F_CONDBR), +cond ("bgeu", "tgeu", CONDGEU, F_CONDBR|F_ALIAS), /* for cc */ +cond ("bgu", "tgu", CONDGU, F_CONDBR), +cond ("bl", "tl", CONDL, F_CONDBR), +cond ("blt", "tlt", CONDL, F_CONDBR|F_ALIAS), +cond ("ble", "tle", CONDLE, F_CONDBR), +cond ("bleu", "tleu", CONDLEU, F_CONDBR), +cond ("blu", "tlu", CONDLU, F_CONDBR|F_ALIAS), /* for cs */ +cond ("bn", "tn", CONDN, F_CONDBR), +cond ("bne", "tne", CONDNE, F_CONDBR), +cond ("bneg", "tneg", CONDNEG, F_CONDBR), +cond ("bnz", "tnz", CONDNZ, F_CONDBR|F_ALIAS), /* for ne */ +cond ("bpos", "tpos", CONDPOS, F_CONDBR), +cond ("bvc", "tvc", CONDVC, F_CONDBR), +cond ("bvs", "tvs", CONDVS, F_CONDBR), +cond ("bz", "tz", CONDZ, F_CONDBR|F_ALIAS), /* for e */ + +#undef cond +#undef br +#undef brr /* v9 */ +#undef tr + +#define brr(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask)|BPRED, ANNUL|(lose), "1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask)|BPRED, ANNUL|(lose), ",T 1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask)|BPRED|ANNUL, (lose), ",a 1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask)|BPRED|ANNUL, (lose), ",a,T 1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask), ANNUL|BPRED|(lose), ",N 1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask)|ANNUL, BPRED|(lose), ",a,N 1,k", F_DELAYED|(flags), v9 } + +#define condr(bop, mask, flags) /* v9 */ \ + brr(bop, F2(0, 3)|COND(mask), F2(~0, ~3)|COND(~(mask)), (flags)) /* v9 */ + +/* v9 */ condr("brnz", 0x5, F_CONDBR), +/* v9 */ condr("brz", 0x1, F_CONDBR), +/* v9 */ condr("brgez", 0x7, F_CONDBR), +/* v9 */ condr("brlz", 0x3, F_CONDBR), +/* v9 */ condr("brlez", 0x2, F_CONDBR), +/* v9 */ condr("brgz", 0x6, F_CONDBR), + +#undef condr /* v9 */ +#undef brr /* v9 */ + +#define movr(opcode, mask, flags) /* v9 */ \ + { opcode, F3(2, 0x2f, 0)|RCOND(mask), F3(~2, ~0x2f, ~0)|RCOND(~(mask)), "1,2,d", (flags), v9 }, \ + { opcode, F3(2, 0x2f, 1)|RCOND(mask), F3(~2, ~0x2f, ~1)|RCOND(~(mask)), "1,j,d", (flags), v9 } + +#define fmrrs(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask), (lose), "1,f,g", (flags) | F_FLOAT, v9 } +#define fmrrd(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask), (lose), "1,B,H", (flags) | F_FLOAT, v9 } +#define fmrrq(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask), (lose), "1,R,J", (flags) | F_FLOAT, v9 } + +#define fmovrs(mop, mask, flags) /* v9 */ \ + fmrrs(mop, F3(2, 0x35, 0)|OPF_LOW5(5)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~5)|RCOND(~(mask)), (flags)) /* v9 */ +#define fmovrd(mop, mask, flags) /* v9 */ \ + fmrrd(mop, F3(2, 0x35, 0)|OPF_LOW5(6)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~6)|RCOND(~(mask)), (flags)) /* v9 */ +#define fmovrq(mop, mask, flags) /* v9 */ \ + fmrrq(mop, F3(2, 0x35, 0)|OPF_LOW5(7)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~7)|RCOND(~(mask)), (flags)) /* v9 */ + +/* v9 */ movr("movrne", 0x5, 0), +/* v9 */ movr("movre", 0x1, 0), +/* v9 */ movr("movrgez", 0x7, 0), +/* v9 */ movr("movrlz", 0x3, 0), +/* v9 */ movr("movrlez", 0x2, 0), +/* v9 */ movr("movrgz", 0x6, 0), +/* v9 */ movr("movrnz", 0x5, F_ALIAS), +/* v9 */ movr("movrz", 0x1, F_ALIAS), + +/* v9 */ fmovrs("fmovrsne", 0x5, 0), +/* v9 */ fmovrs("fmovrse", 0x1, 0), +/* v9 */ fmovrs("fmovrsgez", 0x7, 0), +/* v9 */ fmovrs("fmovrslz", 0x3, 0), +/* v9 */ fmovrs("fmovrslez", 0x2, 0), +/* v9 */ fmovrs("fmovrsgz", 0x6, 0), +/* v9 */ fmovrs("fmovrsnz", 0x5, F_ALIAS), +/* v9 */ fmovrs("fmovrsz", 0x1, F_ALIAS), + +/* v9 */ fmovrd("fmovrdne", 0x5, 0), +/* v9 */ fmovrd("fmovrde", 0x1, 0), +/* v9 */ fmovrd("fmovrdgez", 0x7, 0), +/* v9 */ fmovrd("fmovrdlz", 0x3, 0), +/* v9 */ fmovrd("fmovrdlez", 0x2, 0), +/* v9 */ fmovrd("fmovrdgz", 0x6, 0), +/* v9 */ fmovrd("fmovrdnz", 0x5, F_ALIAS), +/* v9 */ fmovrd("fmovrdz", 0x1, F_ALIAS), + +/* v9 */ fmovrq("fmovrqne", 0x5, 0), +/* v9 */ fmovrq("fmovrqe", 0x1, 0), +/* v9 */ fmovrq("fmovrqgez", 0x7, 0), +/* v9 */ fmovrq("fmovrqlz", 0x3, 0), +/* v9 */ fmovrq("fmovrqlez", 0x2, 0), +/* v9 */ fmovrq("fmovrqgz", 0x6, 0), +/* v9 */ fmovrq("fmovrqnz", 0x5, F_ALIAS), +/* v9 */ fmovrq("fmovrqz", 0x1, F_ALIAS), + +#undef movr /* v9 */ +#undef fmovr /* v9 */ +#undef fmrr /* v9 */ + +#define movicc(opcode, cond, flags) /* v9 */ \ + { opcode, F3(2, 0x2c, 0)|MCOND(cond,1)|ICC, F3(~2, ~0x2c, ~0)|MCOND(~cond,~1)|XCC|(1<<11), "z,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|MCOND(cond,1)|ICC, F3(~2, ~0x2c, ~1)|MCOND(~cond,~1)|XCC|(1<<11), "z,I,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 0)|MCOND(cond,1)|XCC, F3(~2, ~0x2c, ~0)|MCOND(~cond,~1)|(1<<11), "Z,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|MCOND(cond,1)|XCC, F3(~2, ~0x2c, ~1)|MCOND(~cond,~1)|(1<<11), "Z,I,d", flags, v9 } + +#define movfcc(opcode, fcond, flags) /* v9 */ \ + { opcode, F3(2, 0x2c, 0)|FCC(0)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~0)|F3(~2, ~0x2c, ~0), "6,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|FCC(0)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~0)|F3(~2, ~0x2c, ~1), "6,I,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 0)|FCC(1)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~1)|F3(~2, ~0x2c, ~0), "7,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|FCC(1)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~1)|F3(~2, ~0x2c, ~1), "7,I,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 0)|FCC(2)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~2)|F3(~2, ~0x2c, ~0), "8,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|FCC(2)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~2)|F3(~2, ~0x2c, ~1), "8,I,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 0)|FCC(3)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~3)|F3(~2, ~0x2c, ~0), "9,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|FCC(3)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~3)|F3(~2, ~0x2c, ~1), "9,I,d", flags, v9 } + +#define movcc(opcode, cond, fcond, flags) /* v9 */ \ + movfcc (opcode, fcond, flags), /* v9 */ \ + movicc (opcode, cond, flags) /* v9 */ + +/* v9 */ movcc ("mova", CONDA, FCONDA, 0), +/* v9 */ movicc ("movcc", CONDCC, 0), +/* v9 */ movicc ("movgeu", CONDGEU, F_ALIAS), +/* v9 */ movicc ("movcs", CONDCS, 0), +/* v9 */ movicc ("movlu", CONDLU, F_ALIAS), +/* v9 */ movcc ("move", CONDE, FCONDE, 0), +/* v9 */ movcc ("movg", CONDG, FCONDG, 0), +/* v9 */ movcc ("movge", CONDGE, FCONDGE, 0), +/* v9 */ movicc ("movgu", CONDGU, 0), +/* v9 */ movcc ("movl", CONDL, FCONDL, 0), +/* v9 */ movcc ("movle", CONDLE, FCONDLE, 0), +/* v9 */ movicc ("movleu", CONDLEU, 0), +/* v9 */ movfcc ("movlg", FCONDLG, 0), +/* v9 */ movcc ("movn", CONDN, FCONDN, 0), +/* v9 */ movcc ("movne", CONDNE, FCONDNE, 0), +/* v9 */ movicc ("movneg", CONDNEG, 0), +/* v9 */ movcc ("movnz", CONDNZ, FCONDNZ, F_ALIAS), +/* v9 */ movfcc ("movo", FCONDO, 0), +/* v9 */ movicc ("movpos", CONDPOS, 0), +/* v9 */ movfcc ("movu", FCONDU, 0), +/* v9 */ movfcc ("movue", FCONDUE, 0), +/* v9 */ movfcc ("movug", FCONDUG, 0), +/* v9 */ movfcc ("movuge", FCONDUGE, 0), +/* v9 */ movfcc ("movul", FCONDUL, 0), +/* v9 */ movfcc ("movule", FCONDULE, 0), +/* v9 */ movicc ("movvc", CONDVC, 0), +/* v9 */ movicc ("movvs", CONDVS, 0), +/* v9 */ movcc ("movz", CONDZ, FCONDZ, F_ALIAS), + +#undef movicc /* v9 */ +#undef movfcc /* v9 */ +#undef movcc /* v9 */ + +#define FM_SF 1 /* v9 - values for fpsize */ +#define FM_DF 2 /* v9 */ +#define FM_QF 3 /* v9 */ + +#define fmoviccx(opcode, fpsize, args, cond, flags) /* v9 */ \ +{ opcode, F3F(2, 0x35, 0x100+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x100+fpsize))|MCOND(~cond,~0), "z," args, flags, v9 }, \ +{ opcode, F3F(2, 0x35, 0x180+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x180+fpsize))|MCOND(~cond,~0), "Z," args, flags, v9 } + +#define fmovfccx(opcode, fpsize, args, fcond, flags) /* v9 */ \ +{ opcode, F3F(2, 0x35, 0x000+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x000+fpsize))|MCOND(~fcond,~0), "6," args, flags, v9 }, \ +{ opcode, F3F(2, 0x35, 0x040+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x040+fpsize))|MCOND(~fcond,~0), "7," args, flags, v9 }, \ +{ opcode, F3F(2, 0x35, 0x080+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x080+fpsize))|MCOND(~fcond,~0), "8," args, flags, v9 }, \ +{ opcode, F3F(2, 0x35, 0x0c0+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x0c0+fpsize))|MCOND(~fcond,~0), "9," args, flags, v9 } + +/* FIXME: use fmovicc/fmovfcc? */ /* v9 */ +#define fmovccx(opcode, fpsize, args, cond, fcond, flags) /* v9 */ \ +{ opcode, F3F(2, 0x35, 0x100+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x100+fpsize))|MCOND(~cond,~0), "z," args, flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x000+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x000+fpsize))|MCOND(~fcond,~0), "6," args, flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x180+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x180+fpsize))|MCOND(~cond,~0), "Z," args, flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x040+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x040+fpsize))|MCOND(~fcond,~0), "7," args, flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x080+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x080+fpsize))|MCOND(~fcond,~0), "8," args, flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x0c0+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x0c0+fpsize))|MCOND(~fcond,~0), "9," args, flags | F_FLOAT, v9 } + +#define fmovicc(suffix, cond, flags) /* v9 */ \ +fmoviccx("fmovd" suffix, FM_DF, "B,H", cond, flags), \ +fmoviccx("fmovq" suffix, FM_QF, "R,J", cond, flags), \ +fmoviccx("fmovs" suffix, FM_SF, "f,g", cond, flags) + +#define fmovfcc(suffix, fcond, flags) /* v9 */ \ +fmovfccx("fmovd" suffix, FM_DF, "B,H", fcond, flags), \ +fmovfccx("fmovq" suffix, FM_QF, "R,J", fcond, flags), \ +fmovfccx("fmovs" suffix, FM_SF, "f,g", fcond, flags) + +#define fmovcc(suffix, cond, fcond, flags) /* v9 */ \ +fmovccx("fmovd" suffix, FM_DF, "B,H", cond, fcond, flags), \ +fmovccx("fmovq" suffix, FM_QF, "R,J", cond, fcond, flags), \ +fmovccx("fmovs" suffix, FM_SF, "f,g", cond, fcond, flags) + +/* v9 */ fmovcc ("a", CONDA, FCONDA, 0), +/* v9 */ fmovicc ("cc", CONDCC, 0), +/* v9 */ fmovicc ("cs", CONDCS, 0), +/* v9 */ fmovcc ("e", CONDE, FCONDE, 0), +/* v9 */ fmovcc ("g", CONDG, FCONDG, 0), +/* v9 */ fmovcc ("ge", CONDGE, FCONDGE, 0), +/* v9 */ fmovicc ("geu", CONDGEU, F_ALIAS), +/* v9 */ fmovicc ("gu", CONDGU, 0), +/* v9 */ fmovcc ("l", CONDL, FCONDL, 0), +/* v9 */ fmovcc ("le", CONDLE, FCONDLE, 0), +/* v9 */ fmovicc ("leu", CONDLEU, 0), +/* v9 */ fmovfcc ("lg", FCONDLG, 0), +/* v9 */ fmovicc ("lu", CONDLU, F_ALIAS), +/* v9 */ fmovcc ("n", CONDN, FCONDN, 0), +/* v9 */ fmovcc ("ne", CONDNE, FCONDNE, 0), +/* v9 */ fmovicc ("neg", CONDNEG, 0), +/* v9 */ fmovcc ("nz", CONDNZ, FCONDNZ, F_ALIAS), +/* v9 */ fmovfcc ("o", FCONDO, 0), +/* v9 */ fmovicc ("pos", CONDPOS, 0), +/* v9 */ fmovfcc ("u", FCONDU, 0), +/* v9 */ fmovfcc ("ue", FCONDUE, 0), +/* v9 */ fmovfcc ("ug", FCONDUG, 0), +/* v9 */ fmovfcc ("uge", FCONDUGE, 0), +/* v9 */ fmovfcc ("ul", FCONDUL, 0), +/* v9 */ fmovfcc ("ule", FCONDULE, 0), +/* v9 */ fmovicc ("vc", CONDVC, 0), +/* v9 */ fmovicc ("vs", CONDVS, 0), +/* v9 */ fmovcc ("z", CONDZ, FCONDZ, F_ALIAS), + +#undef fmoviccx /* v9 */ +#undef fmovfccx /* v9 */ +#undef fmovccx /* v9 */ +#undef fmovicc /* v9 */ +#undef fmovfcc /* v9 */ +#undef fmovcc /* v9 */ +#undef FM_DF /* v9 */ +#undef FM_QF /* v9 */ +#undef FM_SF /* v9 */ + +/* Coprocessor branches. */ +#define CBR(opcode, mask, lose, flags, arch) \ + { opcode, (mask), ANNUL | (lose), "l", flags | F_DELAYED, arch }, \ + { opcode, (mask) | ANNUL, (lose), ",a l", flags | F_DELAYED, arch } + +/* Floating point branches. */ +#define FBR(opcode, mask, lose, flags) \ + { opcode, (mask), ANNUL | (lose), "l", flags | F_DELAYED | F_FBR, v6 }, \ + { opcode, (mask) | ANNUL, (lose), ",a l", flags | F_DELAYED | F_FBR, v6 } + +/* V9 extended floating point branches. */ +#define FBRX(opcode, mask, lose, flags) /* v9 */ \ + { opcode, FBFCC(0)|(mask)|BPRED, ANNUL|FBFCC(~0)|(lose), "6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask)|BPRED, ANNUL|FBFCC(~0)|(lose), ",T 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask)|BPRED|ANNUL, FBFCC(~0)|(lose), ",a 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask)|BPRED|ANNUL, FBFCC(~0)|(lose), ",a,T 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask), ANNUL|BPRED|FBFCC(~0)|(lose), ",N 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask)|ANNUL, BPRED|FBFCC(~0)|(lose), ",a,N 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|BPRED, ANNUL|FBFCC(~1)|(lose), "7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|BPRED, ANNUL|FBFCC(~1)|(lose), ",T 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|BPRED|ANNUL, FBFCC(~1)|(lose), ",a 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|BPRED|ANNUL, FBFCC(~1)|(lose), ",a,T 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask), ANNUL|BPRED|FBFCC(~1)|(lose), ",N 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|ANNUL, BPRED|FBFCC(~1)|(lose), ",a,N 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|BPRED, ANNUL|FBFCC(~2)|(lose), "8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|BPRED, ANNUL|FBFCC(~2)|(lose), ",T 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|BPRED|ANNUL, FBFCC(~2)|(lose), ",a 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|BPRED|ANNUL, FBFCC(~2)|(lose), ",a,T 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask), ANNUL|BPRED|FBFCC(~2)|(lose), ",N 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|ANNUL, BPRED|FBFCC(~2)|(lose), ",a,N 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|BPRED, ANNUL|FBFCC(~3)|(lose), "9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|BPRED, ANNUL|FBFCC(~3)|(lose), ",T 9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|BPRED|ANNUL, FBFCC(~3)|(lose), ",a 9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|BPRED|ANNUL, FBFCC(~3)|(lose), ",a,T 9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask), ANNUL|BPRED|FBFCC(~3)|(lose), ",N 9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|ANNUL, BPRED|FBFCC(~3)|(lose), ",a,N 9,G", flags|F_DELAYED|F_FBR, v9 } + +/* v9: We must put `FBRX' before `FBR', to ensure that we never match + v9: something against an expression unless it is an expression. Otherwise, + v9: we end up with undefined symbol tables entries, because they get added, + v9: but are not deleted if the pattern fails to match. */ + +#define CONDFC(fop, cop, mask, flags) \ + FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \ + FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \ + CBR(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags, v6notlet) + +#define CONDFCL(fop, cop, mask, flags) \ + FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \ + FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \ + CBR(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags, v6) + +#define CONDF(fop, mask, flags) \ + FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \ + FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags) + +CONDFC ("fb", "cb", 0x8, F_UNBR), +CONDFCL ("fba", "cba", 0x8, F_UNBR|F_ALIAS), +CONDFC ("fbe", "cb0", 0x9, F_CONDBR), +CONDF ("fbz", 0x9, F_CONDBR|F_ALIAS), +CONDFC ("fbg", "cb2", 0x6, F_CONDBR), +CONDFC ("fbge", "cb02", 0xb, F_CONDBR), +CONDFC ("fbl", "cb1", 0x4, F_CONDBR), +CONDFC ("fble", "cb01", 0xd, F_CONDBR), +CONDFC ("fblg", "cb12", 0x2, F_CONDBR), +CONDFCL ("fbn", "cbn", 0x0, F_UNBR), +CONDFC ("fbne", "cb123", 0x1, F_CONDBR), +CONDF ("fbnz", 0x1, F_CONDBR|F_ALIAS), +CONDFC ("fbo", "cb012", 0xf, F_CONDBR), +CONDFC ("fbu", "cb3", 0x7, F_CONDBR), +CONDFC ("fbue", "cb03", 0xa, F_CONDBR), +CONDFC ("fbug", "cb23", 0x5, F_CONDBR), +CONDFC ("fbuge", "cb023", 0xc, F_CONDBR), +CONDFC ("fbul", "cb13", 0x3, F_CONDBR), +CONDFC ("fbule", "cb013", 0xe, F_CONDBR), + +#undef CONDFC +#undef CONDFCL +#undef CONDF +#undef CBR +#undef FBR +#undef FBRX /* v9 */ + +{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI(~0), "1+2", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+rs2,%g0 */ +{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI_RS2(~0), "1", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+%g0,%g0 */ +{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "1+i", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+i,%g0 */ +{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "i+1", F_UNBR|F_DELAYED, v6 }, /* jmpl i+rs1,%g0 */ +{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* jmpl %g0+i,%g0 */ +{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|SIMM13(~0), "1", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+0,%g0 */ + +{ "nop", F2(0, 4), 0xfeffffff, "", 0, v6 }, /* sethi 0, %g0 */ + +{ "set", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v6 }, +{ "setuw", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v9 }, +{ "setsw", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v9 }, +{ "setx", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,1,d", F_ALIAS, v9 }, + +{ "sethi", F2(0x0, 0x4), F2(~0x0, ~0x4), "h,d", 0, v6 }, + +{ "taddcc", F3(2, 0x20, 0), F3(~2, ~0x20, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "1,i,d", 0, v6 }, +{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "i,1,d", 0, v6 }, +{ "taddcctv", F3(2, 0x22, 0), F3(~2, ~0x22, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "1,i,d", 0, v6 }, +{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "i,1,d", 0, v6 }, + +{ "tsubcc", F3(2, 0x21, 0), F3(~2, ~0x21, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "tsubcc", F3(2, 0x21, 1), F3(~2, ~0x21, ~1), "1,i,d", 0, v6 }, +{ "tsubcctv", F3(2, 0x23, 0), F3(~2, ~0x23, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "tsubcctv", F3(2, 0x23, 1), F3(~2, ~0x23, ~1), "1,i,d", 0, v6 }, + +{ "unimp", F2(0x0, 0x0), 0xffc00000, "n", 0, v6notv9 }, +{ "illtrap", F2(0, 0), F2(~0, ~0)|RD_G0, "n", 0, v9 }, + +/* This *is* a commutative instruction. */ +{ "xnor", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "1,i,d", 0, v6 }, +{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "i,1,d", 0, v6 }, +/* This *is* a commutative instruction. */ +{ "xnorcc", F3(2, 0x17, 0), F3(~2, ~0x17, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "1,i,d", 0, v6 }, +{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "i,1,d", 0, v6 }, +{ "xor", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "1,i,d", 0, v6 }, +{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,1,d", 0, v6 }, +{ "xorcc", F3(2, 0x13, 0), F3(~2, ~0x13, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "1,i,d", 0, v6 }, +{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "i,1,d", 0, v6 }, + +{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,d", F_ALIAS, v6 }, /* xnor rs1,%0,rd */ +{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "r", F_ALIAS, v6 }, /* xnor rd,%0,rd */ + +{ "btog", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* xor rd,rs2,rd */ +{ "btog", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,r", F_ALIAS, v6 }, /* xor rd,i,rd */ + +/* FPop1 and FPop2 are not instructions. Don't accept them. */ + +{ "fdtoi", F3F(2, 0x34, 0x0d2), F3F(~2, ~0x34, ~0x0d2)|RS1_G0, "B,g", F_FLOAT, v6 }, +{ "fstoi", F3F(2, 0x34, 0x0d1), F3F(~2, ~0x34, ~0x0d1)|RS1_G0, "f,g", F_FLOAT, v6 }, +{ "fqtoi", F3F(2, 0x34, 0x0d3), F3F(~2, ~0x34, ~0x0d3)|RS1_G0, "R,g", F_FLOAT, v8 }, + +{ "fdtox", F3F(2, 0x34, 0x082), F3F(~2, ~0x34, ~0x082)|RS1_G0, "B,H", F_FLOAT, v9 }, +{ "fstox", F3F(2, 0x34, 0x081), F3F(~2, ~0x34, ~0x081)|RS1_G0, "f,H", F_FLOAT, v9 }, +{ "fqtox", F3F(2, 0x34, 0x083), F3F(~2, ~0x34, ~0x083)|RS1_G0, "R,H", F_FLOAT, v9 }, + +{ "fitod", F3F(2, 0x34, 0x0c8), F3F(~2, ~0x34, ~0x0c8)|RS1_G0, "f,H", F_FLOAT, v6 }, +{ "fitos", F3F(2, 0x34, 0x0c4), F3F(~2, ~0x34, ~0x0c4)|RS1_G0, "f,g", F_FLOAT, v6 }, +{ "fitoq", F3F(2, 0x34, 0x0cc), F3F(~2, ~0x34, ~0x0cc)|RS1_G0, "f,J", F_FLOAT, v8 }, + +{ "fxtod", F3F(2, 0x34, 0x088), F3F(~2, ~0x34, ~0x088)|RS1_G0, "B,H", F_FLOAT, v9 }, +{ "fxtos", F3F(2, 0x34, 0x084), F3F(~2, ~0x34, ~0x084)|RS1_G0, "B,g", F_FLOAT, v9 }, +{ "fxtoq", F3F(2, 0x34, 0x08c), F3F(~2, ~0x34, ~0x08c)|RS1_G0, "B,J", F_FLOAT, v9 }, + +{ "fdtoq", F3F(2, 0x34, 0x0ce), F3F(~2, ~0x34, ~0x0ce)|RS1_G0, "B,J", F_FLOAT, v8 }, +{ "fdtos", F3F(2, 0x34, 0x0c6), F3F(~2, ~0x34, ~0x0c6)|RS1_G0, "B,g", F_FLOAT, v6 }, +{ "fqtod", F3F(2, 0x34, 0x0cb), F3F(~2, ~0x34, ~0x0cb)|RS1_G0, "R,H", F_FLOAT, v8 }, +{ "fqtos", F3F(2, 0x34, 0x0c7), F3F(~2, ~0x34, ~0x0c7)|RS1_G0, "R,g", F_FLOAT, v8 }, +{ "fstod", F3F(2, 0x34, 0x0c9), F3F(~2, ~0x34, ~0x0c9)|RS1_G0, "f,H", F_FLOAT, v6 }, +{ "fstoq", F3F(2, 0x34, 0x0cd), F3F(~2, ~0x34, ~0x0cd)|RS1_G0, "f,J", F_FLOAT, v8 }, + +{ "fdivd", F3F(2, 0x34, 0x04e), F3F(~2, ~0x34, ~0x04e), "v,B,H", F_FLOAT, v6 }, +{ "fdivq", F3F(2, 0x34, 0x04f), F3F(~2, ~0x34, ~0x04f), "V,R,J", F_FLOAT, v8 }, +{ "fdivx", F3F(2, 0x34, 0x04f), F3F(~2, ~0x34, ~0x04f), "V,R,J", F_FLOAT|F_ALIAS, v8 }, +{ "fdivs", F3F(2, 0x34, 0x04d), F3F(~2, ~0x34, ~0x04d), "e,f,g", F_FLOAT, v6 }, +{ "fmuld", F3F(2, 0x34, 0x04a), F3F(~2, ~0x34, ~0x04a), "v,B,H", F_FLOAT, v6 }, +{ "fmulq", F3F(2, 0x34, 0x04b), F3F(~2, ~0x34, ~0x04b), "V,R,J", F_FLOAT, v8 }, +{ "fmulx", F3F(2, 0x34, 0x04b), F3F(~2, ~0x34, ~0x04b), "V,R,J", F_FLOAT|F_ALIAS, v8 }, +{ "fmuls", F3F(2, 0x34, 0x049), F3F(~2, ~0x34, ~0x049), "e,f,g", F_FLOAT, v6 }, + +{ "fdmulq", F3F(2, 0x34, 0x06e), F3F(~2, ~0x34, ~0x06e), "v,B,J", F_FLOAT, v8 }, +{ "fdmulx", F3F(2, 0x34, 0x06e), F3F(~2, ~0x34, ~0x06e), "v,B,J", F_FLOAT|F_ALIAS, v8 }, +{ "fsmuld", F3F(2, 0x34, 0x069), F3F(~2, ~0x34, ~0x069), "e,f,H", F_FLOAT, v8 }, + +{ "fsqrtd", F3F(2, 0x34, 0x02a), F3F(~2, ~0x34, ~0x02a)|RS1_G0, "B,H", F_FLOAT, v7 }, +{ "fsqrtq", F3F(2, 0x34, 0x02b), F3F(~2, ~0x34, ~0x02b)|RS1_G0, "R,J", F_FLOAT, v8 }, +{ "fsqrtx", F3F(2, 0x34, 0x02b), F3F(~2, ~0x34, ~0x02b)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v8 }, +{ "fsqrts", F3F(2, 0x34, 0x029), F3F(~2, ~0x34, ~0x029)|RS1_G0, "f,g", F_FLOAT, v7 }, + +{ "fabsd", F3F(2, 0x34, 0x00a), F3F(~2, ~0x34, ~0x00a)|RS1_G0, "B,H", F_FLOAT, v9 }, +{ "fabsq", F3F(2, 0x34, 0x00b), F3F(~2, ~0x34, ~0x00b)|RS1_G0, "R,J", F_FLOAT, v9 }, +{ "fabsx", F3F(2, 0x34, 0x00b), F3F(~2, ~0x34, ~0x00b)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 }, +{ "fabss", F3F(2, 0x34, 0x009), F3F(~2, ~0x34, ~0x009)|RS1_G0, "f,g", F_FLOAT, v6 }, +{ "fmovd", F3F(2, 0x34, 0x002), F3F(~2, ~0x34, ~0x002)|RS1_G0, "B,H", F_FLOAT, v9 }, +{ "fmovq", F3F(2, 0x34, 0x003), F3F(~2, ~0x34, ~0x003)|RS1_G0, "R,J", F_FLOAT, v9 }, +{ "fmovx", F3F(2, 0x34, 0x003), F3F(~2, ~0x34, ~0x003)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 }, +{ "fmovs", F3F(2, 0x34, 0x001), F3F(~2, ~0x34, ~0x001)|RS1_G0, "f,g", F_FLOAT, v6 }, +{ "fnegd", F3F(2, 0x34, 0x006), F3F(~2, ~0x34, ~0x006)|RS1_G0, "B,H", F_FLOAT, v9 }, +{ "fnegq", F3F(2, 0x34, 0x007), F3F(~2, ~0x34, ~0x007)|RS1_G0, "R,J", F_FLOAT, v9 }, +{ "fnegx", F3F(2, 0x34, 0x007), F3F(~2, ~0x34, ~0x007)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 }, +{ "fnegs", F3F(2, 0x34, 0x005), F3F(~2, ~0x34, ~0x005)|RS1_G0, "f,g", F_FLOAT, v6 }, + +{ "faddd", F3F(2, 0x34, 0x042), F3F(~2, ~0x34, ~0x042), "v,B,H", F_FLOAT, v6 }, +{ "faddq", F3F(2, 0x34, 0x043), F3F(~2, ~0x34, ~0x043), "V,R,J", F_FLOAT, v8 }, +{ "faddx", F3F(2, 0x34, 0x043), F3F(~2, ~0x34, ~0x043), "V,R,J", F_FLOAT|F_ALIAS, v8 }, +{ "fadds", F3F(2, 0x34, 0x041), F3F(~2, ~0x34, ~0x041), "e,f,g", F_FLOAT, v6 }, +{ "fsubd", F3F(2, 0x34, 0x046), F3F(~2, ~0x34, ~0x046), "v,B,H", F_FLOAT, v6 }, +{ "fsubq", F3F(2, 0x34, 0x047), F3F(~2, ~0x34, ~0x047), "V,R,J", F_FLOAT, v8 }, +{ "fsubx", F3F(2, 0x34, 0x047), F3F(~2, ~0x34, ~0x047), "V,R,J", F_FLOAT|F_ALIAS, v8 }, +{ "fsubs", F3F(2, 0x34, 0x045), F3F(~2, ~0x34, ~0x045), "e,f,g", F_FLOAT, v6 }, + +#define CMPFCC(x) (((x)&0x3)<<25) + +{ "fcmpd", F3F(2, 0x35, 0x052), F3F(~2, ~0x35, ~0x052)|RD_G0, "v,B", F_FLOAT, v6 }, +{ "fcmpd", CMPFCC(0)|F3F(2, 0x35, 0x052), CMPFCC(~0)|F3F(~2, ~0x35, ~0x052), "6,v,B", F_FLOAT, v9 }, +{ "fcmpd", CMPFCC(1)|F3F(2, 0x35, 0x052), CMPFCC(~1)|F3F(~2, ~0x35, ~0x052), "7,v,B", F_FLOAT, v9 }, +{ "fcmpd", CMPFCC(2)|F3F(2, 0x35, 0x052), CMPFCC(~2)|F3F(~2, ~0x35, ~0x052), "8,v,B", F_FLOAT, v9 }, +{ "fcmpd", CMPFCC(3)|F3F(2, 0x35, 0x052), CMPFCC(~3)|F3F(~2, ~0x35, ~0x052), "9,v,B", F_FLOAT, v9 }, +{ "fcmped", F3F(2, 0x35, 0x056), F3F(~2, ~0x35, ~0x056)|RD_G0, "v,B", F_FLOAT, v6 }, +{ "fcmped", CMPFCC(0)|F3F(2, 0x35, 0x056), CMPFCC(~0)|F3F(~2, ~0x35, ~0x056), "6,v,B", F_FLOAT, v9 }, +{ "fcmped", CMPFCC(1)|F3F(2, 0x35, 0x056), CMPFCC(~1)|F3F(~2, ~0x35, ~0x056), "7,v,B", F_FLOAT, v9 }, +{ "fcmped", CMPFCC(2)|F3F(2, 0x35, 0x056), CMPFCC(~2)|F3F(~2, ~0x35, ~0x056), "8,v,B", F_FLOAT, v9 }, +{ "fcmped", CMPFCC(3)|F3F(2, 0x35, 0x056), CMPFCC(~3)|F3F(~2, ~0x35, ~0x056), "9,v,B", F_FLOAT, v9 }, +{ "fcmpq", F3F(2, 0x35, 0x053), F3F(~2, ~0x35, ~0x053)|RD_G0, "V,R", F_FLOAT, v8 }, +{ "fcmpq", CMPFCC(0)|F3F(2, 0x35, 0x053), CMPFCC(~0)|F3F(~2, ~0x35, ~0x053), "6,V,R", F_FLOAT, v9 }, +{ "fcmpq", CMPFCC(1)|F3F(2, 0x35, 0x053), CMPFCC(~1)|F3F(~2, ~0x35, ~0x053), "7,V,R", F_FLOAT, v9 }, +{ "fcmpq", CMPFCC(2)|F3F(2, 0x35, 0x053), CMPFCC(~2)|F3F(~2, ~0x35, ~0x053), "8,V,R", F_FLOAT, v9 }, +{ "fcmpq", CMPFCC(3)|F3F(2, 0x35, 0x053), CMPFCC(~3)|F3F(~2, ~0x35, ~0x053), "9,V,R", F_FLOAT, v9 }, +{ "fcmpeq", F3F(2, 0x35, 0x057), F3F(~2, ~0x35, ~0x057)|RD_G0, "V,R", F_FLOAT, v8 }, +{ "fcmpeq", CMPFCC(0)|F3F(2, 0x35, 0x057), CMPFCC(~0)|F3F(~2, ~0x35, ~0x057), "6,V,R", F_FLOAT, v9 }, +{ "fcmpeq", CMPFCC(1)|F3F(2, 0x35, 0x057), CMPFCC(~1)|F3F(~2, ~0x35, ~0x057), "7,V,R", F_FLOAT, v9 }, +{ "fcmpeq", CMPFCC(2)|F3F(2, 0x35, 0x057), CMPFCC(~2)|F3F(~2, ~0x35, ~0x057), "8,V,R", F_FLOAT, v9 }, +{ "fcmpeq", CMPFCC(3)|F3F(2, 0x35, 0x057), CMPFCC(~3)|F3F(~2, ~0x35, ~0x057), "9,V,R", F_FLOAT, v9 }, +{ "fcmpx", F3F(2, 0x35, 0x053), F3F(~2, ~0x35, ~0x053)|RD_G0, "V,R", F_FLOAT|F_ALIAS, v8 }, +{ "fcmpx", CMPFCC(0)|F3F(2, 0x35, 0x053), CMPFCC(~0)|F3F(~2, ~0x35, ~0x053), "6,V,R", F_FLOAT|F_ALIAS, v9 }, +{ "fcmpx", CMPFCC(1)|F3F(2, 0x35, 0x053), CMPFCC(~1)|F3F(~2, ~0x35, ~0x053), "7,V,R", F_FLOAT|F_ALIAS, v9 }, +{ "fcmpx", CMPFCC(2)|F3F(2, 0x35, 0x053), CMPFCC(~2)|F3F(~2, ~0x35, ~0x053), "8,V,R", F_FLOAT|F_ALIAS, v9 }, +{ "fcmpx", CMPFCC(3)|F3F(2, 0x35, 0x053), CMPFCC(~3)|F3F(~2, ~0x35, ~0x053), "9,V,R", F_FLOAT|F_ALIAS, v9 }, +{ "fcmpex", F3F(2, 0x35, 0x057), F3F(~2, ~0x35, ~0x057)|RD_G0, "V,R", F_FLOAT|F_ALIAS, v8 }, +{ "fcmpex", CMPFCC(0)|F3F(2, 0x35, 0x057), CMPFCC(~0)|F3F(~2, ~0x35, ~0x057), "6,V,R", F_FLOAT|F_ALIAS, v9 }, +{ "fcmpex", CMPFCC(1)|F3F(2, 0x35, 0x057), CMPFCC(~1)|F3F(~2, ~0x35, ~0x057), "7,V,R", F_FLOAT|F_ALIAS, v9 }, +{ "fcmpex", CMPFCC(2)|F3F(2, 0x35, 0x057), CMPFCC(~2)|F3F(~2, ~0x35, ~0x057), "8,V,R", F_FLOAT|F_ALIAS, v9 }, +{ "fcmpex", CMPFCC(3)|F3F(2, 0x35, 0x057), CMPFCC(~3)|F3F(~2, ~0x35, ~0x057), "9,V,R", F_FLOAT|F_ALIAS, v9 }, +{ "fcmps", F3F(2, 0x35, 0x051), F3F(~2, ~0x35, ~0x051)|RD_G0, "e,f", F_FLOAT, v6 }, +{ "fcmps", CMPFCC(0)|F3F(2, 0x35, 0x051), CMPFCC(~0)|F3F(~2, ~0x35, ~0x051), "6,e,f", F_FLOAT, v9 }, +{ "fcmps", CMPFCC(1)|F3F(2, 0x35, 0x051), CMPFCC(~1)|F3F(~2, ~0x35, ~0x051), "7,e,f", F_FLOAT, v9 }, +{ "fcmps", CMPFCC(2)|F3F(2, 0x35, 0x051), CMPFCC(~2)|F3F(~2, ~0x35, ~0x051), "8,e,f", F_FLOAT, v9 }, +{ "fcmps", CMPFCC(3)|F3F(2, 0x35, 0x051), CMPFCC(~3)|F3F(~2, ~0x35, ~0x051), "9,e,f", F_FLOAT, v9 }, +{ "fcmpes", F3F(2, 0x35, 0x055), F3F(~2, ~0x35, ~0x055)|RD_G0, "e,f", F_FLOAT, v6 }, +{ "fcmpes", CMPFCC(0)|F3F(2, 0x35, 0x055), CMPFCC(~0)|F3F(~2, ~0x35, ~0x055), "6,e,f", F_FLOAT, v9 }, +{ "fcmpes", CMPFCC(1)|F3F(2, 0x35, 0x055), CMPFCC(~1)|F3F(~2, ~0x35, ~0x055), "7,e,f", F_FLOAT, v9 }, +{ "fcmpes", CMPFCC(2)|F3F(2, 0x35, 0x055), CMPFCC(~2)|F3F(~2, ~0x35, ~0x055), "8,e,f", F_FLOAT, v9 }, +{ "fcmpes", CMPFCC(3)|F3F(2, 0x35, 0x055), CMPFCC(~3)|F3F(~2, ~0x35, ~0x055), "9,e,f", F_FLOAT, v9 }, + +/* These Extended FPop (FIFO) instructions are new in the Fujitsu + MB86934, replacing the CPop instructions from v6 and later + processors. */ + +#define EFPOP1_2(name, op, args) { name, F3F(2, 0x36, op), F3F(~2, ~0x36, ~op)|RS1_G0, args, 0, sparclite } +#define EFPOP1_3(name, op, args) { name, F3F(2, 0x36, op), F3F(~2, ~0x36, ~op), args, 0, sparclite } +#define EFPOP2_2(name, op, args) { name, F3F(2, 0x37, op), F3F(~2, ~0x37, ~op)|RD_G0, args, 0, sparclite } + +EFPOP1_2 ("efitod", 0x0c8, "f,H"), +EFPOP1_2 ("efitos", 0x0c4, "f,g"), +EFPOP1_2 ("efdtoi", 0x0d2, "B,g"), +EFPOP1_2 ("efstoi", 0x0d1, "f,g"), +EFPOP1_2 ("efstod", 0x0c9, "f,H"), +EFPOP1_2 ("efdtos", 0x0c6, "B,g"), +EFPOP1_2 ("efmovs", 0x001, "f,g"), +EFPOP1_2 ("efnegs", 0x005, "f,g"), +EFPOP1_2 ("efabss", 0x009, "f,g"), +EFPOP1_2 ("efsqrtd", 0x02a, "B,H"), +EFPOP1_2 ("efsqrts", 0x029, "f,g"), +EFPOP1_3 ("efaddd", 0x042, "v,B,H"), +EFPOP1_3 ("efadds", 0x041, "e,f,g"), +EFPOP1_3 ("efsubd", 0x046, "v,B,H"), +EFPOP1_3 ("efsubs", 0x045, "e,f,g"), +EFPOP1_3 ("efdivd", 0x04e, "v,B,H"), +EFPOP1_3 ("efdivs", 0x04d, "e,f,g"), +EFPOP1_3 ("efmuld", 0x04a, "v,B,H"), +EFPOP1_3 ("efmuls", 0x049, "e,f,g"), +EFPOP1_3 ("efsmuld", 0x069, "e,f,H"), +EFPOP2_2 ("efcmpd", 0x052, "v,B"), +EFPOP2_2 ("efcmped", 0x056, "v,B"), +EFPOP2_2 ("efcmps", 0x051, "e,f"), +EFPOP2_2 ("efcmpes", 0x055, "e,f"), + +#undef EFPOP1_2 +#undef EFPOP1_3 +#undef EFPOP2_2 + +/* These are marked F_ALIAS, so that they won't conflict with sparclite insns + present. Otherwise, the F_ALIAS flag is ignored. */ +{ "cpop1", F3(2, 0x36, 0), F3(~2, ~0x36, ~1), "[1+2],d", F_ALIAS, v6notv9 }, +{ "cpop2", F3(2, 0x37, 0), F3(~2, ~0x37, ~1), "[1+2],d", F_ALIAS, v6notv9 }, + +/* sparclet specific insns */ + +COMMUTEOP ("umac", 0x3e, sparclet), +COMMUTEOP ("smac", 0x3f, sparclet), +COMMUTEOP ("umacd", 0x2e, sparclet), +COMMUTEOP ("smacd", 0x2f, sparclet), +COMMUTEOP ("umuld", 0x09, sparclet), +COMMUTEOP ("smuld", 0x0d, sparclet), + +{ "shuffle", F3(2, 0x2d, 0), F3(~2, ~0x2d, ~0)|ASI(~0), "1,2,d", 0, sparclet }, +{ "shuffle", F3(2, 0x2d, 1), F3(~2, ~0x2d, ~1), "1,i,d", 0, sparclet }, + +/* The manual isn't completely accurate on these insns. The `rs2' field is + treated as being 6 bits to account for 6 bit immediates to cpush. It is + assumed that it is intended that bit 5 is 0 when rs2 contains a reg. */ +#define BIT5 (1<<5) +{ "crdcxt", F3(2, 0x36, 0)|SLCPOP(4), F3(~2, ~0x36, ~0)|SLCPOP(~4)|BIT5|RS2(~0), "U,d", 0, sparclet }, +{ "cwrcxt", F3(2, 0x36, 0)|SLCPOP(3), F3(~2, ~0x36, ~0)|SLCPOP(~3)|BIT5|RS2(~0), "1,u", 0, sparclet }, +{ "cpush", F3(2, 0x36, 0)|SLCPOP(0), F3(~2, ~0x36, ~0)|SLCPOP(~0)|BIT5|RD(~0), "1,2", 0, sparclet }, +{ "cpush", F3(2, 0x36, 1)|SLCPOP(0), F3(~2, ~0x36, ~1)|SLCPOP(~0)|RD(~0), "1,Y", 0, sparclet }, +{ "cpusha", F3(2, 0x36, 0)|SLCPOP(1), F3(~2, ~0x36, ~0)|SLCPOP(~1)|BIT5|RD(~0), "1,2", 0, sparclet }, +{ "cpusha", F3(2, 0x36, 1)|SLCPOP(1), F3(~2, ~0x36, ~1)|SLCPOP(~1)|RD(~0), "1,Y", 0, sparclet }, +{ "cpull", F3(2, 0x36, 0)|SLCPOP(2), F3(~2, ~0x36, ~0)|SLCPOP(~2)|BIT5|RS1(~0)|RS2(~0), "d", 0, sparclet }, +#undef BIT5 + +/* sparclet coprocessor branch insns */ +#define SLCBCC2(opcode, mask, lose) \ + { opcode, (mask), ANNUL|(lose), "l", F_DELAYED|F_CONDBR, sparclet }, \ + { opcode, (mask)|ANNUL, (lose), ",a l", F_DELAYED|F_CONDBR, sparclet } +#define SLCBCC(opcode, mask) \ + SLCBCC2(opcode, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask))) + +/* cbn,cba can't be defined here because they're defined elsewhere and GAS + requires all mnemonics of the same name to be consecutive. */ +/*SLCBCC("cbn", 0), - already defined */ +SLCBCC("cbe", 1), +SLCBCC("cbf", 2), +SLCBCC("cbef", 3), +SLCBCC("cbr", 4), +SLCBCC("cber", 5), +SLCBCC("cbfr", 6), +SLCBCC("cbefr", 7), +/*SLCBCC("cba", 8), - already defined */ +SLCBCC("cbne", 9), +SLCBCC("cbnf", 10), +SLCBCC("cbnef", 11), +SLCBCC("cbnr", 12), +SLCBCC("cbner", 13), +SLCBCC("cbnfr", 14), +SLCBCC("cbnefr", 15), + +#undef SLCBCC2 +#undef SLCBCC + +{ "casa", F3(3, 0x3c, 0), F3(~3, ~0x3c, ~0), "[1]A,2,d", 0, v9 }, +{ "casa", F3(3, 0x3c, 1), F3(~3, ~0x3c, ~1), "[1]o,2,d", 0, v9 }, +{ "casxa", F3(3, 0x3e, 0), F3(~3, ~0x3e, ~0), "[1]A,2,d", 0, v9 }, +{ "casxa", F3(3, 0x3e, 1), F3(~3, ~0x3e, ~1), "[1]o,2,d", 0, v9 }, + +/* v9 synthetic insns */ +{ "iprefetch", F2(0, 1)|(2<<20)|BPRED, F2(~0, ~1)|(1<<20)|ANNUL|COND(~0), "G", 0, v9 }, /* bn,a,pt %xcc,label */ +{ "signx", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|ASI(~0)|RS2_G0, "1,d", F_ALIAS, v9 }, /* sra rs1,%g0,rd */ +{ "signx", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|ASI(~0)|RS2_G0, "r", F_ALIAS, v9 }, /* sra rd,%g0,rd */ +{ "clruw", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|ASI(~0)|RS2_G0, "1,d", F_ALIAS, v9 }, /* srl rs1,%g0,rd */ +{ "clruw", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|ASI(~0)|RS2_G0, "r", F_ALIAS, v9 }, /* srl rd,%g0,rd */ +{ "cas", F3(3, 0x3c, 0)|ASI(0x80), F3(~3, ~0x3c, ~0)|ASI(~0x80), "[1],2,d", F_ALIAS, v9 }, /* casa [rs1]ASI_P,rs2,rd */ +{ "casl", F3(3, 0x3c, 0)|ASI(0x88), F3(~3, ~0x3c, ~0)|ASI(~0x88), "[1],2,d", F_ALIAS, v9 }, /* casa [rs1]ASI_P_L,rs2,rd */ +{ "casx", F3(3, 0x3e, 0)|ASI(0x80), F3(~3, ~0x3e, ~0)|ASI(~0x80), "[1],2,d", F_ALIAS, v9 }, /* casxa [rs1]ASI_P,rs2,rd */ +{ "casxl", F3(3, 0x3e, 0)|ASI(0x88), F3(~3, ~0x3e, ~0)|ASI(~0x88), "[1],2,d", F_ALIAS, v9 }, /* casxa [rs1]ASI_P_L,rs2,rd */ + +/* Ultrasparc extensions */ +{ "shutdown", F3F(2, 0x36, 0x080), F3F(~2, ~0x36, ~0x080)|RD_G0|RS1_G0|RS2_G0, "", 0, v9a }, + +/* FIXME: Do we want to mark these as F_FLOAT, or something similar? */ +{ "fpadd16", F3F(2, 0x36, 0x050), F3F(~2, ~0x36, ~0x050), "v,B,H", 0, v9a }, +{ "fpadd16s", F3F(2, 0x36, 0x051), F3F(~2, ~0x36, ~0x051), "e,f,g", 0, v9a }, +{ "fpadd32", F3F(2, 0x36, 0x052), F3F(~2, ~0x36, ~0x052), "v,B,H", 0, v9a }, +{ "fpadd32s", F3F(2, 0x36, 0x053), F3F(~2, ~0x36, ~0x053), "e,f,g", 0, v9a }, +{ "fpsub16", F3F(2, 0x36, 0x054), F3F(~2, ~0x36, ~0x054), "v,B,H", 0, v9a }, +{ "fpsub16s", F3F(2, 0x36, 0x055), F3F(~2, ~0x36, ~0x055), "e,f,g", 0, v9a }, +{ "fpsub32", F3F(2, 0x36, 0x056), F3F(~2, ~0x36, ~0x056), "v,B,H", 0, v9a }, +{ "fpsub32s", F3F(2, 0x36, 0x057), F3F(~2, ~0x36, ~0x057), "e,f,g", 0, v9a }, + +{ "fpack32", F3F(2, 0x36, 0x03a), F3F(~2, ~0x36, ~0x03a), "v,B,H", 0, v9a }, +{ "fpack16", F3F(2, 0x36, 0x03b), F3F(~2, ~0x36, ~0x03b)|RS1_G0, "B,g", 0, v9a }, +{ "fpackfix", F3F(2, 0x36, 0x03d), F3F(~2, ~0x36, ~0x03d)|RS1_G0, "B,g", 0, v9a }, +{ "fexpand", F3F(2, 0x36, 0x04d), F3F(~2, ~0x36, ~0x04d)|RS1_G0, "f,H", 0, v9a }, +{ "fpmerge", F3F(2, 0x36, 0x04b), F3F(~2, ~0x36, ~0x04b), "e,f,H", 0, v9a }, + +/* Note that the mixing of 32/64 bit regs is intentional. */ +{ "fmul8x16", F3F(2, 0x36, 0x031), F3F(~2, ~0x36, ~0x031), "e,B,H", 0, v9a }, +{ "fmul8x16au", F3F(2, 0x36, 0x033), F3F(~2, ~0x36, ~0x033), "e,f,H", 0, v9a }, +{ "fmul8x16al", F3F(2, 0x36, 0x035), F3F(~2, ~0x36, ~0x035), "e,f,H", 0, v9a }, +{ "fmul8sux16", F3F(2, 0x36, 0x036), F3F(~2, ~0x36, ~0x036), "v,B,H", 0, v9a }, +{ "fmul8ulx16", F3F(2, 0x36, 0x037), F3F(~2, ~0x36, ~0x037), "v,B,H", 0, v9a }, +{ "fmuld8sux16", F3F(2, 0x36, 0x038), F3F(~2, ~0x36, ~0x038), "e,f,H", 0, v9a }, +{ "fmuld8ulx16", F3F(2, 0x36, 0x039), F3F(~2, ~0x36, ~0x039), "e,f,H", 0, v9a }, + +{ "alignaddr", F3F(2, 0x36, 0x018), F3F(~2, ~0x36, ~0x018), "1,2,d", 0, v9a }, +{ "alignaddrl", F3F(2, 0x36, 0x01a), F3F(~2, ~0x36, ~0x01a), "1,2,d", 0, v9a }, +{ "faligndata", F3F(2, 0x36, 0x048), F3F(~2, ~0x36, ~0x048), "v,B,H", 0, v9a }, + +{ "fzero", F3F(2, 0x36, 0x060), F3F(~2, ~0x36, ~0x060), "H", 0, v9a }, +{ "fzeros", F3F(2, 0x36, 0x061), F3F(~2, ~0x36, ~0x061), "g", 0, v9a }, +{ "fone", F3F(2, 0x36, 0x07e), F3F(~2, ~0x36, ~0x07e), "H", 0, v9a }, +{ "fones", F3F(2, 0x36, 0x07f), F3F(~2, ~0x36, ~0x07f), "g", 0, v9a }, +{ "fsrc1", F3F(2, 0x36, 0x074), F3F(~2, ~0x36, ~0x074), "v,H", 0, v9a }, +{ "fsrc1s", F3F(2, 0x36, 0x075), F3F(~2, ~0x36, ~0x075), "e,g", 0, v9a }, +{ "fsrc2", F3F(2, 0x36, 0x078), F3F(~2, ~0x36, ~0x078), "B,H", 0, v9a }, +{ "fsrc2s", F3F(2, 0x36, 0x079), F3F(~2, ~0x36, ~0x079), "f,g", 0, v9a }, +{ "fnot1", F3F(2, 0x36, 0x06a), F3F(~2, ~0x36, ~0x06a), "v,H", 0, v9a }, +{ "fnot1s", F3F(2, 0x36, 0x06b), F3F(~2, ~0x36, ~0x06b), "e,g", 0, v9a }, +{ "fnot2", F3F(2, 0x36, 0x066), F3F(~2, ~0x36, ~0x066), "B,H", 0, v9a }, +{ "fnot2s", F3F(2, 0x36, 0x067), F3F(~2, ~0x36, ~0x067), "f,g", 0, v9a }, +{ "for", F3F(2, 0x36, 0x07c), F3F(~2, ~0x36, ~0x07c), "v,B,H", 0, v9a }, +{ "fors", F3F(2, 0x36, 0x07d), F3F(~2, ~0x36, ~0x07d), "e,f,g", 0, v9a }, +{ "fnor", F3F(2, 0x36, 0x062), F3F(~2, ~0x36, ~0x062), "v,B,H", 0, v9a }, +{ "fnors", F3F(2, 0x36, 0x063), F3F(~2, ~0x36, ~0x063), "e,f,g", 0, v9a }, +{ "fand", F3F(2, 0x36, 0x070), F3F(~2, ~0x36, ~0x070), "v,B,H", 0, v9a }, +{ "fands", F3F(2, 0x36, 0x071), F3F(~2, ~0x36, ~0x071), "e,f,g", 0, v9a }, +{ "fnand", F3F(2, 0x36, 0x06e), F3F(~2, ~0x36, ~0x06e), "v,B,H", 0, v9a }, +{ "fnands", F3F(2, 0x36, 0x06f), F3F(~2, ~0x36, ~0x06f), "e,f,g", 0, v9a }, +{ "fxor", F3F(2, 0x36, 0x06c), F3F(~2, ~0x36, ~0x06c), "v,B,H", 0, v9a }, +{ "fxors", F3F(2, 0x36, 0x06d), F3F(~2, ~0x36, ~0x06d), "e,f,g", 0, v9a }, +{ "fxnor", F3F(2, 0x36, 0x072), F3F(~2, ~0x36, ~0x072), "v,B,H", 0, v9a }, +{ "fxnors", F3F(2, 0x36, 0x073), F3F(~2, ~0x36, ~0x073), "e,f,g", 0, v9a }, +{ "fornot1", F3F(2, 0x36, 0x07a), F3F(~2, ~0x36, ~0x07a), "v,B,H", 0, v9a }, +{ "fornot1s", F3F(2, 0x36, 0x07b), F3F(~2, ~0x36, ~0x07b), "e,f,g", 0, v9a }, +{ "fornot2", F3F(2, 0x36, 0x076), F3F(~2, ~0x36, ~0x076), "v,B,H", 0, v9a }, +{ "fornot2s", F3F(2, 0x36, 0x077), F3F(~2, ~0x36, ~0x077), "e,f,g", 0, v9a }, +{ "fandnot1", F3F(2, 0x36, 0x068), F3F(~2, ~0x36, ~0x068), "v,B,H", 0, v9a }, +{ "fandnot1s", F3F(2, 0x36, 0x069), F3F(~2, ~0x36, ~0x069), "e,f,g", 0, v9a }, +{ "fandnot2", F3F(2, 0x36, 0x064), F3F(~2, ~0x36, ~0x064), "v,B,H", 0, v9a }, +{ "fandnot2s", F3F(2, 0x36, 0x065), F3F(~2, ~0x36, ~0x065), "e,f,g", 0, v9a }, + +{ "fcmpgt16", F3F(2, 0x36, 0x028), F3F(~2, ~0x36, ~0x028), "v,B,d", 0, v9a }, +{ "fcmpgt32", F3F(2, 0x36, 0x02c), F3F(~2, ~0x36, ~0x02c), "v,B,d", 0, v9a }, +{ "fcmple16", F3F(2, 0x36, 0x020), F3F(~2, ~0x36, ~0x020), "v,B,d", 0, v9a }, +{ "fcmple32", F3F(2, 0x36, 0x024), F3F(~2, ~0x36, ~0x024), "v,B,d", 0, v9a }, +{ "fcmpne16", F3F(2, 0x36, 0x022), F3F(~2, ~0x36, ~0x022), "v,B,d", 0, v9a }, +{ "fcmpne32", F3F(2, 0x36, 0x026), F3F(~2, ~0x36, ~0x026), "v,B,d", 0, v9a }, +{ "fcmpeq16", F3F(2, 0x36, 0x02a), F3F(~2, ~0x36, ~0x02a), "v,B,d", 0, v9a }, +{ "fcmpeq32", F3F(2, 0x36, 0x02e), F3F(~2, ~0x36, ~0x02e), "v,B,d", 0, v9a }, + +{ "edge8", F3F(2, 0x36, 0x000), F3F(~2, ~0x36, ~0x000), "1,2,d", 0, v9a }, +{ "edge8l", F3F(2, 0x36, 0x002), F3F(~2, ~0x36, ~0x002), "1,2,d", 0, v9a }, +{ "edge16", F3F(2, 0x36, 0x004), F3F(~2, ~0x36, ~0x004), "1,2,d", 0, v9a }, +{ "edge16l", F3F(2, 0x36, 0x006), F3F(~2, ~0x36, ~0x006), "1,2,d", 0, v9a }, +{ "edge32", F3F(2, 0x36, 0x008), F3F(~2, ~0x36, ~0x008), "1,2,d", 0, v9a }, +{ "edge32l", F3F(2, 0x36, 0x00a), F3F(~2, ~0x36, ~0x00a), "1,2,d", 0, v9a }, + +{ "pdist", F3F(2, 0x36, 0x03e), F3F(~2, ~0x36, ~0x03e), "v,B,H", 0, v9a }, + +{ "array8", F3F(2, 0x36, 0x010), F3F(~2, ~0x36, ~0x010), "1,2,d", 0, v9a }, +{ "array16", F3F(2, 0x36, 0x012), F3F(~2, ~0x36, ~0x012), "1,2,d", 0, v9a }, +{ "array32", F3F(2, 0x36, 0x014), F3F(~2, ~0x36, ~0x014), "1,2,d", 0, v9a }, + +/* Cheetah instructions */ +{ "edge8n", F3F(2, 0x36, 0x001), F3F(~2, ~0x36, ~0x001), "1,2,d", 0, v9b }, +{ "edge8ln", F3F(2, 0x36, 0x003), F3F(~2, ~0x36, ~0x003), "1,2,d", 0, v9b }, +{ "edge16n", F3F(2, 0x36, 0x005), F3F(~2, ~0x36, ~0x005), "1,2,d", 0, v9b }, +{ "edge16ln", F3F(2, 0x36, 0x007), F3F(~2, ~0x36, ~0x007), "1,2,d", 0, v9b }, +{ "edge32n", F3F(2, 0x36, 0x009), F3F(~2, ~0x36, ~0x009), "1,2,d", 0, v9b }, +{ "edge32ln", F3F(2, 0x36, 0x00b), F3F(~2, ~0x36, ~0x00b), "1,2,d", 0, v9b }, + +{ "bmask", F3F(2, 0x36, 0x019), F3F(~2, ~0x36, ~0x019), "1,2,d", 0, v9b }, +{ "bshuffle", F3F(2, 0x36, 0x04c), F3F(~2, ~0x36, ~0x04c), "v,B,H", 0, v9b }, + +{ "siam", F3F(2, 0x36, 0x081), F3F(~2, ~0x36, ~0x081)|RD_G0|RS1_G0|RS2(~7), "3", 0, v9b }, + +/* More v9 specific insns, these need to come last so they do not clash + with v9a instructions such as "edge8" which looks like impdep1. */ + +#define IMPDEP(name, code) \ +{ name, F3(2, code, 0), F3(~2, ~code, ~0)|ASI(~0), "1,2,d", 0, v9notv9a }, \ +{ name, F3(2, code, 1), F3(~2, ~code, ~1), "1,i,d", 0, v9notv9a }, \ +{ name, F3(2, code, 0), F3(~2, ~code, ~0), "x,1,2,d", 0, v9notv9a }, \ +{ name, F3(2, code, 0), F3(~2, ~code, ~0), "x,e,f,g", 0, v9notv9a } + +IMPDEP ("impdep1", 0x36), +IMPDEP ("impdep2", 0x37), + +#undef IMPDEP + +{ "addxc", F3F(2, 0x36, 0x011), F3F(~2, ~0x36, ~0x011), "1,2,d", 0, v9b }, +{ "addxccc", F3F(2, 0x36, 0x013), F3F(~2, ~0x36, ~0x013), "1,2,d", 0, v9b }, +{ "umulxhi", F3F(2, 0x36, 0x016), F3F(~2, ~0x36, ~0x016), "1,2,d", 0, v9b }, + +}; + +static const int sparc_num_opcodes = ((sizeof sparc_opcodes)/(sizeof sparc_opcodes[0])); + +/* Utilities for argument parsing. */ + +typedef struct +{ + int value; + const char *name; +} arg; + +/* Look up VALUE in TABLE. */ + +static const char * +lookup_value (const arg *table, int value) +{ + const arg *p; + + for (p = table; p->name; ++p) + if (value == p->value) + return p->name; + + return NULL; +} + +/* Handle ASI's. */ + +static const arg asi_table_v8[] = +{ + { 0x00, "#ASI_M_RES00" }, + { 0x01, "#ASI_M_UNA01" }, + { 0x02, "#ASI_M_MXCC" }, + { 0x03, "#ASI_M_FLUSH_PROBE" }, + { 0x04, "#ASI_M_MMUREGS" }, + { 0x05, "#ASI_M_TLBDIAG" }, + { 0x06, "#ASI_M_DIAGS" }, + { 0x07, "#ASI_M_IODIAG" }, + { 0x08, "#ASI_M_USERTXT" }, + { 0x09, "#ASI_M_KERNELTXT" }, + { 0x0A, "#ASI_M_USERDATA" }, + { 0x0B, "#ASI_M_KERNELDATA" }, + { 0x0C, "#ASI_M_TXTC_TAG" }, + { 0x0D, "#ASI_M_TXTC_DATA" }, + { 0x0E, "#ASI_M_DATAC_TAG" }, + { 0x0F, "#ASI_M_DATAC_DATA" }, + { 0x10, "#ASI_M_FLUSH_PAGE" }, + { 0x11, "#ASI_M_FLUSH_SEG" }, + { 0x12, "#ASI_M_FLUSH_REGION" }, + { 0x13, "#ASI_M_FLUSH_CTX" }, + { 0x14, "#ASI_M_FLUSH_USER" }, + { 0x17, "#ASI_M_BCOPY" }, + { 0x18, "#ASI_M_IFLUSH_PAGE" }, + { 0x19, "#ASI_M_IFLUSH_SEG" }, + { 0x1A, "#ASI_M_IFLUSH_REGION" }, + { 0x1B, "#ASI_M_IFLUSH_CTX" }, + { 0x1C, "#ASI_M_IFLUSH_USER" }, + { 0x1F, "#ASI_M_BFILL" }, + { 0x20, "#ASI_M_BYPASS" }, + { 0x29, "#ASI_M_FBMEM" }, + { 0x2A, "#ASI_M_VMEUS" }, + { 0x2B, "#ASI_M_VMEPS" }, + { 0x2C, "#ASI_M_VMEUT" }, + { 0x2D, "#ASI_M_VMEPT" }, + { 0x2E, "#ASI_M_SBUS" }, + { 0x2F, "#ASI_M_CTL" }, + { 0x31, "#ASI_M_FLUSH_IWHOLE" }, + { 0x36, "#ASI_M_IC_FLCLEAR" }, + { 0x37, "#ASI_M_DC_FLCLEAR" }, + { 0x39, "#ASI_M_DCDR" }, + { 0x40, "#ASI_M_VIKING_TMP1" }, + { 0x41, "#ASI_M_VIKING_TMP2" }, + { 0x4c, "#ASI_M_ACTION" }, + { 0, NULL } +}; + +static const arg asi_table_v9[] = +{ + /* These are in the v9 architecture manual. */ + /* The shorter versions appear first, they're here because Sun's as has them. + Sun's as uses #ASI_P_L instead of #ASI_PL (which appears in the + UltraSPARC architecture manual). */ + { 0x04, "#ASI_N" }, + { 0x0c, "#ASI_N_L" }, + { 0x10, "#ASI_AIUP" }, + { 0x11, "#ASI_AIUS" }, + { 0x18, "#ASI_AIUP_L" }, + { 0x19, "#ASI_AIUS_L" }, + { 0x80, "#ASI_P" }, + { 0x81, "#ASI_S" }, + { 0x82, "#ASI_PNF" }, + { 0x83, "#ASI_SNF" }, + { 0x88, "#ASI_P_L" }, + { 0x89, "#ASI_S_L" }, + { 0x8a, "#ASI_PNF_L" }, + { 0x8b, "#ASI_SNF_L" }, + { 0x04, "#ASI_NUCLEUS" }, + { 0x0c, "#ASI_NUCLEUS_LITTLE" }, + { 0x10, "#ASI_AS_IF_USER_PRIMARY" }, + { 0x11, "#ASI_AS_IF_USER_SECONDARY" }, + { 0x18, "#ASI_AS_IF_USER_PRIMARY_LITTLE" }, + { 0x19, "#ASI_AS_IF_USER_SECONDARY_LITTLE" }, + { 0x80, "#ASI_PRIMARY" }, + { 0x81, "#ASI_SECONDARY" }, + { 0x82, "#ASI_PRIMARY_NOFAULT" }, + { 0x83, "#ASI_SECONDARY_NOFAULT" }, + { 0x88, "#ASI_PRIMARY_LITTLE" }, + { 0x89, "#ASI_SECONDARY_LITTLE" }, + { 0x8a, "#ASI_PRIMARY_NOFAULT_LITTLE" }, + { 0x8b, "#ASI_SECONDARY_NOFAULT_LITTLE" }, + /* These are UltraSPARC extensions. */ + { 0x14, "#ASI_PHYS_USE_EC"}, + { 0x15, "#ASI_PHYS_BYPASS_EC_WITH_EBIT"}, + { 0x45, "#ASI_LSU_CONTROL_REG"}, + { 0x47, "#ASI_DCACHE_TAG"}, + { 0x4a, "#ASI_UPA_CONFIG_REG"}, + { 0x50, "#ASI_IMMU" }, + { 0x51, "#ASI_IMMU_TSB_8KB_PTR_REG" }, + { 0x52, "#ASI_IMMU_TSB_64KB_PTR_REG" }, + /*{ 0x53, "#reserved?" },*/ + { 0x54, "#ASI_ITLB_DATA_IN_REG" }, + { 0x55, "#ASI_ITLB_DATA_ACCESS_REG" }, + { 0x56, "#ASI_ITLB_TAG_READ_REG" }, + { 0x57, "#ASI_IMMU_DEMAP" }, + { 0x58, "#ASI_DMMU" }, + { 0x59, "#ASI_DMMU_TSB_8KB_PTR_REG" }, + { 0x5a, "#ASI_DMMU_TSB_64KB_PTR_REG" }, + { 0x5b, "#ASI_DMMU_TSB_DIRECT_PTR_REG" }, + { 0x5c, "#ASI_DTLB_DATA_IN_REG" }, + { 0x5d, "#ASI_DTLB_DATA_ACCESS_REG" }, + { 0x5e, "#ASI_DTLB_TAG_READ_REG" }, + { 0x5f, "#ASI_DMMU_DEMAP" }, + { 0x67, "#ASI_IC_TAG"}, + /* FIXME: There are dozens of them. Not sure we want them all. + Most are for kernel building but some are for vis type stuff. */ + { 0, NULL } +}; + +/* Return the name for ASI value VALUE or NULL if not found. */ + +static const char * +sparc_decode_asi_v9 (int value) +{ + return lookup_value (asi_table_v9, value); +} + +static const char * +sparc_decode_asi_v8 (int value) +{ + return lookup_value (asi_table_v8, value); +} + +/* Handle membar masks. */ + +static const arg membar_table[] = +{ + { 0x40, "#Sync" }, + { 0x20, "#MemIssue" }, + { 0x10, "#Lookaside" }, + { 0x08, "#StoreStore" }, + { 0x04, "#LoadStore" }, + { 0x02, "#StoreLoad" }, + { 0x01, "#LoadLoad" }, + { 0, NULL } +}; + +/* Return the name for membar value VALUE or NULL if not found. */ + +static const char * +sparc_decode_membar (int value) +{ + return lookup_value (membar_table, value); +} + +/* Handle prefetch args. */ + +static const arg prefetch_table[] = +{ + { 0, "#n_reads" }, + { 1, "#one_read" }, + { 2, "#n_writes" }, + { 3, "#one_write" }, + { 4, "#page" }, + { 16, "#invalidate" }, + { 0, NULL } +}; + +/* Return the name for prefetch value VALUE or NULL if not found. */ + +static const char * +sparc_decode_prefetch (int value) +{ + return lookup_value (prefetch_table, value); +} + +/* Handle sparclet coprocessor registers. */ + +static const arg sparclet_cpreg_table[] = +{ + { 0, "%ccsr" }, + { 1, "%ccfr" }, + { 2, "%cccrcr" }, + { 3, "%ccpr" }, + { 4, "%ccsr2" }, + { 5, "%cccrr" }, + { 6, "%ccrstr" }, + { 0, NULL } +}; + +/* Return the name for sparclet cpreg value VALUE or NULL if not found. */ + +static const char * +sparc_decode_sparclet_cpreg (int value) +{ + return lookup_value (sparclet_cpreg_table, value); +} + +#undef MASK_V9 + +/* opcodes/sparc-dis.c */ + +/* Print SPARC instructions. + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +/* Bitmask of v9 architectures. */ +#define MASK_V9 ((1 << SPARC_OPCODE_ARCH_V9) \ + | (1 << SPARC_OPCODE_ARCH_V9A) \ + | (1 << SPARC_OPCODE_ARCH_V9B)) +/* 1 if INSN is for v9 only. */ +#define V9_ONLY_P(insn) (! ((insn)->architecture & ~MASK_V9)) +/* 1 if INSN is for v9. */ +#define V9_P(insn) (((insn)->architecture & MASK_V9) != 0) + +/* The sorted opcode table. */ +static const sparc_opcode **sorted_opcodes; + +/* For faster lookup, after insns are sorted they are hashed. */ +/* ??? I think there is room for even more improvement. */ + +#define HASH_SIZE 256 +/* It is important that we only look at insn code bits as that is how the + opcode table is hashed. OPCODE_BITS is a table of valid bits for each + of the main types (0,1,2,3). */ +static const int opcode_bits[4] = { 0x01c00000, 0x0, 0x01f80000, 0x01f80000 }; +#define HASH_INSN(INSN) \ + ((((INSN) >> 24) & 0xc0) | (((INSN) & opcode_bits[((INSN) >> 30) & 3]) >> 19)) +typedef struct sparc_opcode_hash +{ + struct sparc_opcode_hash *next; + const sparc_opcode *opcode; +} sparc_opcode_hash; + +static sparc_opcode_hash *opcode_hash_table[HASH_SIZE]; + +/* Sign-extend a value which is N bits long. */ +#define SEX(value, bits) \ + ((((int)(value)) << ((8 * sizeof (int)) - bits)) \ + >> ((8 * sizeof (int)) - bits) ) + +static const char * const reg_names[] = +{ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", + "o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7", + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", + "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", + "f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39", + "f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47", + "f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55", + "f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63", +/* psr, wim, tbr, fpsr, cpsr are v8 only. */ + "y", "psr", "wim", "tbr", "pc", "npc", "fpsr", "cpsr" +}; + +#define freg_names (®_names[4 * 8]) + +/* These are ordered according to there register number in + rdpr and wrpr insns. */ +static const char * const v9_priv_reg_names[] = +{ + "tpc", "tnpc", "tstate", "tt", "tick", "tba", "pstate", "tl", + "pil", "cwp", "cansave", "canrestore", "cleanwin", "otherwin", + "wstate", "fq", "gl" + /* "ver" - special cased */ +}; + +/* These are ordered according to there register number in + rdhpr and wrhpr insns. */ +static const char * const v9_hpriv_reg_names[] = +{ + "hpstate", "htstate", "resv2", "hintp", "resv4", "htba", "hver", + "resv7", "resv8", "resv9", "resv10", "resv11", "resv12", "resv13", + "resv14", "resv15", "resv16", "resv17", "resv18", "resv19", "resv20", + "resv21", "resv22", "resv23", "resv24", "resv25", "resv26", "resv27", + "resv28", "resv29", "resv30", "hstick_cmpr" +}; + +/* These are ordered according to there register number in + rd and wr insns (-16). */ +static const char * const v9a_asr_reg_names[] = +{ + "pcr", "pic", "dcr", "gsr", "set_softint", "clear_softint", + "softint", "tick_cmpr", "sys_tick", "sys_tick_cmpr" +}; + +/* Macros used to extract instruction fields. Not all fields have + macros defined here, only those which are actually used. */ + +#define X_RD(i) (((i) >> 25) & 0x1f) +#define X_RS1(i) (((i) >> 14) & 0x1f) +#define X_LDST_I(i) (((i) >> 13) & 1) +#define X_ASI(i) (((i) >> 5) & 0xff) +#define X_RS2(i) (((i) >> 0) & 0x1f) +#define X_IMM(i,n) (((i) >> 0) & ((1 << (n)) - 1)) +#define X_SIMM(i,n) SEX (X_IMM ((i), (n)), (n)) +#define X_DISP22(i) (((i) >> 0) & 0x3fffff) +#define X_IMM22(i) X_DISP22 (i) +#define X_DISP30(i) (((i) >> 0) & 0x3fffffff) + +/* These are for v9. */ +#define X_DISP16(i) (((((i) >> 20) & 3) << 14) | (((i) >> 0) & 0x3fff)) +#define X_DISP19(i) (((i) >> 0) & 0x7ffff) +#define X_MEMBAR(i) ((i) & 0x7f) + +/* Here is the union which was used to extract instruction fields + before the shift and mask macros were written. + + union sparc_insn + { + unsigned long int code; + struct + { + unsigned int anop:2; + #define op ldst.anop + unsigned int anrd:5; + #define rd ldst.anrd + unsigned int op3:6; + unsigned int anrs1:5; + #define rs1 ldst.anrs1 + unsigned int i:1; + unsigned int anasi:8; + #define asi ldst.anasi + unsigned int anrs2:5; + #define rs2 ldst.anrs2 + #define shcnt rs2 + } ldst; + struct + { + unsigned int anop:2, anrd:5, op3:6, anrs1:5, i:1; + unsigned int IMM13:13; + #define imm13 IMM13.IMM13 + } IMM13; + struct + { + unsigned int anop:2; + unsigned int a:1; + unsigned int cond:4; + unsigned int op2:3; + unsigned int DISP22:22; + #define disp22 branch.DISP22 + #define imm22 disp22 + } branch; + struct + { + unsigned int anop:2; + unsigned int a:1; + unsigned int z:1; + unsigned int rcond:3; + unsigned int op2:3; + unsigned int DISP16HI:2; + unsigned int p:1; + unsigned int _rs1:5; + unsigned int DISP16LO:14; + } branch16; + struct + { + unsigned int anop:2; + unsigned int adisp30:30; + #define disp30 call.adisp30 + } call; + }; */ + +/* Nonzero if INSN is the opcode for a delayed branch. */ + +static int +is_delayed_branch (unsigned long insn) +{ + sparc_opcode_hash *op; + + for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next) + { + const sparc_opcode *opcode = op->opcode; + + if ((opcode->match & insn) == opcode->match + && (opcode->lose & insn) == 0) + return opcode->flags & F_DELAYED; + } + return 0; +} + +/* extern void qsort (); */ + +/* Records current mask of SPARC_OPCODE_ARCH_FOO values, used to pass value + to compare_opcodes. */ +static unsigned int current_arch_mask; + +/* Given BFD mach number, return a mask of SPARC_OPCODE_ARCH_FOO values. */ + +static int +compute_arch_mask (unsigned long mach) +{ + switch (mach) + { + case 0 : + case bfd_mach_sparc : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8); + case bfd_mach_sparc_sparclet : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLET); + case bfd_mach_sparc_sparclite : + case bfd_mach_sparc_sparclite_le : + /* sparclites insns are recognized by default (because that's how + they've always been treated, for better or worse). Kludge this by + indicating generic v8 is also selected. */ + return (SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLITE) + | SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8)); + case bfd_mach_sparc_v8plus : + case bfd_mach_sparc_v9 : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9); + case bfd_mach_sparc_v8plusa : + case bfd_mach_sparc_v9a : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A); + case bfd_mach_sparc_v8plusb : + case bfd_mach_sparc_v9b : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9B); + } + abort (); +} + +/* Compare opcodes A and B. */ + +static int +compare_opcodes (const void * a, const void * b) +{ + sparc_opcode *op0 = * (sparc_opcode **) a; + sparc_opcode *op1 = * (sparc_opcode **) b; + unsigned long int match0 = op0->match, match1 = op1->match; + unsigned long int lose0 = op0->lose, lose1 = op1->lose; + register unsigned int i; + + /* If one (and only one) insn isn't supported by the current architecture, + prefer the one that is. If neither are supported, but they're both for + the same architecture, continue processing. Otherwise (both unsupported + and for different architectures), prefer lower numbered arch's (fudged + by comparing the bitmasks). */ + if (op0->architecture & current_arch_mask) + { + if (! (op1->architecture & current_arch_mask)) + return -1; + } + else + { + if (op1->architecture & current_arch_mask) + return 1; + else if (op0->architecture != op1->architecture) + return op0->architecture - op1->architecture; + } + + /* If a bit is set in both match and lose, there is something + wrong with the opcode table. */ + if (match0 & lose0) + { + fprintf + (stderr, + /* xgettext:c-format */ + _("Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n"), + op0->name, match0, lose0); + op0->lose &= ~op0->match; + lose0 = op0->lose; + } + + if (match1 & lose1) + { + fprintf + (stderr, + /* xgettext:c-format */ + _("Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n"), + op1->name, match1, lose1); + op1->lose &= ~op1->match; + lose1 = op1->lose; + } + + /* Because the bits that are variable in one opcode are constant in + another, it is important to order the opcodes in the right order. */ + for (i = 0; i < 32; ++i) + { + unsigned long int x = 1 << i; + int x0 = (match0 & x) != 0; + int x1 = (match1 & x) != 0; + + if (x0 != x1) + return x1 - x0; + } + + for (i = 0; i < 32; ++i) + { + unsigned long int x = 1 << i; + int x0 = (lose0 & x) != 0; + int x1 = (lose1 & x) != 0; + + if (x0 != x1) + return x1 - x0; + } + + /* They are functionally equal. So as long as the opcode table is + valid, we can put whichever one first we want, on aesthetic grounds. */ + + /* Our first aesthetic ground is that aliases defer to real insns. */ + { + int alias_diff = (op0->flags & F_ALIAS) - (op1->flags & F_ALIAS); + + if (alias_diff != 0) + /* Put the one that isn't an alias first. */ + return alias_diff; + } + + /* Except for aliases, two "identical" instructions had + better have the same opcode. This is a sanity check on the table. */ + i = strcmp (op0->name, op1->name); + if (i) + { + if (op0->flags & F_ALIAS) /* If they're both aliases, be arbitrary. */ + return i; + else + fprintf (stderr, + /* xgettext:c-format */ + _("Internal error: bad sparc-opcode.h: \"%s\" == \"%s\"\n"), + op0->name, op1->name); + } + + /* Fewer arguments are preferred. */ + { + int length_diff = strlen (op0->args) - strlen (op1->args); + + if (length_diff != 0) + /* Put the one with fewer arguments first. */ + return length_diff; + } + + /* Put 1+i before i+1. */ + { + char *p0 = (char *) strchr (op0->args, '+'); + char *p1 = (char *) strchr (op1->args, '+'); + + if (p0 && p1) + { + /* There is a plus in both operands. Note that a plus + sign cannot be the first character in args, + so the following [-1]'s are valid. */ + if (p0[-1] == 'i' && p1[1] == 'i') + /* op0 is i+1 and op1 is 1+i, so op1 goes first. */ + return 1; + if (p0[1] == 'i' && p1[-1] == 'i') + /* op0 is 1+i and op1 is i+1, so op0 goes first. */ + return -1; + } + } + + /* Put 1,i before i,1. */ + { + int i0 = strncmp (op0->args, "i,1", 3) == 0; + int i1 = strncmp (op1->args, "i,1", 3) == 0; + + if (i0 ^ i1) + return i0 - i1; + } + + /* They are, as far as we can tell, identical. + Since qsort may have rearranged the table partially, there is + no way to tell which one was first in the opcode table as + written, so just say there are equal. */ + /* ??? This is no longer true now that we sort a vector of pointers, + not the table itself. */ + return 0; +} + +/* Build a hash table from the opcode table. + OPCODE_TABLE is a sorted list of pointers into the opcode table. */ + +static void +build_hash_table (const sparc_opcode **opcode_table, + sparc_opcode_hash **hash_table, + int num_opcodes) +{ + int i; + int hash_count[HASH_SIZE]; + static sparc_opcode_hash *hash_buf = NULL; + + /* Start at the end of the table and work backwards so that each + chain is sorted. */ + + memset (hash_table, 0, HASH_SIZE * sizeof (hash_table[0])); + memset (hash_count, 0, HASH_SIZE * sizeof (hash_count[0])); + if (hash_buf != NULL) + free (hash_buf); + hash_buf = malloc (sizeof (* hash_buf) * num_opcodes); + for (i = num_opcodes - 1; i >= 0; --i) + { + int hash = HASH_INSN (opcode_table[i]->match); + sparc_opcode_hash *h = &hash_buf[i]; + + h->next = hash_table[hash]; + h->opcode = opcode_table[i]; + hash_table[hash] = h; + ++hash_count[hash]; + } + +#if 0 /* for debugging */ + { + int min_count = num_opcodes, max_count = 0; + int total; + + for (i = 0; i < HASH_SIZE; ++i) + { + if (hash_count[i] < min_count) + min_count = hash_count[i]; + if (hash_count[i] > max_count) + max_count = hash_count[i]; + total += hash_count[i]; + } + + printf ("Opcode hash table stats: min %d, max %d, ave %f\n", + min_count, max_count, (double) total / HASH_SIZE); + } +#endif +} + +/* Print one instruction from MEMADDR on INFO->STREAM. + + We suffix the instruction with a comment that gives the absolute + address involved, as well as its symbolic form, if the instruction + is preceded by a findable `sethi' and it either adds an immediate + displacement to that register, or it is an `add' or `or' instruction + on that register. */ + +int +print_insn_sparc (bfd_vma memaddr, disassemble_info *info) +{ + FILE *stream = info->stream; + bfd_byte buffer[4]; + unsigned long insn; + sparc_opcode_hash *op; + /* Nonzero of opcode table has been initialized. */ + static int opcodes_initialized = 0; + /* bfd mach number of last call. */ + static unsigned long current_mach = 0; + bfd_vma (*getword) (const unsigned char *); + + if (!opcodes_initialized + || info->mach != current_mach) + { + int i; + + current_arch_mask = compute_arch_mask (info->mach); + + if (!opcodes_initialized) + sorted_opcodes = + malloc (sparc_num_opcodes * sizeof (sparc_opcode *)); + /* Reset the sorted table so we can resort it. */ + for (i = 0; i < sparc_num_opcodes; ++i) + sorted_opcodes[i] = &sparc_opcodes[i]; + qsort ((char *) sorted_opcodes, sparc_num_opcodes, + sizeof (sorted_opcodes[0]), compare_opcodes); + + build_hash_table (sorted_opcodes, opcode_hash_table, sparc_num_opcodes); + current_mach = info->mach; + opcodes_initialized = 1; + } + + { + int status = + (*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info); + + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + } + + /* On SPARClite variants such as DANlite (sparc86x), instructions + are always big-endian even when the machine is in little-endian mode. */ + if (info->endian == BFD_ENDIAN_BIG || info->mach == bfd_mach_sparc_sparclite) + getword = bfd_getb32; + else + getword = bfd_getl32; + + insn = getword (buffer); + + info->insn_info_valid = 1; /* We do return this info. */ + info->insn_type = dis_nonbranch; /* Assume non branch insn. */ + info->branch_delay_insns = 0; /* Assume no delay. */ + info->target = 0; /* Assume no target known. */ + + for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next) + { + const sparc_opcode *opcode = op->opcode; + + /* If the insn isn't supported by the current architecture, skip it. */ + if (! (opcode->architecture & current_arch_mask)) + continue; + + if ((opcode->match & insn) == opcode->match + && (opcode->lose & insn) == 0) + { + /* Nonzero means that we have found an instruction which has + the effect of adding or or'ing the imm13 field to rs1. */ + int imm_added_to_rs1 = 0; + int imm_ored_to_rs1 = 0; + + /* Nonzero means that we have found a plus sign in the args + field of the opcode table. */ + int found_plus = 0; + + /* Nonzero means we have an annulled branch. */ + /* int is_annulled = 0; */ /* see FIXME below */ + + /* Do we have an `add' or `or' instruction combining an + immediate with rs1? */ + if (opcode->match == 0x80102000) /* or */ + imm_ored_to_rs1 = 1; + if (opcode->match == 0x80002000) /* add */ + imm_added_to_rs1 = 1; + + if (X_RS1 (insn) != X_RD (insn) + && strchr (opcode->args, 'r') != NULL) + /* Can't do simple format if source and dest are different. */ + continue; + if (X_RS2 (insn) != X_RD (insn) + && strchr (opcode->args, 'O') != NULL) + /* Can't do simple format if source and dest are different. */ + continue; + + (*info->fprintf_func) (stream, "%s", opcode->name); + + { + const char *s; + + if (opcode->args[0] != ',') + (*info->fprintf_func) (stream, " "); + + for (s = opcode->args; *s != '\0'; ++s) + { + while (*s == ',') + { + (*info->fprintf_func) (stream, ","); + ++s; + switch (*s) + { + case 'a': + (*info->fprintf_func) (stream, "a"); + /* is_annulled = 1; */ /* see FIXME below */ + ++s; + continue; + case 'N': + (*info->fprintf_func) (stream, "pn"); + ++s; + continue; + + case 'T': + (*info->fprintf_func) (stream, "pt"); + ++s; + continue; + + default: + break; + } + } + + (*info->fprintf_func) (stream, " "); + + switch (*s) + { + case '+': + found_plus = 1; + /* Fall through. */ + + default: + (*info->fprintf_func) (stream, "%c", *s); + break; + + case '#': + (*info->fprintf_func) (stream, "0"); + break; + +#define reg(n) (*info->fprintf_func) (stream, "%%%s", reg_names[n]) + case '1': + case 'r': + reg (X_RS1 (insn)); + break; + + case '2': + case 'O': + reg (X_RS2 (insn)); + break; + + case 'd': + reg (X_RD (insn)); + break; +#undef reg + +#define freg(n) (*info->fprintf_func) (stream, "%%%s", freg_names[n]) +#define fregx(n) (*info->fprintf_func) (stream, "%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)]) + case 'e': + freg (X_RS1 (insn)); + break; + case 'v': /* Double/even. */ + case 'V': /* Quad/multiple of 4. */ + fregx (X_RS1 (insn)); + break; + + case 'f': + freg (X_RS2 (insn)); + break; + case 'B': /* Double/even. */ + case 'R': /* Quad/multiple of 4. */ + fregx (X_RS2 (insn)); + break; + + case 'g': + freg (X_RD (insn)); + break; + case 'H': /* Double/even. */ + case 'J': /* Quad/multiple of 4. */ + fregx (X_RD (insn)); + break; +#undef freg +#undef fregx + +#define creg(n) (*info->fprintf_func) (stream, "%%c%u", (unsigned int) (n)) + case 'b': + creg (X_RS1 (insn)); + break; + + case 'c': + creg (X_RS2 (insn)); + break; + + case 'D': + creg (X_RD (insn)); + break; +#undef creg + + case 'h': + (*info->fprintf_func) (stream, "%%hi(%#x)", + ((unsigned) 0xFFFFFFFF + & ((int) X_IMM22 (insn) << 10))); + break; + + case 'i': /* 13 bit immediate. */ + case 'I': /* 11 bit immediate. */ + case 'j': /* 10 bit immediate. */ + { + int imm; + + if (*s == 'i') + imm = X_SIMM (insn, 13); + else if (*s == 'I') + imm = X_SIMM (insn, 11); + else + imm = X_SIMM (insn, 10); + + /* Check to see whether we have a 1+i, and take + note of that fact. + + Note: because of the way we sort the table, + we will be matching 1+i rather than i+1, + so it is OK to assume that i is after +, + not before it. */ + if (found_plus) + imm_added_to_rs1 = 1; + + if (imm <= 9) + (*info->fprintf_func) (stream, "%d", imm); + else + (*info->fprintf_func) (stream, "%#x", imm); + } + break; + + case 'X': /* 5 bit unsigned immediate. */ + case 'Y': /* 6 bit unsigned immediate. */ + { + int imm = X_IMM (insn, *s == 'X' ? 5 : 6); + + if (imm <= 9) + (info->fprintf_func) (stream, "%d", imm); + else + (info->fprintf_func) (stream, "%#x", (unsigned) imm); + } + break; + + case '3': + (info->fprintf_func) (stream, "%ld", X_IMM (insn, 3)); + break; + + case 'K': + { + int mask = X_MEMBAR (insn); + int bit = 0x40, printed_one = 0; + const char *name; + + if (mask == 0) + (info->fprintf_func) (stream, "0"); + else + while (bit) + { + if (mask & bit) + { + if (printed_one) + (info->fprintf_func) (stream, "|"); + name = sparc_decode_membar (bit); + (info->fprintf_func) (stream, "%s", name); + printed_one = 1; + } + bit >>= 1; + } + break; + } + + case 'k': + info->target = memaddr + SEX (X_DISP16 (insn), 16) * 4; + (*info->print_address_func) (info->target, info); + break; + + case 'G': + info->target = memaddr + SEX (X_DISP19 (insn), 19) * 4; + (*info->print_address_func) (info->target, info); + break; + + case '6': + case '7': + case '8': + case '9': + (*info->fprintf_func) (stream, "%%fcc%c", *s - '6' + '0'); + break; + + case 'z': + (*info->fprintf_func) (stream, "%%icc"); + break; + + case 'Z': + (*info->fprintf_func) (stream, "%%xcc"); + break; + + case 'E': + (*info->fprintf_func) (stream, "%%ccr"); + break; + + case 's': + (*info->fprintf_func) (stream, "%%fprs"); + break; + + case 'o': + (*info->fprintf_func) (stream, "%%asi"); + break; + + case 'W': + (*info->fprintf_func) (stream, "%%tick"); + break; + + case 'P': + (*info->fprintf_func) (stream, "%%pc"); + break; + + case '?': + if (X_RS1 (insn) == 31) + (*info->fprintf_func) (stream, "%%ver"); + else if ((unsigned) X_RS1 (insn) < 17) + (*info->fprintf_func) (stream, "%%%s", + v9_priv_reg_names[X_RS1 (insn)]); + else + (*info->fprintf_func) (stream, "%%reserved"); + break; + + case '!': + if ((unsigned) X_RD (insn) < 17) + (*info->fprintf_func) (stream, "%%%s", + v9_priv_reg_names[X_RD (insn)]); + else + (*info->fprintf_func) (stream, "%%reserved"); + break; + + case '$': + if ((unsigned) X_RS1 (insn) < 32) + (*info->fprintf_func) (stream, "%%%s", + v9_hpriv_reg_names[X_RS1 (insn)]); + else + (*info->fprintf_func) (stream, "%%reserved"); + break; + + case '%': + if ((unsigned) X_RD (insn) < 32) + (*info->fprintf_func) (stream, "%%%s", + v9_hpriv_reg_names[X_RD (insn)]); + else + (*info->fprintf_func) (stream, "%%reserved"); + break; + + case '/': + if (X_RS1 (insn) < 16 || X_RS1 (insn) > 25) + (*info->fprintf_func) (stream, "%%reserved"); + else + (*info->fprintf_func) (stream, "%%%s", + v9a_asr_reg_names[X_RS1 (insn)-16]); + break; + + case '_': + if (X_RD (insn) < 16 || X_RD (insn) > 25) + (*info->fprintf_func) (stream, "%%reserved"); + else + (*info->fprintf_func) (stream, "%%%s", + v9a_asr_reg_names[X_RD (insn)-16]); + break; + + case '*': + { + const char *name = sparc_decode_prefetch (X_RD (insn)); + + if (name) + (*info->fprintf_func) (stream, "%s", name); + else + (*info->fprintf_func) (stream, "%ld", X_RD (insn)); + break; + } + + case 'M': + (*info->fprintf_func) (stream, "%%asr%ld", X_RS1 (insn)); + break; + + case 'm': + (*info->fprintf_func) (stream, "%%asr%ld", X_RD (insn)); + break; + + case 'L': + info->target = memaddr + SEX (X_DISP30 (insn), 30) * 4; + (*info->print_address_func) (info->target, info); + break; + + case 'n': + (*info->fprintf_func) + (stream, "%#x", SEX (X_DISP22 (insn), 22)); + break; + + case 'l': + info->target = memaddr + SEX (X_DISP22 (insn), 22) * 4; + (*info->print_address_func) (info->target, info); + break; + + case 'A': + { + const char *name; + + if ((info->mach == bfd_mach_sparc_v8plusa) || + ((info->mach >= bfd_mach_sparc_v9) && + (info->mach <= bfd_mach_sparc_v9b))) + name = sparc_decode_asi_v9 (X_ASI (insn)); + else + name = sparc_decode_asi_v8 (X_ASI (insn)); + + if (name) + (*info->fprintf_func) (stream, "%s", name); + else + (*info->fprintf_func) (stream, "(%ld)", X_ASI (insn)); + break; + } + + case 'C': + (*info->fprintf_func) (stream, "%%csr"); + break; + + case 'F': + (*info->fprintf_func) (stream, "%%fsr"); + break; + + case 'p': + (*info->fprintf_func) (stream, "%%psr"); + break; + + case 'q': + (*info->fprintf_func) (stream, "%%fq"); + break; + + case 'Q': + (*info->fprintf_func) (stream, "%%cq"); + break; + + case 't': + (*info->fprintf_func) (stream, "%%tbr"); + break; + + case 'w': + (*info->fprintf_func) (stream, "%%wim"); + break; + + case 'x': + (*info->fprintf_func) (stream, "%ld", + ((X_LDST_I (insn) << 8) + + X_ASI (insn))); + break; + + case 'y': + (*info->fprintf_func) (stream, "%%y"); + break; + + case 'u': + case 'U': + { + int val = *s == 'U' ? X_RS1 (insn) : X_RD (insn); + const char *name = sparc_decode_sparclet_cpreg (val); + + if (name) + (*info->fprintf_func) (stream, "%s", name); + else + (*info->fprintf_func) (stream, "%%cpreg(%d)", val); + break; + } + } + } + } + + /* If we are adding or or'ing something to rs1, then + check to see whether the previous instruction was + a sethi to the same register as in the sethi. + If so, attempt to print the result of the add or + or (in this context add and or do the same thing) + and its symbolic value. */ + if (imm_ored_to_rs1 || imm_added_to_rs1) + { + unsigned long prev_insn; + int errcode; + + if (memaddr >= 4) + errcode = + (*info->read_memory_func) + (memaddr - 4, buffer, sizeof (buffer), info); + else + errcode = 1; + + prev_insn = getword (buffer); + + if (errcode == 0) + { + /* If it is a delayed branch, we need to look at the + instruction before the delayed branch. This handles + sequences such as: + + sethi %o1, %hi(_foo), %o1 + call _printf + or %o1, %lo(_foo), %o1 */ + + if (is_delayed_branch (prev_insn)) + { + if (memaddr >= 8) + errcode = (*info->read_memory_func) + (memaddr - 8, buffer, sizeof (buffer), info); + else + errcode = 1; + + prev_insn = getword (buffer); + } + } + + /* If there was a problem reading memory, then assume + the previous instruction was not sethi. */ + if (errcode == 0) + { + /* Is it sethi to the same register? */ + if ((prev_insn & 0xc1c00000) == 0x01000000 + && X_RD (prev_insn) == X_RS1 (insn)) + { + (*info->fprintf_func) (stream, "\t! "); + info->target = + ((unsigned) 0xFFFFFFFF + & ((int) X_IMM22 (prev_insn) << 10)); + if (imm_added_to_rs1) + info->target += X_SIMM (insn, 13); + else + info->target |= X_SIMM (insn, 13); + (*info->print_address_func) (info->target, info); + info->insn_type = dis_dref; + info->data_size = 4; /* FIXME!!! */ + } + } + } + + if (opcode->flags & (F_UNBR|F_CONDBR|F_JSR)) + { + /* FIXME -- check is_annulled flag. */ + if (opcode->flags & F_UNBR) + info->insn_type = dis_branch; + if (opcode->flags & F_CONDBR) + info->insn_type = dis_condbranch; + if (opcode->flags & F_JSR) + info->insn_type = dis_jsr; + if (opcode->flags & F_DELAYED) + info->branch_delay_insns = 1; + } + + return sizeof (buffer); + } + } + + info->insn_type = dis_noninsn; /* Mark as non-valid instruction. */ + (*info->fprintf_func) (stream, ".long %#08lx", insn); + return sizeof (buffer); +} diff --git a/disas/tci.c b/disas/tci.c new file mode 100644 index 0000000..a606b63 --- /dev/null +++ b/disas/tci.c @@ -0,0 +1,59 @@ +/* + * Tiny Code Interpreter for QEMU - disassembler + * + * Copyright (c) 2011 Stefan Weil + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "disas/bfd.h" +#include "tcg/tcg.h" + +/* Disassemble TCI bytecode. */ +int print_insn_tci(bfd_vma addr, disassemble_info *info) +{ + int length; + uint8_t byte; + int status; + TCGOpcode op; + + status = info->read_memory_func(addr, &byte, 1, info); + if (status != 0) { + info->memory_error_func(status, addr, info); + return -1; + } + op = byte; + + addr++; + status = info->read_memory_func(addr, &byte, 1, info); + if (status != 0) { + info->memory_error_func(status, addr, info); + return -1; + } + length = byte; + + if (op >= tcg_op_defs_max) { + info->fprintf_func(info->stream, "illegal opcode %d", op); + } else { + const TCGOpDef *def = &tcg_op_defs[op]; + int nb_oargs = def->nb_oargs; + int nb_iargs = def->nb_iargs; + int nb_cargs = def->nb_cargs; + /* TODO: Improve disassembler output. */ + info->fprintf_func(info->stream, "%s\to=%d i=%d c=%d", + def->name, nb_oargs, nb_iargs, nb_cargs); + } + + return length; +} diff --git a/dma-helpers.c b/dma-helpers.c new file mode 100644 index 0000000..4faec5d --- /dev/null +++ b/dma-helpers.c @@ -0,0 +1,269 @@ +/* + * DMA helper functions + * + * Copyright (c) 2009 Red Hat + * + * This work is licensed under the terms of the GNU General Public License + * (GNU GPL), version 2 or later. + */ + +#include "sysemu/block-backend.h" +#include "sysemu/dma.h" +#include "trace.h" +#include "qemu/thread.h" +#include "qemu/main-loop.h" + +/* #define DEBUG_IOMMU */ + +int dma_memory_set(AddressSpace *as, dma_addr_t addr, uint8_t c, dma_addr_t len) +{ + dma_barrier(as, DMA_DIRECTION_FROM_DEVICE); + +#define FILLBUF_SIZE 512 + uint8_t fillbuf[FILLBUF_SIZE]; + int l; + bool error = false; + + memset(fillbuf, c, FILLBUF_SIZE); + while (len > 0) { + l = len < FILLBUF_SIZE ? len : FILLBUF_SIZE; + error |= address_space_rw(as, addr, MEMTXATTRS_UNSPECIFIED, + fillbuf, l, true); + len -= l; + addr += l; + } + + return error; +} + +void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint, + AddressSpace *as) +{ + qsg->sg = g_malloc(alloc_hint * sizeof(ScatterGatherEntry)); + qsg->nsg = 0; + qsg->nalloc = alloc_hint; + qsg->size = 0; + qsg->as = as; + qsg->dev = dev; + object_ref(OBJECT(dev)); +} + +void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len) +{ + if (qsg->nsg == qsg->nalloc) { + qsg->nalloc = 2 * qsg->nalloc + 1; + qsg->sg = g_realloc(qsg->sg, qsg->nalloc * sizeof(ScatterGatherEntry)); + } + qsg->sg[qsg->nsg].base = base; + qsg->sg[qsg->nsg].len = len; + qsg->size += len; + ++qsg->nsg; +} + +void qemu_sglist_destroy(QEMUSGList *qsg) +{ + object_unref(OBJECT(qsg->dev)); + g_free(qsg->sg); + memset(qsg, 0, sizeof(*qsg)); +} + +typedef struct { + BlockAIOCB common; + BlockBackend *blk; + BlockAIOCB *acb; + QEMUSGList *sg; + uint64_t sector_num; + DMADirection dir; + int sg_cur_index; + dma_addr_t sg_cur_byte; + QEMUIOVector iov; + QEMUBH *bh; + DMAIOFunc *io_func; +} DMAAIOCB; + +static void dma_blk_cb(void *opaque, int ret); + +static void reschedule_dma(void *opaque) +{ + DMAAIOCB *dbs = (DMAAIOCB *)opaque; + + qemu_bh_delete(dbs->bh); + dbs->bh = NULL; + dma_blk_cb(dbs, 0); +} + +static void dma_blk_unmap(DMAAIOCB *dbs) +{ + int i; + + for (i = 0; i < dbs->iov.niov; ++i) { + dma_memory_unmap(dbs->sg->as, dbs->iov.iov[i].iov_base, + dbs->iov.iov[i].iov_len, dbs->dir, + dbs->iov.iov[i].iov_len); + } + qemu_iovec_reset(&dbs->iov); +} + +static void dma_complete(DMAAIOCB *dbs, int ret) +{ + trace_dma_complete(dbs, ret, dbs->common.cb); + + dma_blk_unmap(dbs); + if (dbs->common.cb) { + dbs->common.cb(dbs->common.opaque, ret); + } + qemu_iovec_destroy(&dbs->iov); + if (dbs->bh) { + qemu_bh_delete(dbs->bh); + dbs->bh = NULL; + } + qemu_aio_unref(dbs); +} + +static void dma_blk_cb(void *opaque, int ret) +{ + DMAAIOCB *dbs = (DMAAIOCB *)opaque; + dma_addr_t cur_addr, cur_len; + void *mem; + + trace_dma_blk_cb(dbs, ret); + + dbs->acb = NULL; + dbs->sector_num += dbs->iov.size / 512; + + if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) { + dma_complete(dbs, ret); + return; + } + dma_blk_unmap(dbs); + + while (dbs->sg_cur_index < dbs->sg->nsg) { + cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte; + cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte; + mem = dma_memory_map(dbs->sg->as, cur_addr, &cur_len, dbs->dir); + if (!mem) + break; + qemu_iovec_add(&dbs->iov, mem, cur_len); + dbs->sg_cur_byte += cur_len; + if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) { + dbs->sg_cur_byte = 0; + ++dbs->sg_cur_index; + } + } + + if (dbs->iov.size == 0) { + trace_dma_map_wait(dbs); + dbs->bh = aio_bh_new(blk_get_aio_context(dbs->blk), + reschedule_dma, dbs); + cpu_register_map_client(dbs->bh); + return; + } + + if (dbs->iov.size & ~BDRV_SECTOR_MASK) { + qemu_iovec_discard_back(&dbs->iov, dbs->iov.size & ~BDRV_SECTOR_MASK); + } + + dbs->acb = dbs->io_func(dbs->blk, dbs->sector_num, &dbs->iov, + dbs->iov.size / 512, dma_blk_cb, dbs); + assert(dbs->acb); +} + +static void dma_aio_cancel(BlockAIOCB *acb) +{ + DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common); + + trace_dma_aio_cancel(dbs); + + if (dbs->acb) { + blk_aio_cancel_async(dbs->acb); + } + if (dbs->bh) { + cpu_unregister_map_client(dbs->bh); + qemu_bh_delete(dbs->bh); + dbs->bh = NULL; + } +} + + +static const AIOCBInfo dma_aiocb_info = { + .aiocb_size = sizeof(DMAAIOCB), + .cancel_async = dma_aio_cancel, +}; + +BlockAIOCB *dma_blk_io( + BlockBackend *blk, QEMUSGList *sg, uint64_t sector_num, + DMAIOFunc *io_func, BlockCompletionFunc *cb, + void *opaque, DMADirection dir) +{ + DMAAIOCB *dbs = blk_aio_get(&dma_aiocb_info, blk, cb, opaque); + + trace_dma_blk_io(dbs, blk, sector_num, (dir == DMA_DIRECTION_TO_DEVICE)); + + dbs->acb = NULL; + dbs->blk = blk; + dbs->sg = sg; + dbs->sector_num = sector_num; + dbs->sg_cur_index = 0; + dbs->sg_cur_byte = 0; + dbs->dir = dir; + dbs->io_func = io_func; + dbs->bh = NULL; + qemu_iovec_init(&dbs->iov, sg->nsg); + dma_blk_cb(dbs, 0); + return &dbs->common; +} + + +BlockAIOCB *dma_blk_read(BlockBackend *blk, + QEMUSGList *sg, uint64_t sector, + void (*cb)(void *opaque, int ret), void *opaque) +{ + return dma_blk_io(blk, sg, sector, blk_aio_readv, cb, opaque, + DMA_DIRECTION_FROM_DEVICE); +} + +BlockAIOCB *dma_blk_write(BlockBackend *blk, + QEMUSGList *sg, uint64_t sector, + void (*cb)(void *opaque, int ret), void *opaque) +{ + return dma_blk_io(blk, sg, sector, blk_aio_writev, cb, opaque, + DMA_DIRECTION_TO_DEVICE); +} + + +static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg, + DMADirection dir) +{ + uint64_t resid; + int sg_cur_index; + + resid = sg->size; + sg_cur_index = 0; + len = MIN(len, resid); + while (len > 0) { + ScatterGatherEntry entry = sg->sg[sg_cur_index++]; + int32_t xfer = MIN(len, entry.len); + dma_memory_rw(sg->as, entry.base, ptr, xfer, dir); + ptr += xfer; + len -= xfer; + resid -= xfer; + } + + return resid; +} + +uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg) +{ + return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_FROM_DEVICE); +} + +uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg) +{ + return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_TO_DEVICE); +} + +void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie, + QEMUSGList *sg, enum BlockAcctType type) +{ + block_acct_start(blk_get_stats(blk), cookie, sg->size, type); +} diff --git a/docs/aio_notify.promela b/docs/aio_notify.promela new file mode 100644 index 0000000..ad3f6f0 --- /dev/null +++ b/docs/aio_notify.promela @@ -0,0 +1,104 @@ +/* + * This model describes the interaction between aio_set_dispatching() + * and aio_notify(). + * + * Author: Paolo Bonzini + * + * This file is in the public domain. If you really want a license, + * the WTFPL will do. + * + * To simulate it: + * spin -p docs/aio_notify.promela + * + * To verify it: + * spin -a docs/aio_notify.promela + * gcc -O2 pan.c + * ./a.out -a + */ + +#define MAX 4 +#define LAST (1 << (MAX - 1)) +#define FINAL ((LAST << 1) - 1) + +bool dispatching; +bool event; + +int req, done; + +active proctype waiter() +{ + int fetch, blocking; + + do + :: done != FINAL -> { + // Computing "blocking" is separate from execution of the + // "bottom half" + blocking = (req == 0); + + // This is our "bottom half" + atomic { fetch = req; req = 0; } + done = done | fetch; + + // Wait for a nudge from the other side + do + :: event == 1 -> { event = 0; break; } + :: !blocking -> break; + od; + + dispatching = 1; + + // If you are simulating this model, you may want to add + // something like this here: + // + // int foo; foo++; foo++; foo++; + // + // This only wastes some time and makes it more likely + // that the notifier process hits the "fast path". + + dispatching = 0; + } + :: else -> break; + od +} + +active proctype notifier() +{ + int next = 1; + int sets = 0; + + do + :: next <= LAST -> { + // generate a request + req = req | next; + next = next << 1; + + // aio_notify + if + :: dispatching == 0 -> sets++; event = 1; + :: else -> skip; + fi; + + // Test both synchronous and asynchronous delivery + if + :: 1 -> do + :: req == 0 -> break; + od; + :: 1 -> skip; + fi; + } + :: else -> break; + od; + printf("Skipped %d event_notifier_set\n", MAX - sets); +} + +#define p (done == FINAL) + +never { + do + :: 1 // after an arbitrarily long prefix + :: p -> break // p becomes true + od; + do + :: !p -> accept: break // it then must remains true forever after + od +} diff --git a/docs/atomics.txt b/docs/atomics.txt new file mode 100644 index 0000000..ef285e3 --- /dev/null +++ b/docs/atomics.txt @@ -0,0 +1,352 @@ +CPUs perform independent memory operations effectively in random order. +but this can be a problem for CPU-CPU interaction (including interactions +between QEMU and the guest). Multi-threaded programs use various tools +to instruct the compiler and the CPU to restrict the order to something +that is consistent with the expectations of the programmer. + +The most basic tool is locking. Mutexes, condition variables and +semaphores are used in QEMU, and should be the default approach to +synchronization. Anything else is considerably harder, but it's +also justified more often than one would like. The two tools that +are provided by qemu/atomic.h are memory barriers and atomic operations. + +Macros defined by qemu/atomic.h fall in three camps: + +- compiler barriers: barrier(); + +- weak atomic access and manual memory barriers: atomic_read(), + atomic_set(), smp_rmb(), smp_wmb(), smp_mb(), smp_read_barrier_depends(); + +- sequentially consistent atomic access: everything else. + + +COMPILER MEMORY BARRIER +======================= + +barrier() prevents the compiler from moving the memory accesses either +side of it to the other side. The compiler barrier has no direct effect +on the CPU, which may then reorder things however it wishes. + +barrier() is mostly used within qemu/atomic.h itself. On some +architectures, CPU guarantees are strong enough that blocking compiler +optimizations already ensures the correct order of execution. In this +case, qemu/atomic.h will reduce stronger memory barriers to simple +compiler barriers. + +Still, barrier() can be useful when writing code that can be interrupted +by signal handlers. + + +SEQUENTIALLY CONSISTENT ATOMIC ACCESS +===================================== + +Most of the operations in the qemu/atomic.h header ensure *sequential +consistency*, where "the result of any execution is the same as if the +operations of all the processors were executed in some sequential order, +and the operations of each individual processor appear in this sequence +in the order specified by its program". + +qemu/atomic.h provides the following set of atomic read-modify-write +operations: + + void atomic_inc(ptr) + void atomic_dec(ptr) + void atomic_add(ptr, val) + void atomic_sub(ptr, val) + void atomic_and(ptr, val) + void atomic_or(ptr, val) + + typeof(*ptr) atomic_fetch_inc(ptr) + typeof(*ptr) atomic_fetch_dec(ptr) + typeof(*ptr) atomic_fetch_add(ptr, val) + typeof(*ptr) atomic_fetch_sub(ptr, val) + typeof(*ptr) atomic_fetch_and(ptr, val) + typeof(*ptr) atomic_fetch_or(ptr, val) + typeof(*ptr) atomic_xchg(ptr, val + typeof(*ptr) atomic_cmpxchg(ptr, old, new) + +all of which return the old value of *ptr. These operations are +polymorphic; they operate on any type that is as wide as an int. + +Sequentially consistent loads and stores can be done using: + + atomic_fetch_add(ptr, 0) for loads + atomic_xchg(ptr, val) for stores + +However, they are quite expensive on some platforms, notably POWER and +ARM. Therefore, qemu/atomic.h provides two primitives with slightly +weaker constraints: + + typeof(*ptr) atomic_mb_read(ptr) + void atomic_mb_set(ptr, val) + +The semantics of these primitives map to Java volatile variables, +and are strongly related to memory barriers as used in the Linux +kernel (see below). + +As long as you use atomic_mb_read and atomic_mb_set, accesses cannot +be reordered with each other, and it is also not possible to reorder +"normal" accesses around them. + +However, and this is the important difference between +atomic_mb_read/atomic_mb_set and sequential consistency, it is important +for both threads to access the same volatile variable. It is not the +case that everything visible to thread A when it writes volatile field f +becomes visible to thread B after it reads volatile field g. The store +and load have to "match" (i.e., be performed on the same volatile +field) to achieve the right semantics. + + +These operations operate on any type that is as wide as an int or smaller. + + +WEAK ATOMIC ACCESS AND MANUAL MEMORY BARRIERS +============================================= + +Compared to sequentially consistent atomic access, programming with +weaker consistency models can be considerably more complicated. +In general, if the algorithm you are writing includes both writes +and reads on the same side, it is generally simpler to use sequentially +consistent primitives. + +When using this model, variables are accessed with atomic_read() and +atomic_set(), and restrictions to the ordering of accesses is enforced +using the smp_rmb(), smp_wmb(), smp_mb() and smp_read_barrier_depends() +memory barriers. + +atomic_read() and atomic_set() prevents the compiler from using +optimizations that might otherwise optimize accesses out of existence +on the one hand, or that might create unsolicited accesses on the other. +In general this should not have any effect, because the same compiler +barriers are already implied by memory barriers. However, it is useful +to do so, because it tells readers which variables are shared with +other threads, and which are local to the current thread or protected +by other, more mundane means. + +Memory barriers control the order of references to shared memory. +They come in four kinds: + +- smp_rmb() guarantees that all the LOAD operations specified before + the barrier will appear to happen before all the LOAD operations + specified after the barrier with respect to the other components of + the system. + + In other words, smp_rmb() puts a partial ordering on loads, but is not + required to have any effect on stores. + +- smp_wmb() guarantees that all the STORE operations specified before + the barrier will appear to happen before all the STORE operations + specified after the barrier with respect to the other components of + the system. + + In other words, smp_wmb() puts a partial ordering on stores, but is not + required to have any effect on loads. + +- smp_mb() guarantees that all the LOAD and STORE operations specified + before the barrier will appear to happen before all the LOAD and + STORE operations specified after the barrier with respect to the other + components of the system. + + smp_mb() puts a partial ordering on both loads and stores. It is + stronger than both a read and a write memory barrier; it implies both + smp_rmb() and smp_wmb(), but it also prevents STOREs coming before the + barrier from overtaking LOADs coming after the barrier and vice versa. + +- smp_read_barrier_depends() is a weaker kind of read barrier. On + most processors, whenever two loads are performed such that the + second depends on the result of the first (e.g., the first load + retrieves the address to which the second load will be directed), + the processor will guarantee that the first LOAD will appear to happen + before the second with respect to the other components of the system. + However, this is not always true---for example, it was not true on + Alpha processors. Whenever this kind of access happens to shared + memory (that is not protected by a lock), a read barrier is needed, + and smp_read_barrier_depends() can be used instead of smp_rmb(). + + Note that the first load really has to have a _data_ dependency and not + a control dependency. If the address for the second load is dependent + on the first load, but the dependency is through a conditional rather + than actually loading the address itself, then it's a _control_ + dependency and a full read barrier or better is required. + + +This is the set of barriers that is required *between* two atomic_read() +and atomic_set() operations to achieve sequential consistency: + + | 2nd operation | + |-----------------------------------------| + 1st operation | (after last) | atomic_read | atomic_set | + ---------------+--------------+-------------+------------| + (before first) | | none | smp_wmb() | + ---------------+--------------+-------------+------------| + atomic_read | smp_rmb() | smp_rmb()* | ** | + ---------------+--------------+-------------+------------| + atomic_set | none | smp_mb()*** | smp_wmb() | + ---------------+--------------+-------------+------------| + + * Or smp_read_barrier_depends(). + + ** This requires a load-store barrier. How to achieve this varies + depending on the machine, but in practice smp_rmb()+smp_wmb() + should have the desired effect. For example, on PowerPC the + lwsync instruction is a combined load-load, load-store and + store-store barrier. + + *** This requires a store-load barrier. On most machines, the only + way to achieve this is a full barrier. + + +You can see that the two possible definitions of atomic_mb_read() +and atomic_mb_set() are the following: + + 1) atomic_mb_read(p) = atomic_read(p); smp_rmb() + atomic_mb_set(p, v) = smp_wmb(); atomic_set(p, v); smp_mb() + + 2) atomic_mb_read(p) = smp_mb() atomic_read(p); smp_rmb() + atomic_mb_set(p, v) = smp_wmb(); atomic_set(p, v); + +Usually the former is used, because smp_mb() is expensive and a program +normally has more reads than writes. Therefore it makes more sense to +make atomic_mb_set() the more expensive operation. + +There are two common cases in which atomic_mb_read and atomic_mb_set +generate too many memory barriers, and thus it can be useful to manually +place barriers instead: + +- when a data structure has one thread that is always a writer + and one thread that is always a reader, manual placement of + memory barriers makes the write side faster. Furthermore, + correctness is easy to check for in this case using the "pairing" + trick that is explained below: + + thread 1 thread 1 + ------------------------- ------------------------ + (other writes) + smp_wmb() + atomic_mb_set(&a, x) atomic_set(&a, x) + smp_wmb() + atomic_mb_set(&b, y) atomic_set(&b, y) + + => + thread 2 thread 2 + ------------------------- ------------------------ + y = atomic_mb_read(&b) y = atomic_read(&b) + smp_rmb() + x = atomic_mb_read(&a) x = atomic_read(&a) + smp_rmb() + +- sometimes, a thread is accessing many variables that are otherwise + unrelated to each other (for example because, apart from the current + thread, exactly one other thread will read or write each of these + variables). In this case, it is possible to "hoist" the implicit + barriers provided by atomic_mb_read() and atomic_mb_set() outside + a loop. For example, the above definition atomic_mb_read() gives + the following transformation: + + n = 0; n = 0; + for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++) + n += atomic_mb_read(&a[i]); n += atomic_read(&a[i]); + smp_rmb(); + + Similarly, atomic_mb_set() can be transformed as follows: + smp_mb(): + + smp_wmb(); + for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++) + atomic_mb_set(&a[i], false); atomic_set(&a[i], false); + smp_mb(); + + +The two tricks can be combined. In this case, splitting a loop in +two lets you hoist the barriers out of the loops _and_ eliminate the +expensive smp_mb(): + + smp_wmb(); + for (i = 0; i < 10; i++) { => for (i = 0; i < 10; i++) + atomic_mb_set(&a[i], false); atomic_set(&a[i], false); + atomic_mb_set(&b[i], false); smb_wmb(); + } for (i = 0; i < 10; i++) + atomic_set(&a[i], false); + smp_mb(); + + The other thread can still use atomic_mb_read()/atomic_mb_set() + + +Memory barrier pairing +---------------------- + +A useful rule of thumb is that memory barriers should always, or almost +always, be paired with another barrier. In the case of QEMU, however, +note that the other barrier may actually be in a driver that runs in +the guest! + +For the purposes of pairing, smp_read_barrier_depends() and smp_rmb() +both count as read barriers. A read barrier shall pair with a write +barrier or a full barrier; a write barrier shall pair with a read +barrier or a full barrier. A full barrier can pair with anything. +For example: + + thread 1 thread 2 + =============== =============== + a = 1; + smp_wmb(); + b = 2; x = b; + smp_rmb(); + y = a; + +Note that the "writing" thread is accessing the variables in the +opposite order as the "reading" thread. This is expected: stores +before the write barrier will normally match the loads after the +read barrier, and vice versa. The same is true for more than 2 +access and for data dependency barriers: + + thread 1 thread 2 + =============== =============== + b[2] = 1; + smp_wmb(); + x->i = 2; + smp_wmb(); + a = x; x = a; + smp_read_barrier_depends(); + y = x->i; + smp_read_barrier_depends(); + z = b[y]; + +smp_wmb() also pairs with atomic_mb_read(), and smp_rmb() also pairs +with atomic_mb_set(). + + +COMPARISON WITH LINUX KERNEL MEMORY BARRIERS +============================================ + +Here is a list of differences between Linux kernel atomic operations +and memory barriers, and the equivalents in QEMU: + +- atomic operations in Linux are always on a 32-bit int type and + use a boxed atomic_t type; atomic operations in QEMU are polymorphic + and use normal C types. + +- atomic_read and atomic_set in Linux give no guarantee at all; + atomic_read and atomic_set in QEMU include a compiler barrier + (similar to the ACCESS_ONCE macro in Linux). + +- most atomic read-modify-write operations in Linux return void; + in QEMU, all of them return the old value of the variable. + +- different atomic read-modify-write operations in Linux imply + a different set of memory barriers; in QEMU, all of them enforce + sequential consistency, which means they imply full memory barriers + before and after the operation. + +- Linux does not have an equivalent of atomic_mb_read() and + atomic_mb_set(). In particular, note that set_mb() is a little + weaker than atomic_mb_set(). + + +SOURCES +======= + +* Documentation/memory-barriers.txt from the Linux kernel + +* "The JSR-133 Cookbook for Compiler Writers", available at + http://g.oswego.edu/dl/jmm/cookbook.html diff --git a/docs/bitmaps.md b/docs/bitmaps.md new file mode 100644 index 0000000..f066b48 --- /dev/null +++ b/docs/bitmaps.md @@ -0,0 +1,352 @@ + + +# Dirty Bitmaps and Incremental Backup + +* Dirty Bitmaps are objects that track which data needs to be backed up for the + next incremental backup. + +* Dirty bitmaps can be created at any time and attached to any node + (not just complete drives.) + +## Dirty Bitmap Names + +* A dirty bitmap's name is unique to the node, but bitmaps attached to different + nodes can share the same name. + +## Bitmap Modes + +* A Bitmap can be "frozen," which means that it is currently in-use by a backup + operation and cannot be deleted, renamed, written to, reset, + etc. + +## Basic QMP Usage + +### Supported Commands ### + +* block-dirty-bitmap-add +* block-dirty-bitmap-remove +* block-dirty-bitmap-clear + +### Creation + +* To create a new bitmap, enabled, on the drive with id=drive0: + +```json +{ "execute": "block-dirty-bitmap-add", + "arguments": { + "node": "drive0", + "name": "bitmap0" + } +} +``` + +* This bitmap will have a default granularity that matches the cluster size of + its associated drive, if available, clamped to between [4KiB, 64KiB]. + The current default for qcow2 is 64KiB. + +* To create a new bitmap that tracks changes in 32KiB segments: + +```json +{ "execute": "block-dirty-bitmap-add", + "arguments": { + "node": "drive0", + "name": "bitmap0", + "granularity": 32768 + } +} +``` + +### Deletion + +* Bitmaps that are frozen cannot be deleted. + +* Deleting the bitmap does not impact any other bitmaps attached to the same + node, nor does it affect any backups already created from this node. + +* Because bitmaps are only unique to the node to which they are attached, + you must specify the node/drive name here, too. + +```json +{ "execute": "block-dirty-bitmap-remove", + "arguments": { + "node": "drive0", + "name": "bitmap0" + } +} +``` + +### Resetting + +* Resetting a bitmap will clear all information it holds. + +* An incremental backup created from an empty bitmap will copy no data, + as if nothing has changed. + +```json +{ "execute": "block-dirty-bitmap-clear", + "arguments": { + "node": "drive0", + "name": "bitmap0" + } +} +``` + +## Transactions (Not yet implemented) + +* Transactional commands are forthcoming in a future version, + and are not yet available for use. This section serves as + documentation of intent for their design and usage. + +### Justification + +Bitmaps can be safely modified when the VM is paused or halted by using +the basic QMP commands. For instance, you might perform the following actions: + +1. Boot the VM in a paused state. +2. Create a full drive backup of drive0. +3. Create a new bitmap attached to drive0. +4. Resume execution of the VM. +5. Incremental backups are ready to be created. + +At this point, the bitmap and drive backup would be correctly in sync, +and incremental backups made from this point forward would be correctly aligned +to the full drive backup. + +This is not particularly useful if we decide we want to start incremental +backups after the VM has been running for a while, for which we will need to +perform actions such as the following: + +1. Boot the VM and begin execution. +2. Using a single transaction, perform the following operations: + * Create bitmap0. + * Create a full drive backup of drive0. +3. Incremental backups are now ready to be created. + +### Supported Bitmap Transactions + +* block-dirty-bitmap-add +* block-dirty-bitmap-clear + +The usages are identical to their respective QMP commands, but see below +for examples. + +### Example: New Incremental Backup + +As outlined in the justification, perhaps we want to create a new incremental +backup chain attached to a drive. + +```json +{ "execute": "transaction", + "arguments": { + "actions": [ + {"type": "block-dirty-bitmap-add", + "data": {"node": "drive0", "name": "bitmap0"} }, + {"type": "drive-backup", + "data": {"device": "drive0", "target": "/path/to/full_backup.img", + "sync": "full", "format": "qcow2"} } + ] + } +} +``` + +### Example: New Incremental Backup Anchor Point + +Maybe we just want to create a new full backup with an existing bitmap and +want to reset the bitmap to track the new chain. + +```json +{ "execute": "transaction", + "arguments": { + "actions": [ + {"type": "block-dirty-bitmap-clear", + "data": {"node": "drive0", "name": "bitmap0"} }, + {"type": "drive-backup", + "data": {"device": "drive0", "target": "/path/to/new_full_backup.img", + "sync": "full", "format": "qcow2"} } + ] + } +} +``` + +## Incremental Backups + +The star of the show. + +**Nota Bene!** Only incremental backups of entire drives are supported for now. +So despite the fact that you can attach a bitmap to any arbitrary node, they are +only currently useful when attached to the root node. This is because +drive-backup only supports drives/devices instead of arbitrary nodes. + +### Example: First Incremental Backup + +1. Create a full backup and sync it to the dirty bitmap, as in the transactional +examples above; or with the VM offline, manually create a full copy and then +create a new bitmap before the VM begins execution. + + * Let's assume the full backup is named 'full_backup.img'. + * Let's assume the bitmap you created is 'bitmap0' attached to 'drive0'. + +2. Create a destination image for the incremental backup that utilizes the +full backup as a backing image. + + * Let's assume it is named 'incremental.0.img'. + + ```sh + # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2 + ``` + +3. Issue the incremental backup command: + + ```json + { "execute": "drive-backup", + "arguments": { + "device": "drive0", + "bitmap": "bitmap0", + "target": "incremental.0.img", + "format": "qcow2", + "sync": "dirty-bitmap", + "mode": "existing" + } + } + ``` + +### Example: Second Incremental Backup + +1. Create a new destination image for the incremental backup that points to the + previous one, e.g.: 'incremental.1.img' + + ```sh + # qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2 + ``` + +2. Issue a new incremental backup command. The only difference here is that we + have changed the target image below. + + ```json + { "execute": "drive-backup", + "arguments": { + "device": "drive0", + "bitmap": "bitmap0", + "target": "incremental.1.img", + "format": "qcow2", + "sync": "dirty-bitmap", + "mode": "existing" + } + } + ``` + +## Errors + +* In the event of an error that occurs after a backup job is successfully + launched, either by a direct QMP command or a QMP transaction, the user + will receive a BLOCK_JOB_COMPLETE event with a failure message, accompanied + by a BLOCK_JOB_ERROR event. + +* In the case of an event being cancelled, the user will receive a + BLOCK_JOB_CANCELLED event instead of a pair of COMPLETE and ERROR events. + +* In either case, the incremental backup data contained within the bitmap is + safely rolled back, and the data within the bitmap is not lost. The image + file created for the failed attempt can be safely deleted. + +* Once the underlying problem is fixed (e.g. more storage space is freed up), + you can simply retry the incremental backup command with the same bitmap. + +### Example + +1. Create a target image: + + ```sh + # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2 + ``` + +2. Attempt to create an incremental backup via QMP: + + ```json + { "execute": "drive-backup", + "arguments": { + "device": "drive0", + "bitmap": "bitmap0", + "target": "incremental.0.img", + "format": "qcow2", + "sync": "dirty-bitmap", + "mode": "existing" + } + } + ``` + +3. Receive an event notifying us of failure: + + ```json + { "timestamp": { "seconds": 1424709442, "microseconds": 844524 }, + "data": { "speed": 0, "offset": 0, "len": 67108864, + "error": "No space left on device", + "device": "drive1", "type": "backup" }, + "event": "BLOCK_JOB_COMPLETED" } + ``` + +4. Delete the failed incremental, and re-create the image. + + ```sh + # rm incremental.0.img + # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2 + ``` + +5. Retry the command after fixing the underlying problem, + such as freeing up space on the backup volume: + + ```json + { "execute": "drive-backup", + "arguments": { + "device": "drive0", + "bitmap": "bitmap0", + "target": "incremental.0.img", + "format": "qcow2", + "sync": "dirty-bitmap", + "mode": "existing" + } + } + ``` + +6. Receive confirmation that the job completed successfully: + + ```json + { "timestamp": { "seconds": 1424709668, "microseconds": 526525 }, + "data": { "device": "drive1", "type": "backup", + "speed": 0, "len": 67108864, "offset": 67108864}, + "event": "BLOCK_JOB_COMPLETED" } + ``` + + diff --git a/docs/blkdebug.txt b/docs/blkdebug.txt new file mode 100644 index 0000000..b67a36d --- /dev/null +++ b/docs/blkdebug.txt @@ -0,0 +1,161 @@ +Block I/O error injection using blkdebug +---------------------------------------- +Copyright (C) 2014 Red Hat Inc + +This work is licensed under the terms of the GNU GPL, version 2 or later. See +the COPYING file in the top-level directory. + +The blkdebug block driver is a rule-based error injection engine. It can be +used to exercise error code paths in block drivers including ENOSPC (out of +space) and EIO. + +This document gives an overview of the features available in blkdebug. + +Background +---------- +Block drivers have many error code paths that handle I/O errors. Image formats +are especially complex since metadata I/O errors during cluster allocation or +while updating tables happen halfway through request processing and require +discipline to keep image files consistent. + +Error injection allows test cases to trigger I/O errors at specific points. +This way, all error paths can be tested to make sure they are correct. + +Rules +----- +The blkdebug block driver takes a list of "rules" that tell the error injection +engine when to fail an I/O request. + +Each I/O request is evaluated against the rules. If a rule matches the request +then its "action" is executed. + +Rules can be placed in a configuration file; the configuration file +follows the same .ini-like format used by QEMU's -readconfig option, and +each section of the file represents a rule. + +The following configuration file defines a single rule: + + $ cat blkdebug.conf + [inject-error] + event = "read_aio" + errno = "28" + +This rule fails all aio read requests with ENOSPC (28). Note that the errno +value depends on the host. On Linux, see +/usr/include/asm-generic/errno-base.h for errno values. + +Invoke QEMU as follows: + + $ qemu-system-x86_64 + -drive if=none,cache=none,file=blkdebug:blkdebug.conf:test.img,id=drive0 \ + -device virtio-blk-pci,drive=drive0,id=virtio-blk-pci0 + +Rules support the following attributes: + + event - which type of operation to match (e.g. read_aio, write_aio, + flush_to_os, flush_to_disk). See the "Events" section for + information on events. + + state - (optional) the engine must be in this state number in order for this + rule to match. See the "State transitions" section for information + on states. + + errno - the numeric errno value to return when a request matches this rule. + The errno values depend on the host since the numeric values are not + standarized in the POSIX specification. + + sector - (optional) a sector number that the request must overlap in order to + match this rule + + once - (optional, default "off") only execute this action on the first + matching request + + immediately - (optional, default "off") return a NULL BlockAIOCB + pointer and fail without an errno instead. This + exercises the code path where BlockAIOCB fails and the + caller's BlockCompletionFunc is not invoked. + +Events +------ +Block drivers provide information about the type of I/O request they are about +to make so rules can match specific types of requests. For example, the qcow2 +block driver tells blkdebug when it accesses the L1 table so rules can match +only L1 table accesses and not other metadata or guest data requests. + +The core events are: + + read_aio - guest data read + + write_aio - guest data write + + flush_to_os - write out unwritten block driver state (e.g. cached metadata) + + flush_to_disk - flush the host block device's disk cache + +See block/blkdebug.c:event_names[] for the full list of events. You may need +to grep block driver source code to understand the meaning of specific events. + +State transitions +----------------- +There are cases where more power is needed to match a particular I/O request in +a longer sequence of requests. For example: + + write_aio + flush_to_disk + write_aio + +How do we match the 2nd write_aio but not the first? This is where state +transitions come in. + +The error injection engine has an integer called the "state" that always starts +initialized to 1. The state integer is internal to blkdebug and cannot be +observed from outside but rules can interact with it for powerful matching +behavior. + +Rules can be conditional on the current state and they can transition to a new +state. + +When a rule's "state" attribute is non-zero then the current state must equal +the attribute in order for the rule to match. + +For example, to match the 2nd write_aio: + + [set-state] + event = "write_aio" + state = "1" + new_state = "2" + + [inject-error] + event = "write_aio" + state = "2" + errno = "5" + +The first write_aio request matches the set-state rule and transitions from +state 1 to state 2. Once state 2 has been entered, the set-state rule no +longer matches since it requires state 1. But the inject-error rule now +matches the next write_aio request and injects EIO (5). + +State transition rules support the following attributes: + + event - which type of operation to match (e.g. read_aio, write_aio, + flush_to_os, flush_to_disk). See the "Events" section for + information on events. + + state - (optional) the engine must be in this state number in order for this + rule to match + + new_state - transition to this state number + +Suspend and resume +------------------ +Exercising code paths in block drivers may require specific ordering amongst +concurrent requests. The "breakpoint" feature allows requests to be halted on +a blkdebug event and resumed later. This makes it possible to achieve +deterministic ordering when multiple requests are in flight. + +Breakpoints on blkdebug events are associated with a user-defined "tag" string. +This tag serves as an identifier by which the request can be resumed at a later +point. + +See the qemu-io(1) break, resume, remove_break, and wait_break commands for +details. diff --git a/docs/blkverify.txt b/docs/blkverify.txt new file mode 100644 index 0000000..d556dc4 --- /dev/null +++ b/docs/blkverify.txt @@ -0,0 +1,69 @@ += Block driver correctness testing with blkverify = + +== Introduction == + +This document describes how to use the blkverify protocol to test that a block +driver is operating correctly. + +It is difficult to test and debug block drivers against real guests. Often +processes inside the guest will crash because corrupt sectors were read as part +of the executable. Other times obscure errors are raised by a program inside +the guest. These issues are extremely hard to trace back to bugs in the block +driver. + +Blkverify solves this problem by catching data corruption inside QEMU the first +time bad data is read and reporting the disk sector that is corrupted. + +== How it works == + +The blkverify protocol has two child block devices, the "test" device and the +"raw" device. Read/write operations are mirrored to both devices so their +state should always be in sync. + +The "raw" device is a raw image, a flat file, that has identical starting +contents to the "test" image. The idea is that the "raw" device will handle +read/write operations correctly and not corrupt data. It can be used as a +reference for comparison against the "test" device. + +After a mirrored read operation completes, blkverify will compare the data and +raise an error if it is not identical. This makes it possible to catch the +first instance where corrupt data is read. + +== Example == + +Imagine raw.img has 0xcd repeated throughout its first sector: + + $ ./qemu-io -c 'read -v 0 512' raw.img + 00000000: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + 00000010: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + [...] + 000001e0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + 000001f0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + read 512/512 bytes at offset 0 + 512.000000 bytes, 1 ops; 0.0000 sec (97.656 MiB/sec and 200000.0000 ops/sec) + +And test.img is corrupt, its first sector is zeroed when it shouldn't be: + + $ ./qemu-io -c 'read -v 0 512' test.img + 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + [...] + 000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + read 512/512 bytes at offset 0 + 512.000000 bytes, 1 ops; 0.0000 sec (81.380 MiB/sec and 166666.6667 ops/sec) + +This error is caught by blkverify: + + $ ./qemu-io -c 'read 0 512' blkverify:a.img:b.img + blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0 + +A more realistic scenario is verifying the installation of a guest OS: + + $ ./qemu-img create raw.img 16G + $ ./qemu-img create -f qcow2 test.qcow2 16G + $ x86_64-softmmu/qemu-system-x86_64 -cdrom debian.iso \ + -drive file=blkverify:raw.img:test.qcow2 + +If the installation is aborted when blkverify detects corruption, use qemu-io +to explore the contents of the disk image at the sector in question. diff --git a/docs/bootindex.txt b/docs/bootindex.txt new file mode 100644 index 0000000..f84fac7 --- /dev/null +++ b/docs/bootindex.txt @@ -0,0 +1,43 @@ += Bootindex property = + +Block and net devices have bootindex property. This property is used to +determine the order in which firmware will consider devices for booting +the guest OS. If the bootindex property is not set for a device, it gets +lowest boot priority. There is no particular order in which devices with +unset bootindex property will be considered for booting, but they will +still be bootable. + +== Example == + +Let's assume we have a QEMU machine with two NICs (virtio, e1000) and two +disks (IDE, virtio): + +qemu -drive file=disk1.img,if=none,id=disk1 + -device ide-drive,drive=disk1,bootindex=4 + -drive file=disk2.img,if=none,id=disk2 + -device virtio-blk-pci,drive=disk2,bootindex=3 + -netdev type=user,id=net0 -device virtio-net-pci,netdev=net0,bootindex=2 + -netdev type=user,id=net1 -device e1000,netdev=net1,bootindex=1 + +Given the command above, firmware should try to boot from the e1000 NIC +first. If this fails, it should try the virtio NIC next; if this fails +too, it should try the virtio disk, and then the IDE disk. + +== Limitations == + +1. Some firmware has limitations on which devices can be considered for +booting. For instance, the PC BIOS boot specification allows only one +disk to be bootable. If boot from disk fails for some reason, the BIOS +won't retry booting from other disk. It can still try to boot from +floppy or net, though. + +2. Sometimes, firmware cannot map the device path QEMU wants firmware to +boot from to a boot method. It doesn't happen for devices the firmware +can natively boot from, but if firmware relies on an option ROM for +booting, and the same option ROM is used for booting from more then one +device, the firmware may not be able to ask the option ROM to boot from +a particular device reliably. For instance with the PC BIOS, if a SCSI HBA +has three bootable devices target1, target3, target5 connected to it, +the option ROM will have a boot method for each of them, but it is not +possible to map from boot method back to a specific target. This is a +shortcoming of the PC BIOS boot specification. diff --git a/docs/ccid.txt b/docs/ccid.txt new file mode 100644 index 0000000..c7fda6d --- /dev/null +++ b/docs/ccid.txt @@ -0,0 +1,181 @@ +QEMU CCID Device Documentation. + +Contents +1. USB CCID device +2. Building +3. Using ccid-card-emulated with hardware +4. Using ccid-card-emulated with certificates +5. Using ccid-card-passthru with client side hardware +6. Using ccid-card-passthru with client side certificates +7. Passthrough protocol scenario +8. libcacard + +1. USB CCID device + +The USB CCID device is a USB device implementing the CCID specification, which +lets one connect smart card readers that implement the same spec. For more +information see the specification: + + Universal Serial Bus + Device Class: Smart Card + CCID + Specification for + Integrated Circuit(s) Cards Interface Devices + Revision 1.1 + April 22rd, 2005 + +Smartcards are used for authentication, single sign on, decryption in +public/private schemes and digital signatures. A smartcard reader on the client +cannot be used on a guest with simple usb passthrough since it will then not be +available on the client, possibly locking the computer when it is "removed". On +the other hand this device can let you use the smartcard on both the client and +the guest machine. It is also possible to have a completely virtual smart card +reader and smart card (i.e. not backed by a physical device) using this device. + +2. Building + +The cryptographic functions and access to the physical card is done via NSS. + +Installing NSS: + +In redhat/fedora: + yum install nss-devel +In ubuntu/debian: + apt-get install libnss3-dev + (not tested on ubuntu) + +Configuring and building: + ./configure --enable-smartcard && make + + +3. Using ccid-card-emulated with hardware + +Assuming you have a working smartcard on the host with the current +user, using NSS, qemu acts as another NSS client using ccid-card-emulated: + + qemu -usb -device usb-ccid -device ccid-card-emulated + + +4. Using ccid-card-emulated with certificates stored in files + +You must create the CA and card certificates. This is a one time process. +We use NSS certificates: + + mkdir fake-smartcard + cd fake-smartcard + certutil -N -d sql:$PWD + certutil -S -d sql:$PWD -s "CN=Fake Smart Card CA" -x -t TC,TC,TC -n fake-smartcard-ca + certutil -S -d sql:$PWD -t ,, -s "CN=John Doe" -n id-cert -c fake-smartcard-ca + certutil -S -d sql:$PWD -t ,, -s "CN=John Doe (signing)" --nsCertType smime -n signing-cert -c fake-smartcard-ca + certutil -S -d sql:$PWD -t ,, -s "CN=John Doe (encryption)" --nsCertType sslClient -n encryption-cert -c fake-smartcard-ca + +Note: you must have exactly three certificates. + +You can use the emulated card type with the certificates backend: + + qemu -usb -device usb-ccid -device ccid-card-emulated,backend=certificates,db=sql:$PWD,cert1=id-cert,cert2=signing-cert,cert3=encryption-cert + +To use the certificates in the guest, export the CA certificate: + + certutil -L -r -d sql:$PWD -o fake-smartcard-ca.cer -n fake-smartcard-ca + +and import it in the guest: + + certutil -A -d /etc/pki/nssdb -i fake-smartcard-ca.cer -t TC,TC,TC -n fake-smartcard-ca + +In a Linux guest you can then use the CoolKey PKCS #11 module to access +the card: + + certutil -d /etc/pki/nssdb -L -h all + +It will prompt you for the PIN (which is the password you assigned to the +certificate database early on), and then show you all three certificates +together with the manually imported CA cert: + + Certificate Nickname Trust Attributes + fake-smartcard-ca CT,C,C + John Doe:CAC ID Certificate u,u,u + John Doe:CAC Email Signature Certificate u,u,u + John Doe:CAC Email Encryption Certificate u,u,u + +If this does not happen, CoolKey is not installed or not registered with +NSS. Registration can be done from Firefox or the command line: + + modutil -dbdir /etc/pki/nssdb -add "CAC Module" -libfile /usr/lib64/pkcs11/libcoolkeypk11.so + modutil -dbdir /etc/pki/nssdb -list + + +5. Using ccid-card-passthru with client side hardware + +on the host specify the ccid-card-passthru device with a suitable chardev: + + qemu -chardev socket,server,host=0.0.0.0,port=2001,id=ccid,nowait -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid + +on the client run vscclient, built when you built QEMU: + + vscclient 2001 + + +6. Using ccid-card-passthru with client side certificates + +This case is not particularly useful, but you can use it to debug +your setup if #4 works but #5 does not. + +Follow instructions as per #4, except run QEMU and vscclient as follows: +Run qemu as per #5, and run vscclient from the "fake-smartcard" +directory as follows: + + qemu -chardev socket,server,host=0.0.0.0,port=2001,id=ccid,nowait -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid + vscclient -e "db=\"sql:$PWD\" use_hw=no soft=(,Test,CAC,,id-cert,signing-cert,encryption-cert)" 2001 + + +7. Passthrough protocol scenario + +This is a typical interchange of messages when using the passthru card device. +usb-ccid is a usb device. It defaults to an unattached usb device on startup. +usb-ccid expects a chardev and expects the protocol defined in +cac_card/vscard_common.h to be passed over that. +The usb-ccid device can be in one of three modes: + * detached + * attached with no card + * attached with card + +A typical interchange is: (the arrow shows who started each exchange, it can be client +originated or guest originated) + +client event | vscclient | passthru | usb-ccid | guest event +---------------------------------------------------------------------------------------------- + | VSC_Init | | | + | VSC_ReaderAdd | | attach | + | | | | sees new usb device. +card inserted -> | | | | + | VSC_ATR | insert | insert | see new card + | | | | + | VSC_APDU | VSC_APDU | | <- guest sends APDU +client<->physical | | | | +card APDU exchange| | | | +client response ->| VSC_APDU | VSC_APDU | | receive APDU response + ... + [APDU<->APDU repeats several times] + ... +card removed -> | | | | + | VSC_CardRemove | remove | remove | card removed + ... + [(card insert, apdu's, card remove) repeat] + ... +kill/quit | | | | + vscclient | | | | + | VSC_ReaderRemove | | detach | + | | | | usb device removed. + + +8. libcacard + +Both ccid-card-emulated and vscclient use libcacard as the card emulator. +libcacard implements a completely virtual CAC (DoD standard for smart +cards) compliant card and uses NSS to retrieve certificates and do +any encryption. The backend can then be a real reader and card, or +certificates stored in files. + +For documentation of the library see docs/libcacard.txt. + diff --git a/docs/ich9-ehci-uhci.cfg b/docs/ich9-ehci-uhci.cfg new file mode 100644 index 0000000..a0e9b96 --- /dev/null +++ b/docs/ich9-ehci-uhci.cfg @@ -0,0 +1,37 @@ +########################################################################### +# +# You can pass this file directly to qemu using the -readconfig +# command line switch. +# +# This config file creates a EHCI adapter with companion UHCI +# controllers as multifunction device in PCI slot "1d". +# +# Specify "bus=ehci.0" when creating usb devices to hook them up +# there. +# + +[device "ehci"] + driver = "ich9-usb-ehci1" + addr = "1d.7" + multifunction = "on" + +[device "uhci-1"] + driver = "ich9-usb-uhci1" + addr = "1d.0" + multifunction = "on" + masterbus = "ehci.0" + firstport = "0" + +[device "uhci-2"] + driver = "ich9-usb-uhci2" + addr = "1d.1" + multifunction = "on" + masterbus = "ehci.0" + firstport = "2" + +[device "uhci-3"] + driver = "ich9-usb-uhci3" + addr = "1d.2" + multifunction = "on" + masterbus = "ehci.0" + firstport = "4" diff --git a/docs/image-fuzzer.txt b/docs/image-fuzzer.txt new file mode 100644 index 0000000..3e23ebe --- /dev/null +++ b/docs/image-fuzzer.txt @@ -0,0 +1,239 @@ +# Specification for the fuzz testing tool +# +# Copyright (C) 2014 Maria Kustova +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +Image fuzzer +============ + +Description +----------- + +The goal of the image fuzzer is to catch crashes of qemu-io/qemu-img +by providing to them randomly corrupted images. +Test images are generated from scratch and have valid inner structure with some +elements, e.g. L1/L2 tables, having random invalid values. + + +Test runner +----------- + +The test runner generates test images, executes tests utilizing generated +images, indicates their results and collects all test related artifacts (logs, +core dumps, test images, backing files). +The test means execution of all available commands under test with the same +generated test image. +By default, the test runner generates new tests and executes them until +keyboard interruption. But if a test seed is specified via the '--seed' runner +parameter, then only one test with this seed will be executed, after its finish +the runner will exit. + +The runner uses an external image fuzzer to generate test images. An image +generator should be specified as a mandatory parameter of the test runner. +Details about interactions between the runner and fuzzers see "Module +interfaces". + +The runner activates generation of core dumps during test executions, but it +assumes that core dumps will be generated in the current working directory. +For comprehensive test results, please, set up your test environment +properly. + +Paths to binaries under test (SUTs) qemu-img and qemu-io are retrieved from +environment variables. If the environment check fails the runner will +use SUTs installed in system paths. +qemu-img is required for creation of backing files, so it's mandatory to set +the related environment variable if it's not installed in the system path. +For details about environment variables see qemu-iotests/check. + +The runner accepts a JSON array of fields expected to be fuzzed via the +'--config' argument, e.g. + + '[["feature_name_table"], ["header", "l1_table_offset"]]' + +Each sublist can have one or two strings defining image structure elements. +In the latter case a parent element should be placed on the first position, +and a field name on the second one. + +The runner accepts a list of commands under test as a JSON array via +the '--command' argument. Each command is a list containing a SUT and all its +arguments, e.g. + + runner.py -c '[["qemu-io", "$test_img", "-c", "write $off $len"]]' + /tmp/test ../qcow2 + +For variable arguments next aliases can be used: + - $test_img for a fuzzed img + - $off for an offset in the fuzzed image + - $len for a data size + +Values for last two aliases will be generated based on a size of a virtual +disk of the generated image. +In case when no commands are specified the runner will execute commands from +the default list: + - qemu-img check + - qemu-img info + - qemu-img convert + - qemu-io -c read + - qemu-io -c write + - qemu-io -c aio_read + - qemu-io -c aio_write + - qemu-io -c flush + - qemu-io -c discard + - qemu-io -c truncate + + +Qcow2 image generator +--------------------- + +The 'qcow2' generator is a Python package providing 'create_image' method as +a single public API. See details in 'Test runner/image fuzzer' chapter of +'Module interfaces'. + +Qcow2 contains two submodules: fuzz.py and layout.py. + +'fuzz.py' contains all fuzzing functions, one per image field. It's assumed +that after code analysis every field will have own constraints for its value. +For now only universal potentially dangerous values are used, e.g. type limits +for integers or unsafe symbols as '%s' for strings. For bitmasks random amount +of bits are set to ones. All fuzzed values are checked on non-equality to the +current valid value of the field. In case of equality the value will be +regenerated. + +'layout.py' creates a random valid image, fuzzes a random subset of the image +fields by 'fuzz.py' module and writes a fuzzed image to the file specified. +If a fuzzer configuration is specified, then it has the next interpretation: + + 1. If a list contains a parent image element only, then some random portion + of fields of this element will be fuzzed every test. + The same behavior is applied for the entire image if no configuration is + used. This case is useful for the test specialization. + + 2. If a list contains a parent element and a field name, then a field + will be always fuzzed for every test. This case is useful for regression + testing. + +The generator can create header fields, header extensions, L1/L2 tables and +refcount table and blocks. + +Module interfaces +----------------- + +* Test runner/image fuzzer + +The runner calls an image generator specifying the path to a test image file, +path to a backing file and its format and a fuzzer configuration. +An image generator is expected to provide a + + 'create_image(test_img_path, backing_file_path=None, + backing_file_format=None, fuzz_config=None)' + +method that creates a test image, writes it to the specified file and returns +the size of the virtual disk. +The file should be created if it doesn't exist or overwritten otherwise. +fuzz_config has a form of a list of lists. Every sublist can have one +or two elements: first element is a name of a parent image element, second one +if exists is a name of a field in this element. +Example, + [['header', 'l1_table_offset'], + ['header', 'nb_snapshots'], + ['feature_name_table']] + +Random seed is set by the runner at every test execution for the regression +purpose, so an image generator is not recommended to modify it internally. + + +Overall fuzzer requirements +=========================== + +Input data: +---------- + + - image template (generator) + - work directory + - action vector (optional) + - seed (optional) + - SUT and its arguments (optional) + + +Fuzzer requirements: +------------------- + +1. Should be able to inject random data +2. Should be able to select a random value from the manually pregenerated + vector (boundary values, e.g. max/min cluster size) +3. Image template should describe a general structure invariant for all + test images (image format description) +4. Image template should be autonomous and other fuzzer parts should not + rely on it +5. Image template should contain reference rules (not only block+size + description) +6. Should generate the test image with the correct structure based on an image + template +7. Should accept a seed as an argument (for regression purpose) +8. Should generate a seed if it is not specified as an input parameter. +9. The same seed should generate the same image for the same action vector, + specified or generated. +10. Should accept a vector of actions as an argument (for test reproducing and + for test case specification, e.g. group of tests for header structure, + group of test for snapshots, etc) +11. Action vector should be randomly generated from the pool of available + actions, if it is not specified as an input parameter +12. Pool of actions should be defined automatically based on an image template +13. Should accept a SUT and its call parameters as an argument or select them + randomly otherwise. As far as it's expected to be rarely changed, the list + of all possible test commands can be available in the test runner + internally. +14. Should support an external cancellation of a test run +15. Seed should be logged (for regression purpose) +16. All files related to a test result should be collected: a test image, + SUT logs, fuzzer logs and crash dumps +17. Should be compatible with python version 2.4-2.7 +18. Usage of external libraries should be limited as much as possible. + + +Image formats: +------------- + +Main target image format is qcow2, but support of image templates should +provide an ability to add any other image format. + + +Effectiveness: +------------- + +The fuzzer can be controlled via template, seed and action vector; +it makes the fuzzer itself invariant to an image format and test logic. +It should be able to perform rather complex and precise tests, that can be +specified via an action vector. Otherwise, knowledge about an image structure +allows the fuzzer to generate the pool of all available areas can be fuzzed +and randomly select some of them and so compose its own action vector. +Also complexity of a template defines complexity of the fuzzer, so its +functionality can be varied from simple model-independent fuzzing to smart +model-based one. + + +Glossary: +-------- + +Action vector is a sequence of structure elements retrieved from an image +format, each of them will be fuzzed for the test image. It's a subset of +elements of the action pool. Example: header, refcount table, etc. +Action pool is all available elements of an image structure that generated +automatically from an image template. +Image template is a formal description of an image structure and relations +between image blocks. +Test image is an output image of the fuzzer defined by the current seed and +action vector. diff --git a/docs/libcacard.txt b/docs/libcacard.txt new file mode 100644 index 0000000..8db421d --- /dev/null +++ b/docs/libcacard.txt @@ -0,0 +1,483 @@ +This file documents the CAC (Common Access Card) library in the libcacard +subdirectory. + +Virtual Smart Card Emulator + +This emulator is designed to provide emulation of actual smart cards to a +virtual card reader running in a guest virtual machine. The emulated smart +cards can be representations of real smart cards, where the necessary functions +such as signing, card removal/insertion, etc. are mapped to real, physical +cards which are shared with the client machine the emulator is running on, or +the cards could be pure software constructs. + +The emulator is structured to allow multiple replaceable or additional pieces, +so it can be easily modified for future requirements. The primary envisioned +modifications are: + +1) The socket connection to the virtual card reader (presumably a CCID reader, +but other ISO-7816 compatible readers could be used). The code that handles +this is in vscclient.c. + +2) The virtual card low level emulation. This is currently supplied by using +NSS. This emulation could be replaced by implementations based on other +security libraries, including but not limitted to openssl+pkcs#11 library, +raw pkcs#11, Microsoft CAPI, direct opensc calls, etc. The code that handles +this is in vcard_emul_nss.c. + +3) Emulation for new types of cards. The current implementation emulates the +original DoD CAC standard with separate pki containers. This emulator lives in +cac.c. More than one card type emulator could be included. Other cards could +be emulated as well, including PIV, newer versions of CAC, PKCS #15, etc. + +-------------------- +Replacing the Socket Based Virtual Reader Interface. + +The current implementation contains a replaceable module vscclient.c. The +current vscclient.c implements a sockets interface to the virtual ccid reader +on the guest. CCID commands that are pertinent to emulation are passed +across the socket, and their responses are passed back along that same socket. +The protocol that vscclient uses is defined in vscard_common.h and connects +to a qemu ccid usb device. Since this socket runs as a client, vscclient.c +implements a program with a main entry. It also handles argument parsing for +the emulator. + +An application that wants to use the virtual reader can replace vscclient.c +with its own implementation that connects to its own CCID reader. The calls +that the CCID reader can call are: + + VReaderList * vreader_get_reader_list(); + + This function returns a list of virtual readers. These readers may map to + physical devices, or simulated devices depending on vcard the back end. Each + reader in the list should represent a reader to the virtual machine. Virtual + USB address mapping is left to the CCID reader front end. This call can be + made any time to get an updated list. The returned list is a copy of the + internal list that can be referenced by the caller without locking. This copy + must be freed by the caller with vreader_list_delete when it is no longer + needed. + + VReaderListEntry *vreader_list_get_first(VReaderList *); + + This function gets the first entry on the reader list. Along with + vreader_list_get_next(), vreader_list_get_first() can be used to walk the + reader list returned from vreader_get_reader_list(). VReaderListEntries are + part of the list themselves and do not need to be freed separately from the + list. If there are no entries on the list, it will return NULL. + + VReaderListEntry *vreader_list_get_next(VReaderListEntry *); + + This function gets the next entry in the list. If there are no more entries + it will return NULL. + + VReader * vreader_list_get_reader(VReaderListEntry *) + + This function returns the reader stored in the reader List entry. Caller gets + a new reference to a reader. The caller must free its reference when it is + finished with vreader_free(). + + void vreader_free(VReader *reader); + + This function frees a reference to a reader. Readers are reference counted + and are automatically deleted when the last reference is freed. + + void vreader_list_delete(VReaderList *list); + + This function frees the list, all the elements on the list, and all the + reader references held by the list. + + VReaderStatus vreader_power_on(VReader *reader, char *atr, int *len); + + This function simulates a card power on. A virtual card does not care about + the actual voltage and other physical parameters, but it does care that the + card is actually on or off. Cycling the card causes the card to reset. If + the caller provides enough space, vreader_power_on will return the ATR of + the virtual card. The amount of space provided in atr should be indicated + in *len. The function modifies *len to be the actual length of of the + returned ATR. + + VReaderStatus vreader_power_off(VReader *reader); + + This function simulates a power off of a virtual card. + + VReaderStatus vreader_xfer_bytes(VReader *reader, unsigne char *send_buf, + int send_buf_len, + unsigned char *receive_buf, + int receive_buf_len); + + This function sends a raw apdu to a card and returns the card's response. + The CCID front end should return the response back. Most of the emulation + is driven from these APDUs. + + VReaderStatus vreader_card_is_present(VReader *reader); + + This function returns whether or not the reader has a card inserted. The + vreader_power_on, vreader_power_off, and vreader_xfer_bytes will return + VREADER_NO_CARD. + + const char *vreader_get_name(VReader *reader); + + This function returns the name of the reader. The name comes from the card + emulator level and is usually related to the name of the physical reader. + + VReaderID vreader_get_id(VReader *reader); + + This function returns the id of a reader. All readers start out with an id + of -1. The application can set the id with vreader_set_id. + + VReaderStatus vreader_get_id(VReader *reader, VReaderID id); + + This function sets the reader id. The application is responsible for making + sure that the id is unique for all readers it is actively using. + + VReader *vreader_find_reader_by_id(VReaderID id); + + This function returns the reader which matches the id. If two readers match, + only one is returned. The function returns NULL if the id is -1. + + Event *vevent_wait_next_vevent(); + + This function blocks waiting for reader and card insertion events. There + will be one event for each card insertion, each card removal, each reader + insertion and each reader removal. At start up, events are created for all + the initial readers found, as well as all the cards that are inserted. + + Event *vevent_get_next_vevent(); + + This function returns a pending event if it exists, otherwise it returns + NULL. It does not block. + +---------------- +Card Type Emulator: Adding a New Virtual Card Type + +The ISO 7816 card spec describes 2 types of cards: + 1) File system cards, where the smartcard is managed by reading and writing +data to files in a file system. There is currently only boiler plate +implemented for file system cards. + 2) VM cards, where the card has loadable applets which perform the card +functions. The current implementation supports VM cards. + +In the case of VM cards, the difference between various types of cards is +really what applets have been installed in that card. This structure is +mirrored in card type emulators. The 7816 emulator already handles the basic +ISO 7186 commands. Card type emulators simply need to add the virtual applets +which emulate the real card applets. Card type emulators have exactly one +public entry point: + + VCARDStatus xxx_card_init(VCard *card, const char *flags, + const unsigned char *cert[], + int cert_len[], + VCardKey *key[], + int cert_count); + + The parameters for this are: + card - the virtual card structure which will represent this card. + flags - option flags that may be specific to this card type. + cert - array of binary certificates. + cert_len - array of lengths of each of the certificates specified in cert. + key - array of opaque key structures representing the private keys on + the card. + cert_count - number of entries in cert, cert_len, and key arrays. + + Any cert, cert_len, or key with the same index are matching sets. That is + cert[0] is cert_len[0] long and has the corresponding private key of key[0]. + +The card type emulator is expected to own the VCardKeys, but it should copy +any raw cert data it wants to save. It can create new applets and add them to +the card using the following functions: + + VCardApplet *vcard_new_applet(VCardProcessAPDU apdu_func, + VCardResetApplet reset_func, + const unsigned char *aid, + int aid_len); + + This function creates a new applet. Applet structures store the following + information: + 1) the AID of the applet (set by aid and aid_len). + 2) a function to handle APDUs for this applet. (set by apdu_func, more on + this below). + 3) a function to reset the applet state when the applet is selected. + (set by reset_func, more on this below). + 3) applet private data, a data pointer used by the card type emulator to + store any data or state it needs to complete requests. (set by a + separate call). + 4) applet private data free, a function used to free the applet private + data when the applet itself is destroyed. + The created applet can be added to the card with vcard_add_applet below. + + void vcard_set_applet_private(VCardApplet *applet, + VCardAppletPrivate *private, + VCardAppletPrivateFree private_free); + This function sets the private data and the corresponding free function. + VCardAppletPrivate is an opaque data structure to the rest of the emulator. + The card type emulator can define it any way it wants by defining + struct VCardAppletPrivateStruct {};. If there is already a private data + structure on the applet, the old one is freed before the new one is set up. + passing two NULL clear any existing private data. + + VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet); + + Add an applet onto the list of applets attached to the card. Once an applet + has been added, it can be selected by its AID, and then commands will be + routed to it VCardProcessAPDU function. This function adopts the applet that + is passed into it. Note: 2 applets with the same AID should not be added to + the same card. It is permissible to add more than one applet. Multiple applets + may have the same VCardPRocessAPDU entry point. + +The certs and keys should be attached to private data associated with one or +more appropriate applets for that card. Control will come to the card type +emulators once one of its applets are selected through the VCardProcessAPDU +function it specified when it created the applet. + +The signature of VCardResetApplet is: + VCardStatus (*VCardResetApplet) (VCard *card, int channel); + This function will reset the any internal applet state that needs to be + cleared after a select applet call. It should return VCARD_DONE; + +The signature of VCardProcessAPDU is: + VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu, + VCardResponse **response); + This function examines the APDU and determines whether it should process + the apdu directly, reject the apdu as invalid, or pass the apdu on to + the basic 7816 emulator for processing. + If the 7816 emulator should process the apdu, then the VCardProcessAPDU + should return VCARD_NEXT. + If there is an error, then VCardProcessAPDU should return an error + response using vcard_make_response and the appropriate 7816 error code + (see card_7816t.h) or vcard_make_response with a card type specific error + code. It should then return VCARD_DONE. + If the apdu can be processed correctly, VCardProcessAPDU should do so, + set the response value appropriately for that APDU, and return VCARD_DONE. + VCardProcessAPDU should always set the response if it returns VCARD_DONE. + It should always either return VCARD_DONE or VCARD_NEXT. + +Parsing the APDU -- + +Prior to processing calling the card type emulator's VCardProcessAPDU function, the emulator has already decoded the APDU header and set several fields: + + apdu->a_data - The raw apdu data bytes. + apdu->a_len - The len of the raw apdu data. + apdu->a_body - The start of any post header parameter data. + apdu->a_Lc - The parameter length value. + apdu->a_Le - The expected length of any returned data. + apdu->a_cla - The raw apdu class. + apdu->a_channel - The channel (decoded from the class). + apdu->a_secure_messaging_type - The decoded secure messaging type + (from class). + apdu->a_type - The decode class type. + apdu->a_gen_type - the generic class type (7816, PROPRIETARY, RFU, PTS). + apdu->a_ins - The instruction byte. + apdu->a_p1 - Parameter 1. + apdu->a_p2 - Parameter 2. + +Creating a Response -- + +The expected result of any APDU call is a response. The card type emulator must +set *response with an appropriate VCardResponse value if it returns VCARD_DONE. +Responses could be as simple as returning a 2 byte status word response, to as +complex as returning a block of data along with a 2 byte response. Which is +returned will depend on the semantics of the APDU. The following functions will +create card responses. + + VCardResponse *vcard_make_response(VCard7816Status status); + + This is the most basic function to get a response. This function will + return a response the consists solely one 2 byte status code. If that status + code is defined in card_7816t.h, then this function is guaranteed to + return a response with that status. If a cart type specific status code + is passed and vcard_make_response fails to allocate the appropriate memory + for that response, then vcard_make_response will return a VCardResponse + of VCARD7816_STATUS_EXC_ERROR_MEMORY. In any case, this function is + guaranteed to return a valid VCardResponse. + + VCardResponse *vcard_response_new(unsigned char *buf, int len, + VCard7816Status status); + + This function is similar to vcard_make_response except it includes some + returned data with the response. It could also fail to allocate enough + memory, in which case it will return NULL. + + VCardResponse *vcard_response_new_status_bytes(unsigned char sw1, + unsigned char sw2); + + Sometimes in 7816 the response bytes are treated as two separate bytes with + split meanings. This function allows you to create a response based on + two separate bytes. This function could fail, in which case it will return + NULL. + + VCardResponse *vcard_response_new_bytes(unsigned char *buf, int len, + unsigned char sw1, + unsigned char sw2); + + This function is the same as vcard_response_new except you may specify + the status as two separate bytes like vcard_response_new_status_bytes. + + +Implementing functionality --- + +The following helper functions access information about the current card +and applet. + + VCARDAppletPrivate *vcard_get_current_applet_private(VCard *card, + int channel); + + This function returns any private data set by the card type emulator on + the currently selected applet. The card type emulator keeps track of the + current applet state in this data structure. Any certs and keys associated + with a particular applet is also stored here. + + int vcard_emul_get_login_count(VCard *card); + + This function returns the the number of remaining login attempts for this + card. If the card emulator does not know, or the card does not have a + way of giving this information, this function returns -1. + + + VCard7816Status vcard_emul_login(VCard *card, unsigned char *pin, + int pin_len); + + This function logs into the card and returns the standard 7816 status + word depending on the success or failure of the call. + + void vcard_emul_delete_key(VCardKey *key); + + This function frees the VCardKey passed in to xxxx_card_init. The card + type emulator is responsible for freeing this key when it no longer needs + it. + + VCard7816Status vcard_emul_rsa_op(VCard *card, VCardKey *key, + unsigned char *buffer, + int buffer_size); + + This function does a raw rsa op on the buffer with the given key. + +The sample card type emulator is found in cac.c. It implements the cac specific +applets. Only those applets needed by the coolkey pkcs#11 driver on the guest +have been implemented. To support the full range CAC middleware, a complete CAC +card according to the CAC specs should be implemented here. + +------------------------------ +Virtual Card Emulator + +This code accesses both real smart cards and simulated smart cards through +services provided on the client. The current implementation uses NSS, which +already knows how to talk to various PKCS #11 modules on the client, and is +portable to most operating systems. A particular emulator can have only one +virtual card implementation at a time. + +The virtual card emulator consists of a series of virtual card services. In +addition to the services describe above (services starting with +vcard_emul_xxxx), the virtual card emulator also provides the following +functions: + + VCardEmulError vcard_emul_init(cont VCardEmulOptions *options); + + The options structure is built by another function in the virtual card + interface where a string of virtual card emulator specific strings are + mapped to the options. The actual structure is defined by the virtual card + emulator and is used to determine the configuration of soft cards, or to + determine which physical cards to present to the guest. + + The vcard_emul_init function will build up sets of readers, create any + threads that are needed to watch for changes in the reader state. If readers + have cards present in them, they are also initialized. + + Readers are created with the function. + + VReader *vreader_new(VReaderEmul *reader_emul, + VReaderEmulFree reader_emul_free); + + The freeFunc is used to free the VReaderEmul * when the reader is + destroyed. The VReaderEmul structure is an opaque structure to the + rest of the code, but defined by the virtual card emulator, which can + use it to store any reader specific state. + + Once the reader has been created, it can be added to the front end with the + call: + + VReaderStatus vreader_add_reader(VReader *reader); + + This function will automatically generate the appropriate new reader + events and add the reader to the list. + + To create a new card, the virtual card emulator will call a similar + function. + + VCard *vcard_new(VCardEmul *card_emul, + VCardEmulFree card_emul_free); + + Like vreader_new, this function takes a virtual card emulator specific + structure which it uses to keep track of the card state. + + Once the card is created, it is attached to a card type emulator with the + following function: + + VCardStatus vcard_init(VCard *vcard, VCardEmulType type, + const char *flags, + unsigned char *const *certs, + int *cert_len, + VCardKey *key[], + int cert_count); + + The vcard is the value returned from vcard_new. The type is the + card type emulator that this card should presented to the guest as. + The flags are card type emulator specific options. The certs, + cert_len, and keys are all arrays of length cert_count. These are the + the same of the parameters xxxx_card_init() accepts. + + Finally the card is associated with its reader by the call: + + VReaderStatus vreader_insert_card(VReader *vreader, VCard *vcard); + + This function, like vreader_add_reader, will take care of any event + notification for the card insert. + + + VCardEmulError vcard_emul_force_card_remove(VReader *vreader); + + Force a card that is present to appear to be removed to the guest, even if + that card is a physical card and is present. + + + VCardEmulError vcard_emul_force_card_insert(VReader *reader); + + Force a card that has been removed by vcard_emul_force_card_remove to be + reinserted from the point of view of the guest. This will only work if the + card is physically present (which is always true fro a soft card). + + void vcard_emul_get_atr(Vcard *card, unsigned char *atr, int *atr_len); + + Return the virtual ATR for the card. By convention this should be the value + VCARD_ATR_PREFIX(size) followed by several ascii bytes related to this + particular emulator. For instance the NSS emulator returns + {VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }. Do ot return more data then *atr_len; + + void vcard_emul_reset(VCard *card, VCardPower power) + + Set the state of 'card' to the current power level and reset its internal + state (logout, etc). + +------------------------------------------------------- +List of files and their function: +README - This file +card_7816.c - emulate basic 7816 functionality. Parse APDUs. +card_7816.h - apdu and response services definitions. +card_7816t.h - 7816 specific structures, types and definitions. +event.c - event handling code. +event.h - event handling services definitions. +eventt.h - event handling structures and types +vcard.c - handle common virtual card services like creation, destruction, and + applet management. +vcard.h - common virtual card services function definitions. +vcardt.h - comon virtual card types +vreader.c - common virtual reader services. +vreader.h - common virtual reader services definitions. +vreadert.h - comon virtual reader types. +vcard_emul_type.c - manage the card type emulators. +vcard_emul_type.h - definitions for card type emulators. +cac.c - card type emulator for CAC cards +vcard_emul.h - virtual card emulator service definitions. +vcard_emul_nss.c - virtual card emulator implementation for nss. +vscclient.c - socket connection to guest qemu usb driver. +vscard_common.h - common header with the guest qemu usb driver. +mutex.h - header file for machine independent mutexes. +link_test.c - static test to make sure all the symbols are properly defined. diff --git a/docs/live-block-ops.txt b/docs/live-block-ops.txt new file mode 100644 index 0000000..a257087 --- /dev/null +++ b/docs/live-block-ops.txt @@ -0,0 +1,58 @@ +LIVE BLOCK OPERATIONS +===================== + +High level description of live block operations. Note these are not +supported for use with the raw format at the moment. + +Snapshot live merge +=================== + +Given a snapshot chain, described in this document in the following +format: + +[A] -> [B] -> [C] -> [D] + +Where the rightmost object ([D] in the example) described is the current +image which the guest OS has write access to. To the left of it is its base +image, and so on accordingly until the leftmost image, which has no +base. + +The snapshot live merge operation transforms such a chain into a +smaller one with fewer elements, such as this transformation relative +to the first example: + +[A] -> [D] + +Currently only forward merge with target being the active image is +supported, that is, data copy is performed in the right direction with +destination being the rightmost image. + +The operation is implemented in QEMU through image streaming facilities. + +The basic idea is to execute 'block_stream virtio0' while the guest is +running. Progress can be monitored using 'info block-jobs'. When the +streaming operation completes it raises a QMP event. 'block_stream' +copies data from the backing file(s) into the active image. When finished, +it adjusts the backing file pointer. + +The 'base' parameter specifies an image which data need not be streamed from. +This image will be used as the backing file for the active image when the +operation is finished. + +In the example above, the command would be: + +(qemu) block_stream virtio0 A + + +Live block copy +=============== + +To copy an in use image to another destination in the filesystem, one +should create a live snapshot in the desired destination, then stream +into that image. Example: + +(qemu) snapshot_blkdev ide0-hd0 /new-path/disk.img qcow2 + +(qemu) block_stream ide0-hd0 + + diff --git a/docs/memory-hotplug.txt b/docs/memory-hotplug.txt new file mode 100644 index 0000000..56bdd0a --- /dev/null +++ b/docs/memory-hotplug.txt @@ -0,0 +1,93 @@ +QEMU memory hotplug +=================== + +This document explains how to use the memory hotplug feature in QEMU, +which is present since v2.1.0. + +Guest support is required for memory hotplug to work. + +Basic RAM hotplug +----------------- + +In order to be able to hotplug memory, QEMU has to be told how many +hotpluggable memory slots to create and what is the maximum amount of +memory the guest can grow. This is done at startup time by means of +the -m command-line option, which has the following format: + + -m [size=]megs[,slots=n,maxmem=size] + +Where, + + - "megs" is the startup RAM. It is the RAM the guest will boot with + - "slots" is the number of hotpluggable memory slots + - "maxmem" is the maximum RAM size the guest can have + +For example, the following command-line: + + qemu [...] 1G,slots=3,maxmem=4G + +Creates a guest with 1GB of memory and three hotpluggable memory slots. +The hotpluggable memory slots are empty when the guest is booted, so all +memory the guest will see after boot is 1GB. The maximum memory the +guest can reach is 4GB. This means that three additional gigabytes can be +hotplugged by using any combination of the available memory slots. + +Two monitor commands are used to hotplug memory: + + - "object_add": creates a memory backend object + - "device_add": creates a front-end pc-dimm device and inserts it + into the first empty slot + +For example, the following commands add another 1GB to the guest +discussed earlier: + + (qemu) object_add memory-backend-ram,id=mem1,size=1G + (qemu) device_add pc-dimm,id=dimm1,memdev=mem1 + +Using the file backend +---------------------- + +Besides basic RAM hotplug, QEMU also supports using files as a memory +backend. This is useful for using hugetlbfs in Linux, which provides +access to bigger page sizes. + +For example, assuming that the host has 1GB hugepages available in +the /mnt/hugepages-1GB directory, a 1GB hugepage could be hotplugged +into the guest from the previous section with the following commands: + + (qemu) object_add memory-backend-file,id=mem1,size=1G,mem-path=/mnt/hugepages-1GB + (qemu) device_add pc-dimm,id=dimm1,memdev=mem1 + +It's also possible to start a guest with memory cold-plugged into the +hotpluggable memory slots. This might seem counterintuitive at first, +but this allows for a lot of flexibility when using the file backend. + +In the following command-line example, a 8GB guest is created where 6GB +comes from regular RAM, 1GB is a 1GB hugepage page and 256MB is from +2MB pages. Also, the guest has additional memory slots to hotplug more +2GB if needed: + + qemu [...] -m 6GB,slots=4,maxmem=10G \ + -object memory-backend-file,id=mem1,size=1G,mem-path=/mnt/hugepages-1G \ + -device pc-dimm,id=dimm1,memdev=mem1 \ + -object memory-backend-file,id=mem2,size=256M,mem-path=/mnt/hugepages-2MB \ + -device pc-dimm,id=dimm2,memdev=mem2 + + +RAM hot-unplug +--------------- + +In order to be able to hot unplug pc-dimm device, QEMU has to be told the ids +of pc-dimm device and memory backend object. The ids were assigned when you hot +plugged memory. + +Two monitor commands are used to hot unplug memory: + + - "device_del": deletes a front-end pc-dimm device + - "object_del": deletes a memory backend object + +For example, assuming that the pc-dimm device with id "dimm1" exists, and its memory +backend is "mem1", the following commands tries to remove it. + + (qemu) device_del dimm1 + (qemu) object_del mem1 diff --git a/docs/memory.txt b/docs/memory.txt new file mode 100644 index 0000000..2ceb348 --- /dev/null +++ b/docs/memory.txt @@ -0,0 +1,286 @@ +The memory API +============== + +The memory API models the memory and I/O buses and controllers of a QEMU +machine. It attempts to allow modelling of: + + - ordinary RAM + - memory-mapped I/O (MMIO) + - memory controllers that can dynamically reroute physical memory regions + to different destinations + +The memory model provides support for + + - tracking RAM changes by the guest + - setting up coalesced memory for kvm + - setting up ioeventfd regions for kvm + +Memory is modelled as an acyclic graph of MemoryRegion objects. Sinks +(leaves) are RAM and MMIO regions, while other nodes represent +buses, memory controllers, and memory regions that have been rerouted. + +In addition to MemoryRegion objects, the memory API provides AddressSpace +objects for every root and possibly for intermediate MemoryRegions too. +These represent memory as seen from the CPU or a device's viewpoint. + +Types of regions +---------------- + +There are four types of memory regions (all represented by a single C type +MemoryRegion): + +- RAM: a RAM region is simply a range of host memory that can be made available + to the guest. + +- MMIO: a range of guest memory that is implemented by host callbacks; + each read or write causes a callback to be called on the host. + +- container: a container simply includes other memory regions, each at + a different offset. Containers are useful for grouping several regions + into one unit. For example, a PCI BAR may be composed of a RAM region + and an MMIO region. + + A container's subregions are usually non-overlapping. In some cases it is + useful to have overlapping regions; for example a memory controller that + can overlay a subregion of RAM with MMIO or ROM, or a PCI controller + that does not prevent card from claiming overlapping BARs. + +- alias: a subsection of another region. Aliases allow a region to be + split apart into discontiguous regions. Examples of uses are memory banks + used when the guest address space is smaller than the amount of RAM + addressed, or a memory controller that splits main memory to expose a "PCI + hole". Aliases may point to any type of region, including other aliases, + but an alias may not point back to itself, directly or indirectly. + +It is valid to add subregions to a region which is not a pure container +(that is, to an MMIO, RAM or ROM region). This means that the region +will act like a container, except that any addresses within the container's +region which are not claimed by any subregion are handled by the +container itself (ie by its MMIO callbacks or RAM backing). However +it is generally possible to achieve the same effect with a pure container +one of whose subregions is a low priority "background" region covering +the whole address range; this is often clearer and is preferred. +Subregions cannot be added to an alias region. + +Region names +------------ + +Regions are assigned names by the constructor. For most regions these are +only used for debugging purposes, but RAM regions also use the name to identify +live migration sections. This means that RAM region names need to have ABI +stability. + +Region lifecycle +---------------- + +A region is created by one of the memory_region_init*() functions and +attached to an object, which acts as its owner or parent. QEMU ensures +that the owner object remains alive as long as the region is visible to +the guest, or as long as the region is in use by a virtual CPU or another +device. For example, the owner object will not die between an +address_space_map operation and the corresponding address_space_unmap. + +After creation, a region can be added to an address space or a +container with memory_region_add_subregion(), and removed using +memory_region_del_subregion(). + +Various region attributes (read-only, dirty logging, coalesced mmio, +ioeventfd) can be changed during the region lifecycle. They take effect +as soon as the region is made visible. This can be immediately, later, +or never. + +Destruction of a memory region happens automatically when the owner +object dies. + +If however the memory region is part of a dynamically allocated data +structure, you should call object_unparent() to destroy the memory region +before the data structure is freed. For an example see VFIOMSIXInfo +and VFIOQuirk in hw/vfio/pci.c. + +You must not destroy a memory region as long as it may be in use by a +device or CPU. In order to do this, as a general rule do not create or +destroy memory regions dynamically during a device's lifetime, and only +call object_unparent() in the memory region owner's instance_finalize +callback. The dynamically allocated data structure that contains the +memory region then should obviously be freed in the instance_finalize +callback as well. + +If you break this rule, the following situation can happen: + +- the memory region's owner had a reference taken via memory_region_ref + (for example by address_space_map) + +- the region is unparented, and has no owner anymore + +- when address_space_unmap is called, the reference to the memory region's + owner is leaked. + + +There is an exception to the above rule: it is okay to call +object_unparent at any time for an alias or a container region. It is +therefore also okay to create or destroy alias and container regions +dynamically during a device's lifetime. + +This exceptional usage is valid because aliases and containers only help +QEMU building the guest's memory map; they are never accessed directly. +memory_region_ref and memory_region_unref are never called on aliases +or containers, and the above situation then cannot happen. Exploiting +this exception is rarely necessary, and therefore it is discouraged, +but nevertheless it is used in a few places. + +For regions that "have no owner" (NULL is passed at creation time), the +machine object is actually used as the owner. Since instance_finalize is +never called for the machine object, you must never call object_unparent +on regions that have no owner, unless they are aliases or containers. + + +Overlapping regions and priority +-------------------------------- +Usually, regions may not overlap each other; a memory address decodes into +exactly one target. In some cases it is useful to allow regions to overlap, +and sometimes to control which of an overlapping regions is visible to the +guest. This is done with memory_region_add_subregion_overlap(), which +allows the region to overlap any other region in the same container, and +specifies a priority that allows the core to decide which of two regions at +the same address are visible (highest wins). +Priority values are signed, and the default value is zero. This means that +you can use memory_region_add_subregion_overlap() both to specify a region +that must sit 'above' any others (with a positive priority) and also a +background region that sits 'below' others (with a negative priority). + +If the higher priority region in an overlap is a container or alias, then +the lower priority region will appear in any "holes" that the higher priority +region has left by not mapping subregions to that area of its address range. +(This applies recursively -- if the subregions are themselves containers or +aliases that leave holes then the lower priority region will appear in these +holes too.) + +For example, suppose we have a container A of size 0x8000 with two subregions +B and C. B is a container mapped at 0x2000, size 0x4000, priority 1; C is +an MMIO region mapped at 0x0, size 0x6000, priority 2. B currently has two +of its own subregions: D of size 0x1000 at offset 0 and E of size 0x1000 at +offset 0x2000. As a diagram: + + 0 1000 2000 3000 4000 5000 6000 7000 8000 + |------|------|------|------|------|------|------|-------| + A: [ ] + C: [CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC] + B: [ ] + D: [DDDDD] + E: [EEEEE] + +The regions that will be seen within this address range then are: + [CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC] + +Since B has higher priority than C, its subregions appear in the flat map +even where they overlap with C. In ranges where B has not mapped anything +C's region appears. + +If B had provided its own MMIO operations (ie it was not a pure container) +then these would be used for any addresses in its range not handled by +D or E, and the result would be: + [CCCCCCCCCCCC][DDDDD][BBBBB][EEEEE][BBBBB] + +Priority values are local to a container, because the priorities of two +regions are only compared when they are both children of the same container. +This means that the device in charge of the container (typically modelling +a bus or a memory controller) can use them to manage the interaction of +its child regions without any side effects on other parts of the system. +In the example above, the priorities of D and E are unimportant because +they do not overlap each other. It is the relative priority of B and C +that causes D and E to appear on top of C: D and E's priorities are never +compared against the priority of C. + +Visibility +---------- +The memory core uses the following rules to select a memory region when the +guest accesses an address: + +- all direct subregions of the root region are matched against the address, in + descending priority order + - if the address lies outside the region offset/size, the subregion is + discarded + - if the subregion is a leaf (RAM or MMIO), the search terminates, returning + this leaf region + - if the subregion is a container, the same algorithm is used within the + subregion (after the address is adjusted by the subregion offset) + - if the subregion is an alias, the search is continued at the alias target + (after the address is adjusted by the subregion offset and alias offset) + - if a recursive search within a container or alias subregion does not + find a match (because of a "hole" in the container's coverage of its + address range), then if this is a container with its own MMIO or RAM + backing the search terminates, returning the container itself. Otherwise + we continue with the next subregion in priority order +- if none of the subregions match the address then the search terminates + with no match found + +Example memory map +------------------ + +system_memory: container@0-2^48-1 + | + +---- lomem: alias@0-0xdfffffff ---> #ram (0-0xdfffffff) + | + +---- himem: alias@0x100000000-0x11fffffff ---> #ram (0xe0000000-0xffffffff) + | + +---- vga-window: alias@0xa0000-0xbfffff ---> #pci (0xa0000-0xbffff) + | (prio 1) + | + +---- pci-hole: alias@0xe0000000-0xffffffff ---> #pci (0xe0000000-0xffffffff) + +pci (0-2^32-1) + | + +--- vga-area: container@0xa0000-0xbffff + | | + | +--- alias@0x00000-0x7fff ---> #vram (0x010000-0x017fff) + | | + | +--- alias@0x08000-0xffff ---> #vram (0x020000-0x027fff) + | + +---- vram: ram@0xe1000000-0xe1ffffff + | + +---- vga-mmio: mmio@0xe2000000-0xe200ffff + +ram: ram@0x00000000-0xffffffff + +This is a (simplified) PC memory map. The 4GB RAM block is mapped into the +system address space via two aliases: "lomem" is a 1:1 mapping of the first +3.5GB; "himem" maps the last 0.5GB at address 4GB. This leaves 0.5GB for the +so-called PCI hole, that allows a 32-bit PCI bus to exist in a system with +4GB of memory. + +The memory controller diverts addresses in the range 640K-768K to the PCI +address space. This is modelled using the "vga-window" alias, mapped at a +higher priority so it obscures the RAM at the same addresses. The vga window +can be removed by programming the memory controller; this is modelled by +removing the alias and exposing the RAM underneath. + +The pci address space is not a direct child of the system address space, since +we only want parts of it to be visible (we accomplish this using aliases). +It has two subregions: vga-area models the legacy vga window and is occupied +by two 32K memory banks pointing at two sections of the framebuffer. +In addition the vram is mapped as a BAR at address e1000000, and an additional +BAR containing MMIO registers is mapped after it. + +Note that if the guest maps a BAR outside the PCI hole, it would not be +visible as the pci-hole alias clips it to a 0.5GB range. + +MMIO Operations +--------------- + +MMIO regions are provided with ->read() and ->write() callbacks; in addition +various constraints can be supplied to control how these callbacks are called: + + - .valid.min_access_size, .valid.max_access_size define the access sizes + (in bytes) which the device accepts; accesses outside this range will + have device and bus specific behaviour (ignored, or machine check) + - .valid.aligned specifies that the device only accepts naturally aligned + accesses. Unaligned accesses invoke device and bus specific behaviour. + - .impl.min_access_size, .impl.max_access_size define the access sizes + (in bytes) supported by the *implementation*; other access sizes will be + emulated using the ones available. For example a 4-byte write will be + emulated using four 1-byte writes, if .impl.max_access_size = 1. + - .impl.unaligned specifies that the *implementation* supports unaligned + accesses; if false, unaligned accesses will be emulated by two aligned + accesses. + - .old_mmio can be used to ease porting from code using + cpu_register_io_memory(). It should not be used in new code. diff --git a/docs/migration.txt b/docs/migration.txt new file mode 100644 index 0000000..0492a45 --- /dev/null +++ b/docs/migration.txt @@ -0,0 +1,296 @@ += Migration = + +QEMU has code to load/save the state of the guest that it is running. +These are two complementary operations. Saving the state just does +that, saves the state for each device that the guest is running. +Restoring a guest is just the opposite operation: we need to load the +state of each device. + +For this to work, QEMU has to be launched with the same arguments the +two times. I.e. it can only restore the state in one guest that has +the same devices that the one it was saved (this last requirement can +be relaxed a bit, but for now we can consider that configuration has +to be exactly the same). + +Once that we are able to save/restore a guest, a new functionality is +requested: migration. This means that QEMU is able to start in one +machine and being "migrated" to another machine. I.e. being moved to +another machine. + +Next was the "live migration" functionality. This is important +because some guests run with a lot of state (specially RAM), and it +can take a while to move all state from one machine to another. Live +migration allows the guest to continue running while the state is +transferred. Only while the last part of the state is transferred has +the guest to be stopped. Typically the time that the guest is +unresponsive during live migration is the low hundred of milliseconds +(notice that this depends on a lot of things). + +=== Types of migration === + +Now that we have talked about live migration, there are several ways +to do migration: + +- tcp migration: do the migration using tcp sockets +- unix migration: do the migration using unix sockets +- exec migration: do the migration using the stdin/stdout through a process. +- fd migration: do the migration using an file descriptor that is + passed to QEMU. QEMU doesn't care how this file descriptor is opened. + +All these four migration protocols use the same infrastructure to +save/restore state devices. This infrastructure is shared with the +savevm/loadvm functionality. + +=== State Live Migration === + +This is used for RAM and block devices. It is not yet ported to vmstate. + + +=== What is the common infrastructure === + +QEMU uses a QEMUFile abstraction to be able to do migration. Any type +of migration that wants to use QEMU infrastructure has to create a +QEMUFile with: + +QEMUFile *qemu_fopen_ops(void *opaque, + QEMUFilePutBufferFunc *put_buffer, + QEMUFileGetBufferFunc *get_buffer, + QEMUFileCloseFunc *close); + +The functions have the following functionality: + +This function writes a chunk of data to a file at the given position. +The pos argument can be ignored if the file is only used for +streaming. The handler should try to write all of the data it can. + +typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, + int64_t pos, int size); + +Read a chunk of data from a file at the given position. The pos argument +can be ignored if the file is only be used for streaming. The number of +bytes actually read should be returned. + +typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, + int64_t pos, int size); + +Close a file and return an error code. + +typedef int (QEMUFileCloseFunc)(void *opaque); + +You can use any internal state that you need using the opaque void * +pointer that is passed to all functions. + +The important functions for us are put_buffer()/get_buffer() that +allow to write/read a buffer into the QEMUFile. + +=== How to save the state of one device === + +The state of a device is saved using intermediate buffers. There are +some helper functions to assist this saving. + +There is a new concept that we have to explain here: device state +version. When we migrate a device, we save/load the state as a series +of fields. Some times, due to bugs or new functionality, we need to +change the state to store more/different information. We use the +version to identify each time that we do a change. Each version is +associated with a series of fields saved. The save_state always saves +the state as the newer version. But load_state sometimes is able to +load state from an older version. + +=== Legacy way === + +This way is going to disappear as soon as all current users are ported to VMSTATE. + +Each device has to register two functions, one to save the state and +another to load the state back. + +int register_savevm(DeviceState *dev, + const char *idstr, + int instance_id, + int version_id, + SaveStateHandler *save_state, + LoadStateHandler *load_state, + void *opaque); + +typedef void SaveStateHandler(QEMUFile *f, void *opaque); +typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); + +The important functions for the device state format are the save_state +and load_state. Notice that load_state receives a version_id +parameter to know what state format is receiving. save_state doesn't +have a version_id parameter because it always uses the latest version. + +=== VMState === + +The legacy way of saving/loading state of the device had the problem +that we have to maintain two functions in sync. If we did one change +in one of them and not in the other, we would get a failed migration. + +VMState changed the way that state is saved/loaded. Instead of using +a function to save the state and another to load it, it was changed to +a declarative way of what the state consisted of. Now VMState is able +to interpret that definition to be able to load/save the state. As +the state is declared only once, it can't go out of sync in the +save/load functions. + +An example (from hw/input/pckbd.c) + +static const VMStateDescription vmstate_kbd = { + .name = "pckbd", + .version_id = 3, + .minimum_version_id = 3, + .fields = (VMStateField[]) { + VMSTATE_UINT8(write_cmd, KBDState), + VMSTATE_UINT8(status, KBDState), + VMSTATE_UINT8(mode, KBDState), + VMSTATE_UINT8(pending, KBDState), + VMSTATE_END_OF_LIST() + } +}; + +We are declaring the state with name "pckbd". +The version_id is 3, and the fields are 4 uint8_t in a KBDState structure. +We registered this with: + + vmstate_register(NULL, 0, &vmstate_kbd, s); + +Note: talk about how vmstate <-> qdev interact, and what the instance ids mean. + +You can search for VMSTATE_* macros for lots of types used in QEMU in +include/hw/hw.h. + +=== More about versions === + +You can see that there are several version fields: + +- version_id: the maximum version_id supported by VMState for that device. +- minimum_version_id: the minimum version_id that VMState is able to understand + for that device. +- minimum_version_id_old: For devices that were not able to port to vmstate, we can + assign a function that knows how to read this old state. This field is + ignored if there is no load_state_old handler. + +So, VMState is able to read versions from minimum_version_id to +version_id. And the function load_state_old() (if present) is able to +load state from minimum_version_id_old to minimum_version_id. This +function is deprecated and will be removed when no more users are left. + +=== Massaging functions === + +Sometimes, it is not enough to be able to save the state directly +from one structure, we need to fill the correct values there. One +example is when we are using kvm. Before saving the cpu state, we +need to ask kvm to copy to QEMU the state that it is using. And the +opposite when we are loading the state, we need a way to tell kvm to +load the state for the cpu that we have just loaded from the QEMUFile. + +The functions to do that are inside a vmstate definition, and are called: + +- int (*pre_load)(void *opaque); + + This function is called before we load the state of one device. + +- int (*post_load)(void *opaque, int version_id); + + This function is called after we load the state of one device. + +- void (*pre_save)(void *opaque); + + This function is called before we save the state of one device. + +Example: You can look at hpet.c, that uses the three function to + massage the state that is transferred. + +If you use memory API functions that update memory layout outside +initialization (i.e., in response to a guest action), this is a strong +indication that you need to call these functions in a post_load callback. +Examples of such memory API functions are: + + - memory_region_add_subregion() + - memory_region_del_subregion() + - memory_region_set_readonly() + - memory_region_set_enabled() + - memory_region_set_address() + - memory_region_set_alias_offset() + +=== Subsections === + +The use of version_id allows to be able to migrate from older versions +to newer versions of a device. But not the other way around. This +makes very complicated to fix bugs in stable branches. If we need to +add anything to the state to fix a bug, we have to disable migration +to older versions that don't have that bug-fix (i.e. a new field). + +But sometimes, that bug-fix is only needed sometimes, not always. For +instance, if the device is in the middle of a DMA operation, it is +using a specific functionality, .... + +It is impossible to create a way to make migration from any version to +any other version to work. But we can do better than only allowing +migration from older versions to newer ones. For that fields that are +only needed sometimes, we add the idea of subsections. A subsection +is "like" a device vmstate, but with a particularity, it has a Boolean +function that tells if that values are needed to be sent or not. If +this functions returns false, the subsection is not sent. + +On the receiving side, if we found a subsection for a device that we +don't understand, we just fail the migration. If we understand all +the subsections, then we load the state with success. + +One important note is that the post_load() function is called "after" +loading all subsections, because a newer subsection could change same +value that it uses. + +Example: + +static bool ide_drive_pio_state_needed(void *opaque) +{ + IDEState *s = opaque; + + return ((s->status & DRQ_STAT) != 0) + || (s->bus->error_status & BM_STATUS_PIO_RETRY); +} + +const VMStateDescription vmstate_ide_drive_pio_state = { + .name = "ide_drive/pio_state", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = ide_drive_pio_pre_save, + .post_load = ide_drive_pio_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT32(req_nb_sectors, IDEState), + VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, + vmstate_info_uint8, uint8_t), + VMSTATE_INT32(cur_io_buffer_offset, IDEState), + VMSTATE_INT32(cur_io_buffer_len, IDEState), + VMSTATE_UINT8(end_transfer_fn_idx, IDEState), + VMSTATE_INT32(elementary_transfer_size, IDEState), + VMSTATE_INT32(packet_transfer_size, IDEState), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_ide_drive = { + .name = "ide_drive", + .version_id = 3, + .minimum_version_id = 0, + .post_load = ide_drive_post_load, + .fields = (VMStateField[]) { + .... several fields .... + VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_ide_drive_pio_state, + .needed = ide_drive_pio_state_needed, + }, { + /* empty */ + } + } +}; + +Here we have a subsection for the pio state. We only need to +save/send this state when we are in the middle of a pio operation +(that is what ide_drive_pio_state_needed() checks). If DRQ_STAT is +not enabled, the values on that fields are garbage and don't need to +be sent. diff --git a/docs/multi-thread-compression.txt b/docs/multi-thread-compression.txt new file mode 100644 index 0000000..3d477c3 --- /dev/null +++ b/docs/multi-thread-compression.txt @@ -0,0 +1,149 @@ +Use multiple thread (de)compression in live migration +===================================================== +Copyright (C) 2015 Intel Corporation +Author: Liang Li + +This work is licensed under the terms of the GNU GPLv2 or later. See +the COPYING file in the top-level directory. + +Contents: +========= +* Introduction +* When to use +* Performance +* Usage +* TODO + +Introduction +============ +Instead of sending the guest memory directly, this solution will +compress the RAM page before sending; after receiving, the data will +be decompressed. Using compression in live migration can help +to reduce the data transferred about 60%, this is very useful when the +bandwidth is limited, and the total migration time can also be reduced +about 70% in a typical case. In addition to this, the VM downtime can be +reduced about 50%. The benefit depends on data's compressibility in VM. + +The process of compression will consume additional CPU cycles, and the +extra CPU cycles will increase the migration time. On the other hand, +the amount of data transferred will decrease; this factor can reduce +the total migration time. If the process of the compression is quick +enough, then the total migration time can be reduced, and multiple +thread compression can be used to accelerate the compression process. + +The decompression speed of Zlib is at least 4 times as quick as +compression, if the source and destination CPU have equal speed, +keeping the compression thread count 4 times the decompression +thread count can avoid resource waste. + +Compression level can be used to control the compression speed and the +compression ratio. High compression ratio will take more time, level 0 +stands for no compression, level 1 stands for the best compression +speed, and level 9 stands for the best compression ratio. Users can +select a level number between 0 and 9. + + +When to use the multiple thread compression in live migration +============================================================= +Compression of data will consume extra CPU cycles; so in a system with +high overhead of CPU, avoid using this feature. When the network +bandwidth is very limited and the CPU resource is adequate, use of +multiple thread compression will be very helpful. If both the CPU and +the network bandwidth are adequate, use of multiple thread compression +can still help to reduce the migration time. + +Performance +=========== +Test environment: + +CPU: Intel(R) Xeon(R) CPU E5-2680 0 @ 2.70GHz +Socket Count: 2 +RAM: 128G +NIC: Intel I350 (10/100/1000Mbps) +Host OS: CentOS 7 64-bit +Guest OS: RHEL 6.5 64-bit +Parameter: qemu-system-x86_64 -enable-kvm -smp 4 -m 4096 + /share/ia32e_rhel6u5.qcow -monitor stdio + +There is no additional application is running on the guest when doing +the test. + + +Speed limit: 1000Gb/s +--------------------------------------------------------------- + | original | compress thread: 8 + | way | decompress thread: 2 + | | compression level: 1 +--------------------------------------------------------------- +total time(msec): | 3333 | 1833 +--------------------------------------------------------------- +downtime(msec): | 100 | 27 +--------------------------------------------------------------- +transferred ram(kB):| 363536 | 107819 +--------------------------------------------------------------- +throughput(mbps): | 893.73 | 482.22 +--------------------------------------------------------------- +total ram(kB): | 4211524 | 4211524 +--------------------------------------------------------------- + +There is an application running on the guest which write random numbers +to RAM block areas periodically. + +Speed limit: 1000Gb/s +--------------------------------------------------------------- + | original | compress thread: 8 + | way | decompress thread: 2 + | | compression level: 1 +--------------------------------------------------------------- +total time(msec): | 37369 | 15989 +--------------------------------------------------------------- +downtime(msec): | 337 | 173 +--------------------------------------------------------------- +transferred ram(kB):| 4274143 | 1699824 +--------------------------------------------------------------- +throughput(mbps): | 936.99 | 870.95 +--------------------------------------------------------------- +total ram(kB): | 4211524 | 4211524 +--------------------------------------------------------------- + +Usage +===== +1. Verify both the source and destination QEMU are able +to support the multiple thread compression migration: + {qemu} info_migrate_capabilities + {qemu} ... compress: off ... + +2. Activate compression on the source: + {qemu} migrate_set_capability compress on + +3. Set the compression thread count on source: + {qemu} migrate_set_parameter compress_threads 12 + +4. Set the compression level on the source: + {qemu} migrate_set_parameter compress_level 1 + +5. Set the decompression thread count on destination: + {qemu} migrate_set_parameter decompress_threads 3 + +6. Start outgoing migration: + {qemu} migrate -d tcp:destination.host:4444 + {qemu} info migrate + Capabilities: ... compress: on + ... + +The following are the default settings: + compress: off + compress_threads: 8 + decompress_threads: 2 + compress_level: 1 (which means best speed) + +So, only the first two steps are required to use the multiple +thread compression in migration. You can do more if the default +settings are not appropriate. + +TODO +==== +Some faster (de)compression method such as LZ4 and Quicklz can help +to reduce the CPU consumption when doing (de)compression. If using +these faster (de)compression method, less (de)compression threads +are needed when doing the migration. diff --git a/docs/multiple-iothreads.txt b/docs/multiple-iothreads.txt new file mode 100644 index 0000000..40b8419 --- /dev/null +++ b/docs/multiple-iothreads.txt @@ -0,0 +1,134 @@ +Copyright (c) 2014 Red Hat Inc. + +This work is licensed under the terms of the GNU GPL, version 2 or later. See +the COPYING file in the top-level directory. + + +This document explains the IOThread feature and how to write code that runs +outside the QEMU global mutex. + +The main loop and IOThreads +--------------------------- +QEMU is an event-driven program that can do several things at once using an +event loop. The VNC server and the QMP monitor are both processed from the +same event loop, which monitors their file descriptors until they become +readable and then invokes a callback. + +The default event loop is called the main loop (see main-loop.c). It is +possible to create additional event loop threads using -object +iothread,id=my-iothread. + +Side note: The main loop and IOThread are both event loops but their code is +not shared completely. Sometimes it is useful to remember that although they +are conceptually similar they are currently not interchangeable. + +Why IOThreads are useful +------------------------ +IOThreads allow the user to control the placement of work. The main loop is a +scalability bottleneck on hosts with many CPUs. Work can be spread across +several IOThreads instead of just one main loop. When set up correctly this +can improve I/O latency and reduce jitter seen by the guest. + +The main loop is also deeply associated with the QEMU global mutex, which is a +scalability bottleneck in itself. vCPU threads and the main loop use the QEMU +global mutex to serialize execution of QEMU code. This mutex is necessary +because a lot of QEMU's code historically was not thread-safe. + +The fact that all I/O processing is done in a single main loop and that the +QEMU global mutex is contended by all vCPU threads and the main loop explain +why it is desirable to place work into IOThreads. + +The experimental virtio-blk data-plane implementation has been benchmarked and +shows these effects: +ftp://public.dhe.ibm.com/linux/pdfs/KVM_Virtualized_IO_Performance_Paper.pdf + +How to program for IOThreads +---------------------------- +The main difference between legacy code and new code that can run in an +IOThread is dealing explicitly with the event loop object, AioContext +(see include/block/aio.h). Code that only works in the main loop +implicitly uses the main loop's AioContext. Code that supports running +in IOThreads must be aware of its AioContext. + +AioContext supports the following services: + * File descriptor monitoring (read/write/error on POSIX hosts) + * Event notifiers (inter-thread signalling) + * Timers + * Bottom Halves (BH) deferred callbacks + +There are several old APIs that use the main loop AioContext: + * LEGACY qemu_aio_set_fd_handler() - monitor a file descriptor + * LEGACY qemu_aio_set_event_notifier() - monitor an event notifier + * LEGACY timer_new_ms() - create a timer + * LEGACY qemu_bh_new() - create a BH + * LEGACY qemu_aio_wait() - run an event loop iteration + +Since they implicitly work on the main loop they cannot be used in code that +runs in an IOThread. They might cause a crash or deadlock if called from an +IOThread since the QEMU global mutex is not held. + +Instead, use the AioContext functions directly (see include/block/aio.h): + * aio_set_fd_handler() - monitor a file descriptor + * aio_set_event_notifier() - monitor an event notifier + * aio_timer_new() - create a timer + * aio_bh_new() - create a BH + * aio_poll() - run an event loop iteration + +The AioContext can be obtained from the IOThread using +iothread_get_aio_context() or for the main loop using qemu_get_aio_context(). +Code that takes an AioContext argument works both in IOThreads or the main +loop, depending on which AioContext instance the caller passes in. + +How to synchronize with an IOThread +----------------------------------- +AioContext is not thread-safe so some rules must be followed when using file +descriptors, event notifiers, timers, or BHs across threads: + +1. AioContext functions can be called safely from file descriptor, event +notifier, timer, or BH callbacks invoked by the AioContext. No locking is +necessary. + +2. Other threads wishing to access the AioContext must use +aio_context_acquire()/aio_context_release() for mutual exclusion. Once the +context is acquired no other thread can access it or run event loop iterations +in this AioContext. + +aio_context_acquire()/aio_context_release() calls may be nested. This +means you can call them if you're not sure whether #1 applies. + +There is currently no lock ordering rule if a thread needs to acquire multiple +AioContexts simultaneously. Therefore, it is only safe for code holding the +QEMU global mutex to acquire other AioContexts. + +Side note: the best way to schedule a function call across threads is to create +a BH in the target AioContext beforehand and then call qemu_bh_schedule(). No +acquire/release or locking is needed for the qemu_bh_schedule() call. But be +sure to acquire the AioContext for aio_bh_new() if necessary. + +The relationship between AioContext and the block layer +------------------------------------------------------- +The AioContext originates from the QEMU block layer because it provides a +scoped way of running event loop iterations until all work is done. This +feature is used to complete all in-flight block I/O requests (see +bdrv_drain_all()). Nowadays AioContext is a generic event loop that can be +used by any QEMU subsystem. + +The block layer has support for AioContext integrated. Each BlockDriverState +is associated with an AioContext using bdrv_set_aio_context() and +bdrv_get_aio_context(). This allows block layer code to process I/O inside the +right AioContext. Other subsystems may wish to follow a similar approach. + +Block layer code must therefore expect to run in an IOThread and avoid using +old APIs that implicitly use the main loop. See the "How to program for +IOThreads" above for information on how to do that. + +If main loop code such as a QMP function wishes to access a BlockDriverState it +must first call aio_context_acquire(bdrv_get_aio_context(bs)) to ensure the +IOThread does not run in parallel. + +Long-running jobs (usually in the form of coroutines) are best scheduled in the +BlockDriverState's AioContext to avoid the need to acquire/release around each +bdrv_*() call. Be aware that there is currently no mechanism to get notified +when bdrv_set_aio_context() moves this BlockDriverState to a different +AioContext (see bdrv_detach_aio_context()/bdrv_attach_aio_context()), so you +may need to add this if you want to support long-running jobs. diff --git a/docs/multiseat.txt b/docs/multiseat.txt new file mode 100644 index 0000000..b963665 --- /dev/null +++ b/docs/multiseat.txt @@ -0,0 +1,112 @@ + +multiseat howto (with some multihead coverage) +============================================== + +host side +--------- + +First you must compile qemu with a user interface supporting +multihead/multiseat and input event routing. Right now this +list includes sdl2, gtk (both 2+3) and vnc: + + ./configure --enable-sdl --with-sdlabi=2.0 + +or + + ./configure --enable-gtk + + +Next put together the qemu command line (sdk/gtk): + +qemu -enable-kvm -usb $memory $disk $whatever \ + -display [ sdl | gtk ] \ + -vga std \ + -device usb-tablet + +That is it for the first seat, which will use the standard vga, the +standard ps/2 keyboard (implicitly there) and the usb-tablet. Now the +additional switches for the second seat: + + -device pci-bridge,addr=12.0,chassis_nr=2,id=head.2 \ + -device secondary-vga,bus=head.2,addr=02.0,id=video.2 \ + -device nec-usb-xhci,bus=head.2,addr=0f.0,id=usb.2 \ + -device usb-kbd,bus=usb.2.0,port=1,display=video.2 \ + -device usb-tablet,bus=usb.2.0,port=2,display=video.2 + +This places a pci bridge in slot 12, connects a display adapter and +xhci (usb) controller to the bridge. Then it adds a usb keyboard and +usb mouse, both connected to the xhci and linked to the display. + +The "display=video2" sets up the input routing. Any input coming from +the window which belongs to the video.2 display adapter will be routed +to these input devices. + +The sdl2 ui will start up with two windows, one for each display +device. The gtk ui will start with a single window and each display +in a separate tab. You can either simply switch tabs to switch heads, +or use the "View / Detach tab" menu item to move one of the displays +to its own window so you can see both display devices side-by-side. + +For vnc some additional configuration on the command line is needed. +We'll create two vnc server instances, and bind the second one to the +second seat, simliar to input devices: + + -display vnc=:1,id=primary \ + -display vnc=:2,id=secondary,display=video.2 + +Connecting to vnc display :1 gives you access to the first seat, and +likewise connecting to vnc display :2 shows the second seat. + +Note on spice: Spice handles multihead just fine. But it can't do +multiseat. For tablet events the event source is sent to the spice +agent. But qemu can't figure it, so it can't do input routing. +Fixing this needs a new or extended input interface between +libspice-server and qemu. For keyboard events it is even worse: The +event source isn't included in the spice protocol, so the wire +protocol must be extended to support this. + + +guest side +---------- + +You need a pretty recent linux guest. systemd with loginctl. kernel +3.14+ with CONFIG_DRM_BOCHS enabled. Fedora 20 will do. Must be +fully updated for the new kernel though, i.e. the live iso doesn't cut +it. + +Now we'll have to configure the guest. Boot and login. "lspci -vt" +should list the pci bridge with the display adapter and usb controller: + + [root@fedora ~]# lspci -vt + -[0000:00]-+-00.0 Intel Corporation 440FX - 82441FX PMC [Natoma] + [ ... ] + \-12.0-[01]--+-02.0 Device 1234:1111 + \-0f.0 NEC Corporation USB 3.0 Host Controller + +Good. Now lets tell the system that the pci bridge and all devices +below it belong to a separate seat by dropping a file into +/etc/udev/rules.d: + + [root@fedora ~]# cat /etc/udev/rules.d/70-qemu-autoseat.rules + SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:12.0", TAG+="seat", ENV{ID_AUTOSEAT}="1" + +Reboot. System should come up with two seats. With loginctl you can +check the configuration: + + [root@fedora ~]# loginctl list-seats + SEAT + seat0 + seat-pci-pci-0000_00_12_0 + + 2 seats listed. + +You can use "loginctl seat-status seat-pci-pci-0000_00_12_0" to list +the devices attached to the seat. + +Background info is here: + http://www.freedesktop.org/wiki/Software/systemd/multiseat/ + +Enjoy! + +-- +Gerd Hoffmann diff --git a/docs/q35-chipset.cfg b/docs/q35-chipset.cfg new file mode 100644 index 0000000..e4ddb7d --- /dev/null +++ b/docs/q35-chipset.cfg @@ -0,0 +1,152 @@ +################################################################ +# +# qemu -M q35 creates a bare machine with just the very essential +# chipset devices being present: +# +# 00.0 - Host bridge +# 1f.0 - ISA bridge / LPC +# 1f.2 - SATA (AHCI) controller +# 1f.3 - SMBus controller +# +# This config file documents the other devices and how they are +# created. You can simply use "-readconfig $thisfile" to create +# them all. Here is a overview: +# +# 19.0 - Ethernet controller (not created, our e1000 emulation +# doesn't emulate the ich9 device). +# 1a.* - USB Controller #2 (ehci + uhci companions) +# 1b.0 - HD Audio Controller +# 1c.* - PCI Express Ports +# 1d.* - USB Controller #1 (ehci + uhci companions, +# "qemu -M q35 -usb" creates these too) +# 1e.0 - PCI Bridge +# + +[device "ich9-ehci-2"] + driver = "ich9-usb-ehci2" + multifunction = "on" + bus = "pcie.0" + addr = "1a.7" + +[device "ich9-uhci-4"] + driver = "ich9-usb-uhci4" + multifunction = "on" + bus = "pcie.0" + addr = "1a.0" + masterbus = "ich9-ehci-2.0" + firstport = "0" + +[device "ich9-uhci-5"] + driver = "ich9-usb-uhci5" + multifunction = "on" + bus = "pcie.0" + addr = "1a.1" + masterbus = "ich9-ehci-2.0" + firstport = "2" + +[device "ich9-uhci-6"] + driver = "ich9-usb-uhci6" + multifunction = "on" + bus = "pcie.0" + addr = "1a.2" + masterbus = "ich9-ehci-2.0" + firstport = "4" + + +[device "ich9-hda-audio"] + driver = "ich9-intel-hda" + bus = "pcie.0" + addr = "1b.0" + + +[device "ich9-pcie-port-1"] + driver = "ioh3420" + multifunction = "on" + bus = "pcie.0" + addr = "1c.0" + port = "1" + chassis = "1" + +[device "ich9-pcie-port-2"] + driver = "ioh3420" + multifunction = "on" + bus = "pcie.0" + addr = "1c.1" + port = "2" + chassis = "2" + +[device "ich9-pcie-port-3"] + driver = "ioh3420" + multifunction = "on" + bus = "pcie.0" + addr = "1c.2" + port = "3" + chassis = "3" + +[device "ich9-pcie-port-4"] + driver = "ioh3420" + multifunction = "on" + bus = "pcie.0" + addr = "1c.3" + port = "4" + chassis = "4" + +## +# Example PCIe switch with two downstream ports +# +#[device "pcie-switch-upstream-port-1"] +# driver = "x3130-upstream" +# bus = "ich9-pcie-port-4" +# addr = "00.0" +# +#[device "pcie-switch-downstream-port-1-1"] +# driver = "xio3130-downstream" +# multifunction = "on" +# bus = "pcie-switch-upstream-port-1" +# addr = "00.0" +# port = "1" +# chassis = "5" +# +#[device "pcie-switch-downstream-port-1-2"] +# driver = "xio3130-downstream" +# multifunction = "on" +# bus = "pcie-switch-upstream-port-1" +# addr = "00.1" +# port = "1" +# chassis = "6" + +[device "ich9-ehci-1"] + driver = "ich9-usb-ehci1" + multifunction = "on" + bus = "pcie.0" + addr = "1d.7" + +[device "ich9-uhci-1"] + driver = "ich9-usb-uhci1" + multifunction = "on" + bus = "pcie.0" + addr = "1d.0" + masterbus = "ich9-ehci-1.0" + firstport = "0" + +[device "ich9-uhci-2"] + driver = "ich9-usb-uhci2" + multifunction = "on" + bus = "pcie.0" + addr = "1d.1" + masterbus = "ich9-ehci-1.0" + firstport = "2" + +[device "ich9-uhci-3"] + driver = "ich9-usb-uhci3" + multifunction = "on" + bus = "pcie.0" + addr = "1d.2" + masterbus = "ich9-ehci-1.0" + firstport = "4" + + +[device "ich9-pci-bridge"] + driver = "i82801b11-bridge" + bus = "pcie.0" + addr = "1e.0" diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt new file mode 100644 index 0000000..3f0522e --- /dev/null +++ b/docs/qapi-code-gen.txt @@ -0,0 +1,855 @@ += How to use the QAPI code generator = + +Copyright IBM Corp. 2011 +Copyright (C) 2012-2015 Red Hat, Inc. + +This work is licensed under the terms of the GNU GPL, version 2 or +later. See the COPYING file in the top-level directory. + +== Introduction == + +QAPI is a native C API within QEMU which provides management-level +functionality to internal and external users. For external +users/processes, this interface is made available by a JSON-based wire +format for the QEMU Monitor Protocol (QMP) for controlling qemu, as +well as the QEMU Guest Agent (QGA) for communicating with the guest. +The remainder of this document uses "Client JSON Protocol" when +referring to the wire contents of a QMP or QGA connection. + +To map Client JSON Protocol interfaces to the native C QAPI +implementations, a JSON-based schema is used to define types and +function signatures, and a set of scripts is used to generate types, +signatures, and marshaling/dispatch code. This document will describe +how the schemas, scripts, and resulting code are used. + + +== QMP/Guest agent schema == + +A QAPI schema file is designed to be loosely based on JSON +(http://www.ietf.org/rfc/rfc7159.txt) with changes for quoting style +and the use of comments; a QAPI schema file is then parsed by a python +code generation program. A valid QAPI schema consists of a series of +top-level expressions, with no commas between them. Where +dictionaries (JSON objects) are used, they are parsed as python +OrderedDicts so that ordering is preserved (for predictable layout of +generated C structs and parameter lists). Ordering doesn't matter +between top-level expressions or the keys within an expression, but +does matter within dictionary values for 'data' and 'returns' members +of a single expression. QAPI schema input is written using 'single +quotes' instead of JSON's "double quotes" (in contrast, Client JSON +Protocol uses no comments, and while input accepts 'single quotes' as +an extension, output is strict JSON using only "double quotes"). As +in JSON, trailing commas are not permitted in arrays or dictionaries. +Input must be ASCII (although QMP supports full Unicode strings, the +QAPI parser does not). At present, there is no place where a QAPI +schema requires the use of JSON numbers or null. + +Comments are allowed; anything between an unquoted # and the following +newline is ignored. Although there is not yet a documentation +generator, a form of stylized comments has developed for consistently +documenting details about an expression and when it was added to the +schema. The documentation is delimited between two lines of ##, then +the first line names the expression, an optional overview is provided, +then individual documentation about each member of 'data' is provided, +and finally, a 'Since: x.y.z' tag lists the release that introduced +the expression. Optional fields are tagged with the phrase +'#optional', often with their default value; and extensions added +after the expression was first released are also given a '(since +x.y.z)' comment. For example: + + ## + # @BlockStats: + # + # Statistics of a virtual block device or a block backing device. + # + # @device: #optional If the stats are for a virtual block device, the name + # corresponding to the virtual block device. + # + # @stats: A @BlockDeviceStats for the device. + # + # @parent: #optional This describes the file block device if it has one. + # + # @backing: #optional This describes the backing block device if it has one. + # (Since 2.0) + # + # Since: 0.14.0 + ## + { 'struct': 'BlockStats', + 'data': {'*device': 'str', 'stats': 'BlockDeviceStats', + '*parent': 'BlockStats', + '*backing': 'BlockStats'} } + +The schema sets up a series of types, as well as commands and events +that will use those types. Forward references are allowed: the parser +scans in two passes, where the first pass learns all type names, and +the second validates the schema and generates the code. This allows +the definition of complex structs that can have mutually recursive +types, and allows for indefinite nesting of Client JSON Protocol that +satisfies the schema. A type name should not be defined more than +once. It is permissible for the schema to contain additional types +not used by any commands or events in the Client JSON Protocol, for +the side effect of generated C code used internally. + +There are seven top-level expressions recognized by the parser: +'include', 'command', 'struct', 'enum', 'union', 'alternate', and +'event'. There are several groups of types: simple types (a number of +built-in types, such as 'int' and 'str'; as well as enumerations), +complex types (structs and two flavors of unions), and alternate types +(a choice between other types). The 'command' and 'event' expressions +can refer to existing types by name, or list an anonymous type as a +dictionary. Listing a type name inside an array refers to a +single-dimension array of that type; multi-dimension arrays are not +directly supported (although an array of a complex struct that +contains an array member is possible). + +Types, commands, and events share a common namespace. Therefore, +generally speaking, type definitions should always use CamelCase for +user-defined type names, while built-in types are lowercase. Type +definitions should not end in 'Kind', as this namespace is used for +creating implicit C enums for visiting union types. Command names, +and field names within a type, should be all lower case with words +separated by a hyphen. However, some existing older commands and +complex types use underscore; when extending such expressions, +consistency is preferred over blindly avoiding underscore. Event +names should be ALL_CAPS with words separated by underscore. The +special string '**' appears for some commands that manually perform +their own type checking rather than relying on the type-safe code +produced by the qapi code generators. + +Any name (command, event, type, field, or enum value) beginning with +"x-" is marked experimental, and may be withdrawn or changed +incompatibly in a future release. Downstream vendors may add +extensions; such extensions should begin with a prefix matching +"__RFQDN_" (for the reverse-fully-qualified-domain-name of the +vendor), even if the rest of the name uses dash (example: +__com.redhat_drive-mirror). Other than downstream extensions (with +leading underscore and the use of dots), all names should begin with a +letter, and contain only ASCII letters, digits, dash, and underscore. +It is okay to reuse names that match C keywords; the generator will +rename a field named "default" in the QAPI to "q_default" in the +generated C code. + +In the rest of this document, usage lines are given for each +expression type, with literal strings written in lower case and +placeholders written in capitals. If a literal string includes a +prefix of '*', that key/value pair can be omitted from the expression. +For example, a usage statement that includes '*base':STRUCT-NAME +means that an expression has an optional key 'base', which if present +must have a value that forms a struct name. + + +=== Built-in Types === + +The following types are built-in to the parser: + 'str' - arbitrary UTF-8 string + 'int' - 64-bit signed integer (although the C code may place further + restrictions on acceptable range) + 'number' - floating point number + 'bool' - JSON value of true or false + 'int8', 'int16', 'int32', 'int64' - like 'int', but enforce maximum + bit size + 'uint8', 'uint16', 'uint32', 'uint64' - unsigned counterparts + 'size' - like 'uint64', but allows scaled suffix from command line + visitor + + +=== Includes === + +Usage: { 'include': STRING } + +The QAPI schema definitions can be modularized using the 'include' directive: + + { 'include': 'path/to/file.json' } + +The directive is evaluated recursively, and include paths are relative to the +file using the directive. Multiple includes of the same file are +safe. No other keys should appear in the expression, and the include +value should be a string. + +As a matter of style, it is a good idea to have all files be +self-contained, but at the moment, nothing prevents an included file +from making a forward reference to a type that is only introduced by +an outer file. The parser may be made stricter in the future to +prevent incomplete include files. + + +=== Struct types === + +Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME } + +A struct is a dictionary containing a single 'data' key whose +value is a dictionary. This corresponds to a struct in C or an Object +in JSON. Each value of the 'data' dictionary must be the name of a +type, or a one-element array containing a type name. An example of a +struct is: + + { 'struct': 'MyType', + 'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } } + +The use of '*' as a prefix to the name means the member is optional in +the corresponding JSON protocol usage. + +The default initialization value of an optional argument should not be changed +between versions of QEMU unless the new default maintains backward +compatibility to the user-visible behavior of the old default. + +With proper documentation, this policy still allows some flexibility; for +example, documenting that a default of 0 picks an optimal buffer size allows +one release to declare the optimal size at 512 while another release declares +the optimal size at 4096 - the user-visible behavior is not the bytes used by +the buffer, but the fact that the buffer was optimal size. + +On input structures (only mentioned in the 'data' side of a command), changing +from mandatory to optional is safe (older clients will supply the option, and +newer clients can benefit from the default); changing from optional to +mandatory is backwards incompatible (older clients may be omitting the option, +and must continue to work). + +On output structures (only mentioned in the 'returns' side of a command), +changing from mandatory to optional is in general unsafe (older clients may be +expecting the field, and could crash if it is missing), although it can be done +if the only way that the optional argument will be omitted is when it is +triggered by the presence of a new input flag to the command that older clients +don't know to send. Changing from optional to mandatory is safe. + +A structure that is used in both input and output of various commands +must consider the backwards compatibility constraints of both directions +of use. + +A struct definition can specify another struct as its base. +In this case, the fields of the base type are included as top-level fields +of the new struct's dictionary in the Client JSON Protocol wire +format. An example definition is: + + { 'struct': 'BlockdevOptionsGenericFormat', 'data': { 'file': 'str' } } + { 'struct': 'BlockdevOptionsGenericCOWFormat', + 'base': 'BlockdevOptionsGenericFormat', + 'data': { '*backing': 'str' } } + +An example BlockdevOptionsGenericCOWFormat object on the wire could use +both fields like this: + + { "file": "/some/place/my-image", + "backing": "/some/place/my-backing-file" } + + +=== Enumeration types === + +Usage: { 'enum': STRING, 'data': ARRAY-OF-STRING } + +An enumeration type is a dictionary containing a single 'data' key +whose value is a list of strings. An example enumeration is: + + { 'enum': 'MyEnum', 'data': [ 'value1', 'value2', 'value3' ] } + +Nothing prevents an empty enumeration, although it is probably not +useful. The list of strings should be lower case; if an enum name +represents multiple words, use '-' between words. The string 'max' is +not allowed as an enum value, and values should not be repeated. + +The enumeration values are passed as strings over the Client JSON +Protocol, but are encoded as C enum integral values in generated code. +While the C code starts numbering at 0, it is better to use explicit +comparisons to enum values than implicit comparisons to 0; the C code +will also include a generated enum member ending in _MAX for tracking +the size of the enum, useful when using common functions for +converting between strings and enum values. Since the wire format +always passes by name, it is acceptable to reorder or add new +enumeration members in any location without breaking clients of Client +JSON Protocol; however, removing enum values would break +compatibility. For any struct that has a field that will only contain +a finite set of string values, using an enum type for that field is +better than open-coding the field to be type 'str'. + + +=== Union types === + +Usage: { 'union': STRING, 'data': DICT } +or: { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME, + 'discriminator': ENUM-MEMBER-OF-BASE } + +Union types are used to let the user choose between several different +variants for an object. There are two flavors: simple (no +discriminator or base), flat (both discriminator and base). A union +type is defined using a data dictionary as explained in the following +paragraphs. + +A simple union type defines a mapping from automatic discriminator +values to data types like in this example: + + { 'struct': 'FileOptions', 'data': { 'filename': 'str' } } + { 'struct': 'Qcow2Options', + 'data': { 'backing-file': 'str', 'lazy-refcounts': 'bool' } } + + { 'union': 'BlockdevOptions', + 'data': { 'file': 'FileOptions', + 'qcow2': 'Qcow2Options' } } + +In the Client JSON Protocol, a simple union is represented by a +dictionary that contains the 'type' field as a discriminator, and a +'data' field that is of the specified data type corresponding to the +discriminator value, as in these examples: + + { "type": "file", "data" : { "filename": "/some/place/my-image" } } + { "type": "qcow2", "data" : { "backing-file": "/some/place/my-image", + "lazy-refcounts": true } } + +The generated C code uses a struct containing a union. Additionally, +an implicit C enum 'NameKind' is created, corresponding to the union +'Name', for accessing the various branches of the union. No branch of +the union can be named 'max', as this would collide with the implicit +enum. The value for each branch can be of any type. + + +A flat union definition specifies a struct as its base, and +avoids nesting on the wire. All branches of the union must be +complex types, and the top-level fields of the union dictionary on +the wire will be combination of fields from both the base type and the +appropriate branch type (when merging two dictionaries, there must be +no keys in common). The 'discriminator' field must be the name of an +enum-typed member of the base struct. + +The following example enhances the above simple union example by +adding a common field 'readonly', renaming the discriminator to +something more applicable, and reducing the number of {} required on +the wire: + + { 'enum': 'BlockdevDriver', 'data': [ 'raw', 'qcow2' ] } + { 'struct': 'BlockdevCommonOptions', + 'data': { 'driver': 'BlockdevDriver', 'readonly': 'bool' } } + { 'union': 'BlockdevOptions', + 'base': 'BlockdevCommonOptions', + 'discriminator': 'driver', + 'data': { 'file': 'FileOptions', + 'qcow2': 'Qcow2Options' } } + +Resulting in these JSON objects: + + { "driver": "file", "readonly": true, + "filename": "/some/place/my-image" } + { "driver": "qcow2", "readonly": false, + "backing-file": "/some/place/my-image", "lazy-refcounts": true } + +Notice that in a flat union, the discriminator name is controlled by +the user, but because it must map to a base member with enum type, the +code generator can ensure that branches exist for all values of the +enum (although the order of the keys need not match the declaration of +the enum). In the resulting generated C data types, a flat union is +represented as a struct with the base member fields included directly, +and then a union of structures for each branch of the struct. + +A simple union can always be re-written as a flat union where the base +class has a single member named 'type', and where each branch of the +union has a struct with a single member named 'data'. That is, + + { 'union': 'Simple', 'data': { 'one': 'str', 'two': 'int' } } + +is identical on the wire to: + + { 'enum': 'Enum', 'data': ['one', 'two'] } + { 'struct': 'Base', 'data': { 'type': 'Enum' } } + { 'struct': 'Branch1', 'data': { 'data': 'str' } } + { 'struct': 'Branch2', 'data': { 'data': 'int' } } + { 'union': 'Flat': 'base': 'Base', 'discriminator': 'type', + 'data': { 'one': 'Branch1', 'two': 'Branch2' } } + + +=== Alternate types === + +Usage: { 'alternate': STRING, 'data': DICT } + +An alternate type is one that allows a choice between two or more JSON +data types (string, integer, number, or object, but currently not +array) on the wire. The definition is similar to a simple union type, +where each branch of the union names a QAPI type. For example: + + { 'alternate': 'BlockRef', + 'data': { 'definition': 'BlockdevOptions', + 'reference': 'str' } } + +Just like for a simple union, an implicit C enum 'NameKind' is created +to enumerate the branches for the alternate 'Name'. + +Unlike a union, the discriminator string is never passed on the wire +for the Client JSON Protocol. Instead, the value's JSON type serves +as an implicit discriminator, which in turn means that an alternate +can only express a choice between types represented differently in +JSON. If a branch is typed as the 'bool' built-in, the alternate +accepts true and false; if it is typed as any of the various numeric +built-ins, it accepts a JSON number; if it is typed as a 'str' +built-in or named enum type, it accepts a JSON string; and if it is +typed as a complex type (struct or union), it accepts a JSON object. +Two different complex types, for instance, aren't permitted, because +both are represented as a JSON object. + +The example alternate declaration above allows using both of the +following example objects: + + { "file": "my_existing_block_device_id" } + { "file": { "driver": "file", + "readonly": false, + "filename": "/tmp/mydisk.qcow2" } } + + +=== Commands === + +Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, + '*returns': TYPE-NAME-OR-DICT, + '*gen': false, '*success-response': false } + +Commands are defined by using a dictionary containing several members, +where three members are most common. The 'command' member is a +mandatory string, and determines the "execute" value passed in a +Client JSON Protocol command exchange. + +The 'data' argument maps to the "arguments" dictionary passed in as +part of a Client JSON Protocol command. The 'data' member is optional +and defaults to {} (an empty dictionary). If present, it must be the +string name of a complex type, a one-element array containing the name +of a complex type, or a dictionary that declares an anonymous type +with the same semantics as a 'struct' expression, with one exception +noted below when 'gen' is used. + +The 'returns' member describes what will appear in the "return" field +of a Client JSON Protocol reply on successful completion of a command. +The member is optional from the command declaration; if absent, the +"return" field will be an empty dictionary. If 'returns' is present, +it must be the string name of a complex or built-in type, a +one-element array containing the name of a complex or built-in type, +or a dictionary that declares an anonymous type with the same +semantics as a 'struct' expression, with one exception noted below +when 'gen' is used. Although it is permitted to have the 'returns' +member name a built-in type or an array of built-in types, any command +that does this cannot be extended to return additional information in +the future; thus, new commands should strongly consider returning a +dictionary-based type or an array of dictionaries, even if the +dictionary only contains one field at the present. + +All commands in Client JSON Protocol use a dictionary to report +failure, with no way to specify that in QAPI. Where the error return +is different than the usual GenericError class in order to help the +client react differently to certain error conditions, it is worth +documenting this in the comments before the command declaration. + +Some example commands: + + { 'command': 'my-first-command', + 'data': { 'arg1': 'str', '*arg2': 'str' } } + { 'struct': 'MyType', 'data': { '*value': 'str' } } + { 'command': 'my-second-command', + 'returns': [ 'MyType' ] } + +which would validate this Client JSON Protocol transaction: + + => { "execute": "my-first-command", + "arguments": { "arg1": "hello" } } + <= { "return": { } } + => { "execute": "my-second-command" } + <= { "return": [ { "value": "one" }, { } ] } + +In rare cases, QAPI cannot express a type-safe representation of a +corresponding Client JSON Protocol command. In these cases, if the +command expression includes the key 'gen' with boolean value false, +then the 'data' or 'returns' member that intends to bypass generated +type-safety and do its own manual validation should use an inline +dictionary definition, with a value of '**' rather than a valid type +name for the keys that the generated code will not validate. Please +try to avoid adding new commands that rely on this, and instead use +type-safe unions. For an example of bypass usage: + + { 'command': 'netdev_add', + 'data': {'type': 'str', 'id': 'str', '*props': '**'}, + 'gen': false } + +Normally, the QAPI schema is used to describe synchronous exchanges, +where a response is expected. But in some cases, the action of a +command is expected to change state in a way that a successful +response is not possible (although the command will still return a +normal dictionary error on failure). When a successful reply is not +possible, the command expression should include the optional key +'success-response' with boolean value false. So far, only QGA makes +use of this field. + + +=== Events === + +Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT } + +Events are defined with the keyword 'event'. It is not allowed to +name an event 'MAX', since the generator also produces a C enumeration +of all event names with a generated _MAX value at the end. When +'data' is also specified, additional info will be included in the +event, with similar semantics to a 'struct' expression. Finally there +will be C API generated in qapi-event.h; when called by QEMU code, a +message with timestamp will be emitted on the wire. + +An example event is: + +{ 'event': 'EVENT_C', + 'data': { '*a': 'int', 'b': 'str' } } + +Resulting in this JSON object: + +{ "event": "EVENT_C", + "data": { "b": "test string" }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } + + +== Code generation == + +Schemas are fed into 3 scripts to generate all the code/files that, paired +with the core QAPI libraries, comprise everything required to take JSON +commands read in by a Client JSON Protocol server, unmarshal the arguments into +the underlying C types, call into the corresponding C function, and map the +response back to a Client JSON Protocol response to be returned to the user. + +As an example, we'll use the following schema, which describes a single +complex user-defined type (which will produce a C struct, along with a list +node structure that can be used to chain together a list of such types in +case we want to accept/return a list of this type with a command), and a +command which takes that type as a parameter and returns the same type: + + $ cat example-schema.json + { 'struct': 'UserDefOne', + 'data': { 'integer': 'int', 'string': 'str' } } + + { 'command': 'my-command', + 'data': {'arg1': 'UserDefOne'}, + 'returns': 'UserDefOne' } + + { 'event': 'MY_EVENT' } + +=== scripts/qapi-types.py === + +Used to generate the C types defined by a schema. The following files are +created: + +$(prefix)qapi-types.h - C types corresponding to types defined in + the schema you pass in +$(prefix)qapi-types.c - Cleanup functions for the above C types + +The $(prefix) is an optional parameter used as a namespace to keep the +generated code from one schema/code-generation separated from others so code +can be generated/used from multiple schemas without clobbering previously +created code. + +Example: + + $ python scripts/qapi-types.py --output-dir="qapi-generated" \ + --prefix="example-" example-schema.json + $ cat qapi-generated/example-qapi-types.c +[Uninteresting stuff omitted...] + + void qapi_free_UserDefOneList(UserDefOneList *obj) + { + QapiDeallocVisitor *md; + Visitor *v; + + if (!obj) { + return; + } + + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOneList(v, &obj, NULL, NULL); + qapi_dealloc_visitor_cleanup(md); + } + + void qapi_free_UserDefOne(UserDefOne *obj) + { + QapiDeallocVisitor *md; + Visitor *v; + + if (!obj) { + return; + } + + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &obj, NULL, NULL); + qapi_dealloc_visitor_cleanup(md); + } + + $ cat qapi-generated/example-qapi-types.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QAPI_TYPES_H + #define EXAMPLE_QAPI_TYPES_H + +[Built-in types omitted...] + + typedef struct UserDefOne UserDefOne; + + typedef struct UserDefOneList + { + union { + UserDefOne *value; + uint64_t padding; + }; + struct UserDefOneList *next; + } UserDefOneList; + +[Functions on built-in types omitted...] + + struct UserDefOne + { + int64_t integer; + char *string; + }; + + void qapi_free_UserDefOneList(UserDefOneList *obj); + void qapi_free_UserDefOne(UserDefOne *obj); + + #endif + +=== scripts/qapi-visit.py === + +Used to generate the visitor functions used to walk through and convert +a QObject (as provided by QMP) to a native C data structure and +vice-versa, as well as the visitor function used to dealloc a complex +schema-defined C type. + +The following files are generated: + +$(prefix)qapi-visit.c: visitor function for a particular C type, used + to automagically convert QObjects into the + corresponding C type and vice-versa, as well + as for deallocating memory for an existing C + type + +$(prefix)qapi-visit.h: declarations for previously mentioned visitor + functions + +Example: + + $ python scripts/qapi-visit.py --output-dir="qapi-generated" + --prefix="example-" example-schema.json + $ cat qapi-generated/example-qapi-visit.c +[Uninteresting stuff omitted...] + + static void visit_type_UserDefOne_fields(Visitor *m, UserDefOne **obj, Error **errp) + { + Error *err = NULL; + visit_type_int(m, &(*obj)->integer, "integer", &err); + if (err) { + goto out; + } + visit_type_str(m, &(*obj)->string, "string", &err); + if (err) { + goto out; + } + + out: + error_propagate(errp, err); + } + + void visit_type_UserDefOne(Visitor *m, UserDefOne **obj, const char *name, Error **errp) + { + Error *err = NULL; + + visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err); + if (!err) { + if (*obj) { + visit_type_UserDefOne_fields(m, obj, errp); + } + visit_end_struct(m, &err); + } + error_propagate(errp, err); + } + + void visit_type_UserDefOneList(Visitor *m, UserDefOneList **obj, const char *name, Error **errp) + { + Error *err = NULL; + GenericList *i, **prev; + + visit_start_list(m, name, &err); + if (err) { + goto out; + } + + for (prev = (GenericList **)obj; + !err && (i = visit_next_list(m, prev, &err)) != NULL; + prev = &i) { + UserDefOneList *native_i = (UserDefOneList *)i; + visit_type_UserDefOne(m, &native_i->value, NULL, &err); + } + + error_propagate(errp, err); + err = NULL; + visit_end_list(m, &err); + out: + error_propagate(errp, err); + } + $ python scripts/qapi-commands.py --output-dir="qapi-generated" \ + --prefix="example-" example-schema.json + $ cat qapi-generated/example-qapi-visit.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QAPI_VISIT_H + #define EXAMPLE_QAPI_VISIT_H + +[Visitors for built-in types omitted...] + + void visit_type_UserDefOne(Visitor *m, UserDefOne **obj, const char *name, Error **errp); + void visit_type_UserDefOneList(Visitor *m, UserDefOneList **obj, const char *name, Error **errp); + + #endif + +=== scripts/qapi-commands.py === + +Used to generate the marshaling/dispatch functions for the commands defined +in the schema. The following files are generated: + +$(prefix)qmp-marshal.c: command marshal/dispatch functions for each + QMP command defined in the schema. Functions + generated by qapi-visit.py are used to + convert QObjects received from the wire into + function parameters, and uses the same + visitor functions to convert native C return + values to QObjects from transmission back + over the wire. + +$(prefix)qmp-commands.h: Function prototypes for the QMP commands + specified in the schema. + +Example: + + $ python scripts/qapi-commands.py --output-dir="qapi-generated" + --prefix="example-" example-schema.json + $ cat qapi-generated/example-qmp-marshal.c +[Uninteresting stuff omitted...] + + static void qmp_marshal_output_my_command(UserDefOne *ret_in, QObject **ret_out, Error **errp) + { + Error *local_err = NULL; + QmpOutputVisitor *mo = qmp_output_visitor_new(); + QapiDeallocVisitor *md; + Visitor *v; + + v = qmp_output_get_visitor(mo); + visit_type_UserDefOne(v, &ret_in, "unused", &local_err); + if (local_err) { + goto out; + } + *ret_out = qmp_output_get_qobject(mo); + + out: + error_propagate(errp, local_err); + qmp_output_visitor_cleanup(mo); + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &ret_in, "unused", NULL); + qapi_dealloc_visitor_cleanup(md); + } + + static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp) + { + Error *local_err = NULL; + UserDefOne *retval = NULL; + QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); + QapiDeallocVisitor *md; + Visitor *v; + UserDefOne *arg1 = NULL; + + v = qmp_input_get_visitor(mi); + visit_type_UserDefOne(v, &arg1, "arg1", &local_err); + if (local_err) { + goto out; + } + + retval = qmp_my_command(arg1, &local_err); + if (local_err) { + goto out; + } + + qmp_marshal_output_my_command(retval, ret, &local_err); + + out: + error_propagate(errp, local_err); + qmp_input_visitor_cleanup(mi); + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &arg1, "arg1", NULL); + qapi_dealloc_visitor_cleanup(md); + return; + } + + static void qmp_init_marshal(void) + { + qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS); + } + + qapi_init(qmp_init_marshal); + $ cat qapi-generated/example-qmp-commands.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QMP_COMMANDS_H + #define EXAMPLE_QMP_COMMANDS_H + + #include "example-qapi-types.h" + #include "qapi/qmp/qdict.h" + #include "qapi/error.h" + + UserDefOne *qmp_my_command(UserDefOne *arg1, Error **errp); + + #endif + +=== scripts/qapi-event.py === + +Used to generate the event-related C code defined by a schema. The +following files are created: + +$(prefix)qapi-event.h - Function prototypes for each event type, plus an + enumeration of all event names +$(prefix)qapi-event.c - Implementation of functions to send an event + +Example: + + $ python scripts/qapi-event.py --output-dir="qapi-generated" + --prefix="example-" example-schema.json + $ cat qapi-generated/example-qapi-event.c +[Uninteresting stuff omitted...] + + void qapi_event_send_my_event(Error **errp) + { + QDict *qmp; + Error *local_err = NULL; + QMPEventFuncEmit emit; + emit = qmp_event_get_func_emit(); + if (!emit) { + return; + } + + qmp = qmp_event_build_dict("MY_EVENT"); + + emit(EXAMPLE_QAPI_EVENT_MY_EVENT, qmp, &local_err); + + error_propagate(errp, local_err); + QDECREF(qmp); + } + + const char *EXAMPLE_QAPIEvent_lookup[] = { + "MY_EVENT", + NULL, + }; + $ cat qapi-generated/example-qapi-event.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QAPI_EVENT_H + #define EXAMPLE_QAPI_EVENT_H + + #include "qapi/error.h" + #include "qapi/qmp/qdict.h" + #include "example-qapi-types.h" + + + void qapi_event_send_my_event(Error **errp); + + extern const char *EXAMPLE_QAPIEvent_lookup[]; + typedef enum EXAMPLE_QAPIEvent + { + EXAMPLE_QAPI_EVENT_MY_EVENT = 0, + EXAMPLE_QAPI_EVENT_MAX = 1, + } EXAMPLE_QAPIEvent; + + #endif diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt new file mode 100644 index 0000000..136d271 --- /dev/null +++ b/docs/qdev-device-use.txt @@ -0,0 +1,416 @@ += How to convert to -device & friends = + +=== Specifying Bus and Address on Bus === + +In qdev, each device has a parent bus. Some devices provide one or +more buses for children. You can specify a device's parent bus with +-device parameter bus. + +A device typically has a device address on its parent bus. For buses +where this address can be configured, devices provide a bus-specific +property. Examples: + + bus property name value format + PCI addr %x.%x (dev.fn, .fn optional) + I2C address %u + SCSI scsi-id %u + IDE unit %u + HDA cad %u + virtio-serial-bus nr %u + ccid-bus slot %u + USB port %d(.%d)* (port.port...) + +Example: device i440FX-pcihost is on the root bus, and provides a PCI +bus named pci.0. To put a FOO device into its slot 4, use -device +FOO,bus=/i440FX-pcihost/pci.0,addr=4. The abbreviated form bus=pci.0 +also works as long as the bus name is unique. + +=== Block Devices === + +A QEMU block device (drive) has a host and a guest part. + +In the general case, the guest device is connected to a controller +device. For instance, the IDE controller provides two IDE buses, each +of which can have up to two ide-drive devices, and each ide-drive +device is a guest part, and is connected to a host part. + +Except we sometimes lump controller, bus(es) and drive device(s) all +together into a single device. For instance, the ISA floppy +controller is connected to up to two host drives. + +The old ways to define block devices define host and guest part +together. Sometimes, they can even define a controller device in +addition to the block device. + +The new way keeps the parts separate: you create the host part with +-drive, and guest device(s) with -device. + +The various old ways to define drives all boil down to the common form + + -drive if=TYPE,bus=BUS,unit=UNIT,OPTS... + +TYPE, BUS and UNIT identify the controller device, which of its buses +to use, and the drive's address on that bus. Details depend on TYPE. + +Instead of bus=BUS,unit=UNIT, you can also say index=IDX. + +In the new way, this becomes something like + + -drive if=none,id=DRIVE-ID,HOST-OPTS... + -device DEVNAME,drive=DRIVE-ID,DEV-OPTS... + +The old OPTS get split into HOST-OPTS and DEV-OPTS as follows: + +* file, format, snapshot, cache, aio, readonly, rerror, werror go into + HOST-OPTS. + +* cyls, head, secs and trans go into HOST-OPTS. Future work: they + should go into DEV-OPTS instead. + +* serial goes into DEV-OPTS, for devices supporting serial numbers. + For other devices, it goes nowhere. + +* media is special. In the old way, it selects disk vs. CD-ROM with + if=ide, if=scsi and if=xen. The new way uses DEVNAME for that. + Additionally, readonly=on goes into HOST-OPTS. + +* addr is special, see if=virtio below. + +The -device argument differs in detail for each type of drive: + +* if=ide + + -device DEVNAME,drive=DRIVE-ID,bus=IDE-BUS,unit=UNIT + + where DEVNAME is either ide-hd or ide-cd, IDE-BUS identifies an IDE + bus, normally either ide.0 or ide.1, and UNIT is either 0 or 1. + +* if=scsi + + The old way implicitly creates SCSI controllers as needed. The new + way makes that explicit: + + -device lsi53c895a,id=ID + + As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to + control the PCI device address. + + This SCSI controller provides a single SCSI bus, named ID.0. Put a + disk on it: + + -device DEVNAME,drive=DRIVE-ID,bus=ID.0,scsi-id=UNIT + + where DEVNAME is either scsi-hd, scsi-cd or scsi-generic. + +* if=floppy + + -global isa-fdc.driveA=DRIVE-ID + -global isa-fdc.driveB=DRIVE-ID + + This is -global instead of -device, because the floppy controller is + created automatically, and we want to configure that one, not create + a second one (which isn't possible anyway). + + Without any -global isa-fdc,... you get an empty driveA and no + driveB. You can use -nodefaults to suppress the default driveA, see + "Default Devices". + +* if=virtio + + -device virtio-blk-pci,drive=DRIVE-ID,class=C,vectors=V,ioeventfd=IOEVENTFD + + This lets you control PCI device class and MSI-X vectors. + + IOEVENTFD controls whether or not ioeventfd is used for virtqueue + notify. It can be set to on (default) or off. + + As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to + control the PCI device address. This replaces option addr available + with -drive if=virtio. + +* if=pflash, if=mtd, if=sd, if=xen are not yet available with -device + +For USB devices, the old way is actually different: + + -usbdevice disk:format=FMT:FILENAME + +Provides much less control than -drive's OPTS... The new way fixes +that: + + -device usb-storage,drive=DRIVE-ID,removable=RMB + +The removable parameter gives control over the SCSI INQUIRY removable +(RMB) bit. USB thumbdrives usually set removable=on, while USB hard +disks set removable=off. + +Bug: usb-storage pretends to be a block device, but it's really a SCSI +controller that can serve only a single device, which it creates +automatically. The automatic creation guesses what kind of guest part +to create from the host part, like -drive if=scsi. Host and guest +part are not cleanly separated. + +=== Character Devices === + +A QEMU character device has a host and a guest part. + +The old ways to define character devices define host and guest part +together. + +The new way keeps the parts separate: you create the host part with +-chardev, and the guest device with -device. + +The various old ways to define a character device are all of the +general form + + -FOO FOO-OPTS...,LEGACY-CHARDEV + +where FOO-OPTS... is specific to -FOO, and the host part +LEGACY-CHARDEV is the same everywhere. + +In the new way, this becomes + + -chardev HOST-OPTS...,id=CHR-ID + -device DEVNAME,chardev=CHR-ID,DEV-OPTS... + +The appropriate DEVNAME depends on the machine type. For type "pc": + +* -serial becomes -device isa-serial,iobase=IOADDR,irq=IRQ,index=IDX + + This lets you control I/O ports and IRQs. + +* -parallel becomes -device isa-parallel,iobase=IOADDR,irq=IRQ,index=IDX + + This lets you control I/O ports and IRQs. + +* -usbdevice serial:vendorid=VID,productid=PRID becomes + -device usb-serial,vendorid=VID,productid=PRID + +* -usbdevice braille doesn't support LEGACY-CHARDEV syntax. It always + uses "braille". With -device, this useful default is gone, so you + have to use something like + + -device usb-braille,chardev=braille,vendorid=VID,productid=PRID + -chardev braille,id=braille + +* -virtioconsole becomes + -device virtio-serial-pci,class=C,vectors=V,ioeventfd=IOEVENTFD,max_ports=N + -device virtconsole,is_console=NUM,nr=NR,name=NAME + +LEGACY-CHARDEV translates to -chardev HOST-OPTS... as follows: + +* null becomes -chardev null + +* pty, msmouse, braille, stdio likewise + +* vc:WIDTHxHEIGHT becomes -chardev vc,width=WIDTH,height=HEIGHT + +* vc:CxC becomes -chardev vc,cols=,rows= + +* con: becomes -chardev console + +* COM becomes -chardev serial,path=COM + +* file:FNAME becomes -chardev file,path=FNAME + +* pipe:FNAME becomes -chardev pipe,path=FNAME + +* tcp:HOST:PORT,OPTS... becomes -chardev socket,host=HOST,port=PORT,OPTS... + +* telnet:HOST:PORT,OPTS... becomes + -chardev socket,host=HOST,port=PORT,OPTS...,telnet=on + +* udp:HOST:PORT@LOCALADDR:LOCALPORT becomes + -chardev udp,host=HOST,port=PORT,localaddr=LOCALADDR,localport=LOCALPORT + +* unix:FNAME becomes -chardev socket,path=FNAME + +* /dev/parportN becomes -chardev parport,file=/dev/parportN + +* /dev/ppiN likewise + +* Any other /dev/FNAME becomes -chardev tty,path=/dev/FNAME + +* mon:LEGACY-CHARDEV is special: it multiplexes the monitor onto the + character device defined by LEGACY-CHARDEV. -chardev provides more + general multiplexing instead: you can connect up to four users to a + single host part. You need to pass mux=on to -chardev to enable + switching the input focus. + +QEMU uses LEGACY-CHARDEV syntax not just to set up guest devices, but +also in various other places such as -monitor or -net +user,guestfwd=... You can use chardev:CHR-ID in place of +LEGACY-CHARDEV to refer to a host part defined with -chardev. + +=== Network Devices === + +Host and guest part of network devices have always been separate. + +The old way to define the guest part looks like this: + + -net nic,netdev=NET-ID,macaddr=MACADDR,model=MODEL,name=ID,addr=STR,vectors=V + +Except for USB it looks like this: + + -usbdevice net:netdev=NET-ID,macaddr=MACADDR,name=ID + +The new way is -device: + + -device DEVNAME,netdev=NET-ID,mac=MACADDR,DEV-OPTS... + +DEVNAME equals MODEL, except for virtio you have to name the virtio +device appropriate for the bus (virtio-net-pci for PCI), and for USB +you have to use usb-net. + +The old name=ID parameter becomes the usual id=ID with -device. + +For PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI +device address, as usual. The old -net nic provides parameter addr +for that, which is silently ignored when the NIC is not a PCI device. + +For virtio-net-pci, you can control whether or not ioeventfd is used for +virtqueue notify by setting ioeventfd= to on or off (default). + +-net nic accepts vectors=V for all models, but it's silently ignored +except for virtio-net-pci (model=virtio). With -device, only devices +that support it accept it. + +Not all devices are available with -device at this time. All PCI +devices and ne2k_isa are. + +Some PCI devices aren't available with -net nic, e.g. i82558a. + +To connect to a VLAN instead of an ordinary host part, replace +netdev=NET-ID by vlan=VLAN. + +=== Graphics Devices === + +Host and guest part of graphics devices have always been separate. + +The old way to define the guest graphics device is -vga VGA. Not all +machines support all -vga options. + +The new way is -device. The mapping from -vga argument to -device +depends on the machine type. For machine "pc", it's: + + std -device VGA + cirrus -device cirrus-vga + vmware -device vmware-svga + qxl -device qxl-vga + none -nodefaults + disables more than just VGA, see "Default Devices" + +As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control +the PCI device address. + +-device VGA supports properties bios-offset and bios-size, but they +aren't used with machine type "pc". + +For machine "isapc", it's + + std -device isa-vga + cirrus not yet available with -device + none -nodefaults + disables more than just VGA, see "Default Devices" + +Bug: the new way doesn't work for machine types "pc" and "isapc", +because it violates obscure device initialization ordering +constraints. + +=== Audio Devices === + +Host and guest part of audio devices have always been separate. + +The old way to define guest audio devices is -soundhw C1,... + +The new way is to define each guest audio device separately with +-device. + +Map from -soundhw sound card name to -device: + + ac97 -device AC97 + cs4231a -device cs4231a,iobase=IOADDR,irq=IRQ,dma=DMA + es1370 -device ES1370 + gus -device gus,iobase=IOADDR,irq=IRQ,dma=DMA,freq=F + hda -device intel-hda,msi=MSI -device hda-duplex + sb16 -device sb16,iobase=IOADDR,irq=IRQ,dma=DMA,dma16=DMA16,version=V + adlib not yet available with -device + pcspk not yet available with -device + +For PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI +device address, as usual. + +=== USB Devices === + +The old way to define a virtual USB device is -usbdevice DRIVER:OPTS... + +The new way is -device DEVNAME,DEV-OPTS... Details depend on DRIVER: + +* ccid -device usb-ccid +* keyboard -device usb-kbd +* mouse -device usb-mouse +* tablet -device usb-tablet +* wacom-tablet -device usb-wacom-tablet +* host:... See "Host Device Assignment" +* disk:... See "Block Devices" +* serial:... See "Character Devices" +* braille See "Character Devices" +* net:... See "Network Devices" +* bt:... not yet available with -device + +=== Watchdog Devices === + +Host and guest part of watchdog devices have always been separate. + +The old way to define a guest watchdog device is -watchdog DEVNAME. +The new way is -device DEVNAME. For PCI devices, you can add +bus=PCI-BUS,addr=DEVFN to control the PCI device address, as usual. + +=== Host Device Assignment === + +QEMU supports assigning host PCI devices (qemu-kvm only at this time) +and host USB devices. + +The old way to assign a host PCI device is + + -pcidevice host=ADDR,dma=none,id=ID + +The new way is + + -device pci-assign,host=ADDR,iommu=IOMMU,id=ID + +The old dma=none becomes iommu=off with -device. + +The old way to assign a host USB device is + + -usbdevice host:auto:BUS.ADDR:VID:PRID + +where any of BUS, ADDR, VID, PRID can be the wildcard *. + +The new way is + + -device usb-host,hostbus=BUS,hostaddr=ADDR,vendorid=VID,productid=PRID + +Omitted options match anything, just like the old way's wildcard. + +=== Default Devices === + +QEMU creates a number of devices by default, depending on the machine +type. + +-device DEVNAME... and global DEVNAME... suppress default devices for +some DEVNAMEs: + + default device suppressing DEVNAMEs + CD-ROM ide-cd, ide-drive, scsi-cd + isa-fdc's driveA isa-fdc + parallel isa-parallel + serial isa-serial + VGA VGA, cirrus-vga, vmware-svga + virtioconsole virtio-serial-pci, virtio-serial-s390, virtio-serial + +The default NIC is connected to a default part created along with it. +It is *not* suppressed by configuring a NIC with -device (you may call +that a bug). -net and -netdev suppress the default NIC. + +-nodefaults suppresses all the default devices mentioned above, plus a +few other things such as default SD-Card drive and default monitor. diff --git a/docs/qemupciserial.inf b/docs/qemupciserial.inf new file mode 100644 index 0000000..6f7eef4 --- /dev/null +++ b/docs/qemupciserial.inf @@ -0,0 +1,102 @@ +; qemupciserial.inf for QEMU, based on MSPORTS.INF + +; The driver itself is shipped with Windows (serial.sys). This is +; just a inf file to tell windows which pci id the serial pci card +; emulated by qemu has, and to apply a name tag to it which windows +; will show in the device manager. + +; Installing the driver: Go to device manager. You should find a "pci +; serial card" tagged with a yellow question mark. Open properties. +; Pick "update driver". Then "select driver manually". Pick "Ports +; (Com+Lpt)" from the list. Click "Have a disk". Select this file. +; Procedure may vary a bit depending on the windows version. + +; This file covers all options: pci-serial, pci-serial-2x, pci-serial-4x +; for both 32 and 64 bit platforms. + +[Version] +Signature="$Windows NT$" +Class=MultiFunction +ClassGUID={4d36e971-e325-11ce-bfc1-08002be10318} +Provider=%QEMU% +DriverVer=12/29/2013,1.3.0 +[ControlFlags] +ExcludeFromSelect=* +[Manufacturer] +%QEMU%=QEMU,NTx86,NTAMD64 + +[QEMU.NTx86] +%QEMU-PCI_SERIAL_1_PORT%=ComPort_inst1, PCI\VEN_1B36&DEV_0002 +%QEMU-PCI_SERIAL_2_PORT%=ComPort_inst2, PCI\VEN_1B36&DEV_0003 +%QEMU-PCI_SERIAL_4_PORT%=ComPort_inst4, PCI\VEN_1B36&DEV_0004 + +[QEMU.NTAMD64] +%QEMU-PCI_SERIAL_1_PORT%=ComPort_inst1, PCI\VEN_1B36&DEV_0002 +%QEMU-PCI_SERIAL_2_PORT%=ComPort_inst2, PCI\VEN_1B36&DEV_0003 +%QEMU-PCI_SERIAL_4_PORT%=ComPort_inst4, PCI\VEN_1B36&DEV_0004 + +[ComPort_inst1] +Include=mf.inf +Needs=MFINSTALL.mf + +[ComPort_inst2] +Include=mf.inf +Needs=MFINSTALL.mf + +[ComPort_inst4] +Include=mf.inf +Needs=MFINSTALL.mf + +[ComPort_inst1.HW] +AddReg=ComPort_inst1.RegHW + +[ComPort_inst2.HW] +AddReg=ComPort_inst2.RegHW + +[ComPort_inst4.HW] +AddReg=ComPort_inst4.RegHW + +[ComPort_inst1.Services] +Include=mf.inf +Needs=MFINSTALL.mf.Services + +[ComPort_inst2.Services] +Include=mf.inf +Needs=MFINSTALL.mf.Services + +[ComPort_inst4.Services] +Include=mf.inf +Needs=MFINSTALL.mf.Services + +[ComPort_inst1.RegHW] +HKR,Child0000,HardwareID,,*PNP0501 +HKR,Child0000,VaryingResourceMap,1,00, 00,00,00,00, 08,00,00,00 +HKR,Child0000,ResourceMap,1,02 + +[ComPort_inst2.RegHW] +HKR,Child0000,HardwareID,,*PNP0501 +HKR,Child0000,VaryingResourceMap,1,00, 00,00,00,00, 08,00,00,00 +HKR,Child0000,ResourceMap,1,02 +HKR,Child0001,HardwareID,,*PNP0501 +HKR,Child0001,VaryingResourceMap,1,00, 08,00,00,00, 08,00,00,00 +HKR,Child0001,ResourceMap,1,02 + +[ComPort_inst4.RegHW] +HKR,Child0000,HardwareID,,*PNP0501 +HKR,Child0000,VaryingResourceMap,1,00, 00,00,00,00, 08,00,00,00 +HKR,Child0000,ResourceMap,1,02 +HKR,Child0001,HardwareID,,*PNP0501 +HKR,Child0001,VaryingResourceMap,1,00, 08,00,00,00, 08,00,00,00 +HKR,Child0001,ResourceMap,1,02 +HKR,Child0002,HardwareID,,*PNP0501 +HKR,Child0002,VaryingResourceMap,1,00, 10,00,00,00, 08,00,00,00 +HKR,Child0002,ResourceMap,1,02 +HKR,Child0003,HardwareID,,*PNP0501 +HKR,Child0003,VaryingResourceMap,1,00, 18,00,00,00, 08,00,00,00 +HKR,Child0003,ResourceMap,1,02 + +[Strings] +QEMU="QEMU" +QEMU-PCI_SERIAL_1_PORT="1x QEMU PCI Serial Card" +QEMU-PCI_SERIAL_2_PORT="2x QEMU PCI Serial Card" +QEMU-PCI_SERIAL_4_PORT="4x QEMU PCI Serial Card" diff --git a/docs/qmp/README b/docs/qmp/README new file mode 100644 index 0000000..f6a3a03 --- /dev/null +++ b/docs/qmp/README @@ -0,0 +1,87 @@ + QEMU Machine Protocol + ===================== + +Introduction +------------ + +The QEMU Machine Protocol (QMP) allows applications to operate a +QEMU instance. + +QMP is JSON[1] based and features the following: + +- Lightweight, text-based, easy to parse data format +- Asynchronous messages support (ie. events) +- Capabilities Negotiation + +For detailed information on QMP's usage, please, refer to the following files: + +o qmp-spec.txt QEMU Machine Protocol current specification +o qmp-commands.txt QMP supported commands (auto-generated at build-time) +o qmp-events.txt List of available asynchronous events + +[1] http://www.json.org + +Usage +----- + +You can use the -qmp option to enable QMP. For example, the following +makes QMP available on localhost port 4444: + +$ qemu [...] -qmp tcp:localhost:4444,server,nowait + +However, for more flexibility and to make use of more options, the -mon +command-line option should be used. For instance, the following example +creates one HMP instance (human monitor) on stdio and one QMP instance +on localhost port 4444: + +$ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \ + -chardev socket,id=mon1,host=localhost,port=4444,server,nowait \ + -mon chardev=mon1,mode=control,pretty=on + +Please, refer to QEMU's manpage for more information. + +Simple Testing +-------------- + +To manually test QMP one can connect with telnet and issue commands by hand: + +$ telnet localhost 4444 +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. +{ + "QMP": { + "version": { + "qemu": { + "micro": 50, + "minor": 6, + "major": 1 + }, + "package": "" + }, + "capabilities": [ + ] + } +} + +{ "execute": "qmp_capabilities" } +{ + "return": { + } +} + +{ "execute": "query-status" } +{ + "return": { + "status": "prelaunch", + "singlestep": false, + "running": false + } +} + +Please, refer to the qapi-schema.json file for a complete command reference. + +QMP wiki page +------------- + +http://wiki.qemu-project.org/QMP diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt new file mode 100644 index 0000000..4c13d48 --- /dev/null +++ b/docs/qmp/qmp-events.txt @@ -0,0 +1,650 @@ + QEMU Machine Protocol Events + ============================ + +ACPI_DEVICE_OST +--------------- + +Emitted when guest executes ACPI _OST method. + + - data: ACPIOSTInfo type as described in qapi-schema.json + +{ "event": "ACPI_DEVICE_OST", + "data": { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0 } } + +BALLOON_CHANGE +-------------- + +Emitted when the guest changes the actual BALLOON level. This +value is equivalent to the 'actual' field return by the +'query-balloon' command + +Data: + +- "actual": actual level of the guest memory balloon in bytes (json-number) + +Example: + +{ "event": "BALLOON_CHANGE", + "data": { "actual": 944766976 }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } + +BLOCK_IMAGE_CORRUPTED +--------------------- + +Emitted when a disk image is being marked corrupt. The image can be +identified by its device or node name. The 'device' field is always +present for compatibility reasons, but it can be empty ("") if the +image does not have a device name associated. + +Data: + +- "device": Device name (json-string) +- "node-name": Node name (json-string, optional) +- "msg": Informative message (e.g., reason for the corruption) + (json-string) +- "offset": If the corruption resulted from an image access, this + is the host's access offset into the image + (json-int, optional) +- "size": If the corruption resulted from an image access, this + is the access size (json-int, optional) + +Example: + +{ "event": "BLOCK_IMAGE_CORRUPTED", + "data": { "device": "ide0-hd0", "node-name": "node0", + "msg": "Prevented active L1 table overwrite", "offset": 196608, + "size": 65536 }, + "timestamp": { "seconds": 1378126126, "microseconds": 966463 } } + +BLOCK_IO_ERROR +-------------- + +Emitted when a disk I/O error occurs. + +Data: + +- "device": device name (json-string) +- "operation": I/O operation (json-string, "read" or "write") +- "action": action that has been taken, it's one of the following (json-string): + "ignore": error has been ignored + "report": error has been reported to the device + "stop": the VM is going to stop because of the error + +Example: + +{ "event": "BLOCK_IO_ERROR", + "data": { "device": "ide0-hd1", + "operation": "write", + "action": "stop" }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +Note: If action is "stop", a STOP event will eventually follow the +BLOCK_IO_ERROR event. + +BLOCK_JOB_CANCELLED +------------------- + +Emitted when a block job has been cancelled. + +Data: + +- "type": Job type (json-string; "stream" for image streaming + "commit" for block commit) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) + +Example: + +{ "event": "BLOCK_JOB_CANCELLED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 134217728, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +BLOCK_JOB_COMPLETED +------------------- + +Emitted when a block job has completed. + +Data: + +- "type": Job type (json-string; "stream" for image streaming + "commit" for block commit) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) +- "error": Error message (json-string, optional) + Only present on failure. This field contains a human-readable + error message. There are no semantics other than that streaming + has failed and clients should not try to interpret the error + string. + +Example: + +{ "event": "BLOCK_JOB_COMPLETED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 10737418240, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +BLOCK_JOB_ERROR +--------------- + +Emitted when a block job encounters an error. + +Data: + +- "device": device name (json-string) +- "operation": I/O operation (json-string, "read" or "write") +- "action": action that has been taken, it's one of the following (json-string): + "ignore": error has been ignored, the job may fail later + "report": error will be reported and the job canceled + "stop": error caused job to be paused + +Example: + +{ "event": "BLOCK_JOB_ERROR", + "data": { "device": "ide0-hd1", + "operation": "write", + "action": "stop" }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +BLOCK_JOB_READY +--------------- + +Emitted when a block job is ready to complete. + +Data: + +- "type": Job type (json-string; "stream" for image streaming + "commit" for block commit) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) + +Example: + +{ "event": "BLOCK_JOB_READY", + "data": { "device": "drive0", "type": "mirror", "speed": 0, + "len": 2097152, "offset": 2097152 } + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR +event. + +DEVICE_DELETED +-------------- + +Emitted whenever the device removal completion is acknowledged +by the guest. +At this point, it's safe to reuse the specified device ID. +Device removal can be initiated by the guest or by HMP/QMP commands. + +Data: + +- "device": device name (json-string, optional) +- "path": device path (json-string) + +{ "event": "DEVICE_DELETED", + "data": { "device": "virtio-net-pci-0", + "path": "/machine/peripheral/virtio-net-pci-0" }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +DEVICE_TRAY_MOVED +----------------- + +It's emitted whenever the tray of a removable device is moved by the guest +or by HMP/QMP commands. + +Data: + +- "device": device name (json-string) +- "tray-open": true if the tray has been opened or false if it has been closed + (json-bool) + +{ "event": "DEVICE_TRAY_MOVED", + "data": { "device": "ide1-cd0", + "tray-open": true + }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +GUEST_PANICKED +-------------- + +Emitted when guest OS panic is detected. + +Data: + +- "action": Action that has been taken (json-string, currently always "pause"). + +Example: + +{ "event": "GUEST_PANICKED", + "data": { "action": "pause" } } + +MEM_UNPLUG_ERROR +-------------------- +Emitted when memory hot unplug error occurs. + +Data: + +- "device": device name (json-string) +- "msg": Informative message (e.g., reason for the error) (json-string) + +Example: + +{ "event": "MEM_UNPLUG_ERROR" + "data": { "device": "dimm1", + "msg": "acpi: device unplug for unsupported device" + }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +NIC_RX_FILTER_CHANGED +--------------------- + +The event is emitted once until the query command is executed, +the first event will always be emitted. + +Data: + +- "name": net client name (json-string) +- "path": device path (json-string) + +{ "event": "NIC_RX_FILTER_CHANGED", + "data": { "name": "vnet0", + "path": "/machine/peripheral/vnet0/virtio-backend" }, + "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } +} + +POWERDOWN +--------- + +Emitted when the Virtual Machine is powered down through the power +control system, such as via ACPI. + +Data: None. + +Example: + +{ "event": "POWERDOWN", + "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } + +QUORUM_FAILURE +-------------- + +Emitted by the Quorum block driver if it fails to establish a quorum. + +Data: + +- "reference": device name if defined else node name. +- "sector-num": Number of the first sector of the failed read operation. +- "sectors-count": Failed read operation sector count. + +Example: + +{ "event": "QUORUM_FAILURE", + "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + +QUORUM_REPORT_BAD +----------------- + +Emitted to report a corruption of a Quorum file. + +Data: + +- "error": Error message (json-string, optional) + Only present on failure. This field contains a human-readable + error message. There are no semantics other than that the + block layer reported an error and clients should not try to + interpret the error string. +- "node-name": The graph node name of the block driver state. +- "sector-num": Number of the first sector of the failed read operation. +- "sectors-count": Failed read operation sector count. + +Example: + +{ "event": "QUORUM_REPORT_BAD", + "data": { "node-name": "1.raw", "sector-num": 345435, "sectors-count": 5 }, + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + +RESET +----- + +Emitted when the Virtual Machine is reset. + +Data: None. + +Example: + +{ "event": "RESET", + "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } + +RESUME +------ + +Emitted when the Virtual Machine resumes execution. + +Data: None. + +Example: + +{ "event": "RESUME", + "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } + +RTC_CHANGE +---------- + +Emitted when the guest changes the RTC time. + +Data: + +- "offset": Offset between base RTC clock (as specified by -rtc base), and +new RTC clock value (json-number) + +Example: + +{ "event": "RTC_CHANGE", + "data": { "offset": 78 }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } + +SHUTDOWN +-------- + +Emitted when the Virtual Machine has shut down, indicating that qemu +is about to exit. + +Data: None. + +Example: + +{ "event": "SHUTDOWN", + "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } + +Note: If the command-line option "-no-shutdown" has been specified, a STOP +event will eventually follow the SHUTDOWN event. + +SPICE_CONNECTED +--------------- + +Emitted when a SPICE client connects. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, + "event": "SPICE_CONNECTED", + "data": { + "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +}} + +SPICE_DISCONNECTED +------------------ + +Emitted when a SPICE client disconnects. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, + "event": "SPICE_DISCONNECTED", + "data": { + "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +}} + +SPICE_INITIALIZED +----------------- + +Emitted after initial handshake and authentication takes place (if any) +and the SPICE channel is up and running + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "connection-id": spice connection id. All channels with the same id + belong to the same spice session (json-int) + - "channel-type": channel type. "1" is the main control channel, filter for + this one if you want track spice sessions only (json-int) + - "channel-id": channel id. Usually "0", might be different needed when + multiple channels of the same type exist, such as multiple + display channels in a multihead setup (json-int) + - "tls": whevener the channel is encrypted (json-bool) + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, + "event": "SPICE_INITIALIZED", + "data": {"server": {"auth": "spice", "port": "5921", + "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "49004", "family": "ipv4", "channel-type": 3, + "connection-id": 1804289383, "host": "127.0.0.1", + "channel-id": 0, "tls": true} +}} + +SPICE_MIGRATE_COMPLETED +----------------------- + +Emitted when SPICE migration has completed + +Data: None. + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, + "event": "SPICE_MIGRATE_COMPLETED" } + + +STOP +---- + +Emitted when the Virtual Machine is stopped. + +Data: None. + +Example: + +{ "event": "STOP", + "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } + +SUSPEND +------- + +Emitted when guest enters S3 state. + +Data: None. + +Example: + +{ "event": "SUSPEND", + "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } + +SUSPEND_DISK +------------ + +Emitted when the guest makes a request to enter S4 state. + +Data: None. + +Example: + +{ "event": "SUSPEND_DISK", + "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } + +Note: QEMU shuts down when entering S4 state. + +VNC_CONNECTED +------------- + +Emitted when a VNC client establishes a connection. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "event": "VNC_CONNECTED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0" }, + "client": { "family": "ipv4", "service": "58425", + "host": "127.0.0.1" } }, + "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } + + +Note: This event is emitted before any authentication takes place, thus +the authentication ID is not provided. + +VNC_DISCONNECTED +---------------- + +Emitted when the connection is closed. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "x509_dname": TLS dname (json-string, optional) + - "sasl_username": SASL username (json-string, optional) + +Example: + +{ "event": "VNC_DISCONNECTED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0" }, + "client": { "family": "ipv4", "service": "58425", + "host": "127.0.0.1", "sasl_username": "luiz" } }, + "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } + +VNC_INITIALIZED +--------------- + +Emitted after authentication takes place (if any) and the VNC session is +made active. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "x509_dname": TLS dname (json-string, optional) + - "sasl_username": SASL username (json-string, optional) + +Example: + +{ "event": "VNC_INITIALIZED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0"}, + "client": { "family": "ipv4", "service": "46089", + "host": "127.0.0.1", "sasl_username": "luiz" } }, + "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } + +VSERPORT_CHANGE +--------------- + +Emitted when the guest opens or closes a virtio-serial port. + +Data: + +- "id": device identifier of the virtio-serial port (json-string) +- "open": true if the guest has opened the virtio-serial port (json-bool) + +Example: + +{ "event": "VSERPORT_CHANGE", + "data": { "id": "channel0", "open": true }, + "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } + +WAKEUP +------ + +Emitted when the guest has woken up from S3 and is running. + +Data: None. + +Example: + +{ "event": "WAKEUP", + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + +WATCHDOG +-------- + +Emitted when the watchdog device's timer is expired. + +Data: + +- "action": Action that has been taken, it's one of the following (json-string): + "reset", "shutdown", "poweroff", "pause", "debug", or "none" + +Example: + +{ "event": "WATCHDOG", + "data": { "action": "reset" }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is +followed respectively by the RESET, SHUTDOWN, or STOP events. diff --git a/docs/qmp/qmp-spec.txt b/docs/qmp/qmp-spec.txt new file mode 100644 index 0000000..4c28cd9 --- /dev/null +++ b/docs/qmp/qmp-spec.txt @@ -0,0 +1,336 @@ + QEMU Machine Protocol Specification + +0. About This Document +====================== + +Copyright (C) 2009-2015 Red Hat, Inc. + +This work is licensed under the terms of the GNU GPL, version 2 or +later. See the COPYING file in the top-level directory. + +1. Introduction +=============== + +This document specifies the QEMU Machine Protocol (QMP), a JSON-based +protocol which is available for applications to operate QEMU at the +machine-level. It is also in use by the QEMU Guest Agent (QGA), which +is available for host applications to interact with the guest +operating system. + +2. Protocol Specification +========================= + +This section details the protocol format. For the purpose of this document +"Client" is any application which is using QMP to communicate with QEMU and +"Server" is QEMU itself. + +JSON data structures, when mentioned in this document, are always in the +following format: + + json-DATA-STRUCTURE-NAME + +Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined +by the JSON standard: + +http://www.ietf.org/rfc/rfc7159.txt + +The protocol is always encoded in UTF-8 except for synchronization +bytes (documented below); although thanks to json-string escape +sequences, the server will reply using only the strict ASCII subset. + +For convenience, json-object members mentioned in this document will +be in a certain order. However, in real protocol usage they can be in +ANY order, thus no particular order should be assumed. On the other +hand, use of json-array elements presumes that preserving order is +important unless specifically documented otherwise. Repeating a key +within a json-object gives unpredictable results. + +Also for convenience, the server will accept an extension of +'single-quoted' strings in place of the usual "double-quoted" +json-string, and both input forms of strings understand an additional +escape sequence of "\'" for a single quote. The server will only use +double quoting on output. + +2.1 General Definitions +----------------------- + +2.1.1 All interactions transmitted by the Server are json-objects, always + terminating with CRLF + +2.1.2 All json-objects members are mandatory when not specified otherwise + +2.2 Server Greeting +------------------- + +Right when connected the Server will issue a greeting message, which signals +that the connection has been successfully established and that the Server is +ready for capabilities negotiation (for more information refer to section +'4. Capabilities Negotiation'). + +The greeting message format is: + +{ "QMP": { "version": json-object, "capabilities": json-array } } + + Where, + +- The "version" member contains the Server's version information (the format + is the same of the query-version command) +- The "capabilities" member specify the availability of features beyond the + baseline specification; the order of elements in this array has no + particular significance, so a client must search the entire array + when looking for a particular capability + +2.2.1 Capabilities +------------------ + +As of the date this document was last revised, no server or client +capability strings have been defined. + + +2.3 Issuing Commands +-------------------- + +The format for command execution is: + +{ "execute": json-string, "arguments": json-object, "id": json-value } + + Where, + +- The "execute" member identifies the command to be executed by the Server +- The "arguments" member is used to pass any arguments required for the + execution of the command, it is optional when no arguments are + required. Each command documents what contents will be considered + valid when handling the json-argument +- The "id" member is a transaction identification associated with the + command execution, it is optional and will be part of the response if + provided. The "id" member can be any json-value, although most + clients merely use a json-number incremented for each successive + command + +2.4 Commands Responses +---------------------- + +There are two possible responses which the Server will issue as the result +of a command execution: success or error. + +2.4.1 success +------------- + +The format of a success response is: + +{ "return": json-value, "id": json-value } + + Where, + +- The "return" member contains the data returned by the command, which + is defined on a per-command basis (usually a json-object or + json-array of json-objects, but sometimes a json-number, json-string, + or json-array of json-strings); it is an empty json-object if the + command does not return data +- The "id" member contains the transaction identification associated + with the command execution if issued by the Client + +2.4.2 error +----------- + +The format of an error response is: + +{ "error": { "class": json-string, "desc": json-string }, "id": json-value } + + Where, + +- The "class" member contains the error class name (eg. "GenericError") +- The "desc" member is a human-readable error message. Clients should + not attempt to parse this message. +- The "id" member contains the transaction identification associated with + the command execution if issued by the Client + +NOTE: Some errors can occur before the Server is able to read the "id" member, +in these cases the "id" member will not be part of the error response, even +if provided by the client. + +2.5 Asynchronous events +----------------------- + +As a result of state changes, the Server may send messages unilaterally +to the Client at any time, when not in the middle of any other +response. They are called "asynchronous events". + +The format of asynchronous events is: + +{ "event": json-string, "data": json-object, + "timestamp": { "seconds": json-number, "microseconds": json-number } } + + Where, + +- The "event" member contains the event's name +- The "data" member contains event specific data, which is defined in a + per-event basis, it is optional +- The "timestamp" member contains the exact time of when the event + occurred in the Server. It is a fixed json-object with time in + seconds and microseconds relative to the Unix Epoch (1 Jan 1970); if + there is a failure to retrieve host time, both members of the + timestamp will be set to -1. + +For a listing of supported asynchronous events, please, refer to the +qmp-events.txt file. + +2.5 QGA Synchronization +----------------------- + +When using QGA, an additional synchronization feature is built into +the protocol. If the Client sends a raw 0xFF sentinel byte (not valid +JSON), then the Server will reset its state and discard all pending +data prior to the sentinel. Conversely, if the Client makes use of +the 'guest-sync-delimited' command, the Server will send a raw 0xFF +sentinel byte prior to its response, to aid the Client in discarding +any data prior to the sentinel. + + +3. QMP Examples +=============== + +This section provides some examples of real QMP usage, in all of them +"C" stands for "Client" and "S" stands for "Server". + +3.1 Server greeting +------------------- + +S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 }, + "package": ""}, "capabilities": []}} + +3.2 Client QMP negotiation +-------------------------- +C: { "execute": "qmp_capabilities" } +S: { "return": {}} + +3.3 Simple 'stop' execution +--------------------------- + +C: { "execute": "stop" } +S: { "return": {} } + +3.4 KVM information +------------------- + +C: { "execute": "query-kvm", "id": "example" } +S: { "return": { "enabled": true, "present": true }, "id": "example"} + +3.5 Parsing error +------------------ + +C: { "execute": } +S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } } + +3.6 Powerdown event +------------------- + +S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, + "event": "POWERDOWN" } + +4. Capabilities Negotiation +=========================== + +When a Client successfully establishes a connection, the Server is in +Capabilities Negotiation mode. + +In this mode only the qmp_capabilities command is allowed to run, all +other commands will return the CommandNotFound error. Asynchronous +messages are not delivered either. + +Clients should use the qmp_capabilities command to enable capabilities +advertised in the Server's greeting (section '2.2 Server Greeting') they +support. + +When the qmp_capabilities command is issued, and if it does not return an +error, the Server enters in Command mode where capabilities changes take +effect, all commands (except qmp_capabilities) are allowed and asynchronous +messages are delivered. + +5 Compatibility Considerations +============================== + +All protocol changes or new features which modify the protocol format in an +incompatible way are disabled by default and will be advertised by the +capabilities array (section '2.2 Server Greeting'). Thus, Clients can check +that array and enable the capabilities they support. + +The QMP Server performs a type check on the arguments to a command. It +generates an error if a value does not have the expected type for its +key, or if it does not understand a key that the Client included. The +strictness of the Server catches wrong assumptions of Clients about +the Server's schema. Clients can assume that, when such validation +errors occur, they will be reported before the command generated any +side effect. + +However, Clients must not assume any particular: + +- Length of json-arrays +- Size of json-objects; in particular, future versions of QEMU may add + new keys and Clients should be able to ignore them. +- Order of json-object members or json-array elements +- Amount of errors generated by a command, that is, new errors can be added + to any existing command in newer versions of the Server + +Any command or field name beginning with "x-" is deemed experimental, +and may be withdrawn or changed in an incompatible manner in a future +release. + +Of course, the Server does guarantee to send valid JSON. But apart from +this, a Client should be "conservative in what they send, and liberal in +what they accept". + +6. Downstream extension of QMP +============================== + +We recommend that downstream consumers of QEMU do *not* modify QMP. +Management tools should be able to support both upstream and downstream +versions of QMP without special logic, and downstream extensions are +inherently at odds with that. + +However, we recognize that it is sometimes impossible for downstreams to +avoid modifying QMP. Both upstream and downstream need to take care to +preserve long-term compatibility and interoperability. + +To help with that, QMP reserves JSON object member names beginning with +'__' (double underscore) for downstream use ("downstream names"). This +means upstream will never use any downstream names for its commands, +arguments, errors, asynchronous events, and so forth. + +Any new names downstream wishes to add must begin with '__'. To +ensure compatibility with other downstreams, it is strongly +recommended that you prefix your downstream names with '__RFQDN_' where +RFQDN is a valid, reverse fully qualified domain name which you +control. For example, a qemu-kvm specific monitor command would be: + + (qemu) __org.linux-kvm_enable_irqchip + +Downstream must not change the server greeting (section 2.2) other than +to offer additional capabilities. But see below for why even that is +discouraged. + +Section '5 Compatibility Considerations' applies to downstream as well +as to upstream, obviously. It follows that downstream must behave +exactly like upstream for any input not containing members with +downstream names ("downstream members"), except it may add members +with downstream names to its output. + +Thus, a client should not be able to distinguish downstream from +upstream as long as it doesn't send input with downstream members, and +properly ignores any downstream members in the output it receives. + +Advice on downstream modifications: + +1. Introducing new commands is okay. If you want to extend an existing + command, consider introducing a new one with the new behaviour + instead. + +2. Introducing new asynchronous messages is okay. If you want to extend + an existing message, consider adding a new one instead. + +3. Introducing new errors for use in new commands is okay. Adding new + errors to existing commands counts as extension, so 1. applies. + +4. New capabilities are strongly discouraged. Capabilities are for + evolving the basic protocol, and multiple diverging basic protocol + dialects are most undesirable. diff --git a/docs/rcu.txt b/docs/rcu.txt new file mode 100644 index 0000000..21ecb81 --- /dev/null +++ b/docs/rcu.txt @@ -0,0 +1,390 @@ +Using RCU (Read-Copy-Update) for synchronization +================================================ + +Read-copy update (RCU) is a synchronization mechanism that is used to +protect read-mostly data structures. RCU is very efficient and scalable +on the read side (it is wait-free), and thus can make the read paths +extremely fast. + +RCU supports concurrency between a single writer and multiple readers, +thus it is not used alone. Typically, the write-side will use a lock to +serialize multiple updates, but other approaches are possible (e.g., +restricting updates to a single task). In QEMU, when a lock is used, +this will often be the "iothread mutex", also known as the "big QEMU +lock" (BQL). Also, restricting updates to a single task is done in +QEMU using the "bottom half" API. + +RCU is fundamentally a "wait-to-finish" mechanism. The read side marks +sections of code with "critical sections", and the update side will wait +for the execution of all *currently running* critical sections before +proceeding, or before asynchronously executing a callback. + +The key point here is that only the currently running critical sections +are waited for; critical sections that are started _after_ the beginning +of the wait do not extend the wait, despite running concurrently with +the updater. This is the reason why RCU is more scalable than, +for example, reader-writer locks. It is so much more scalable that +the system will have a single instance of the RCU mechanism; a single +mechanism can be used for an arbitrary number of "things", without +having to worry about things such as contention or deadlocks. + +How is this possible? The basic idea is to split updates in two phases, +"removal" and "reclamation". During removal, we ensure that subsequent +readers will not be able to get a reference to the old data. After +removal has completed, a critical section will not be able to access +the old data. Therefore, critical sections that begin after removal +do not matter; as soon as all previous critical sections have finished, +there cannot be any readers who hold references to the data structure, +and these can now be safely reclaimed (e.g., freed or unref'ed). + +Here is a picutre: + + thread 1 thread 2 thread 3 + ------------------- ------------------------ ------------------- + enter RCU crit.sec. + | finish removal phase + | begin wait + | | enter RCU crit.sec. + exit RCU crit.sec | | + complete wait | + begin reclamation phase | + exit RCU crit.sec. + + +Note how thread 3 is still executing its critical section when thread 2 +starts reclaiming data. This is possible, because the old version of the +data structure was not accessible at the time thread 3 began executing +that critical section. + + +RCU API +======= + +The core RCU API is small: + + void rcu_read_lock(void); + + Used by a reader to inform the reclaimer that the reader is + entering an RCU read-side critical section. + + void rcu_read_unlock(void); + + Used by a reader to inform the reclaimer that the reader is + exiting an RCU read-side critical section. Note that RCU + read-side critical sections may be nested and/or overlapping. + + void synchronize_rcu(void); + + Blocks until all pre-existing RCU read-side critical sections + on all threads have completed. This marks the end of the removal + phase and the beginning of reclamation phase. + + Note that it would be valid for another update to come while + synchronize_rcu is running. Because of this, it is better that + the updater releases any locks it may hold before calling + synchronize_rcu. If this is not possible (for example, because + the updater is protected by the BQL), you can use call_rcu. + + void call_rcu1(struct rcu_head * head, + void (*func)(struct rcu_head *head)); + + This function invokes func(head) after all pre-existing RCU + read-side critical sections on all threads have completed. This + marks the end of the removal phase, with func taking care + asynchronously of the reclamation phase. + + The foo struct needs to have an rcu_head structure added, + perhaps as follows: + + struct foo { + struct rcu_head rcu; + int a; + char b; + long c; + }; + + so that the reclaimer function can fetch the struct foo address + and free it: + + call_rcu1(&foo.rcu, foo_reclaim); + + void foo_reclaim(struct rcu_head *rp) + { + struct foo *fp = container_of(rp, struct foo, rcu); + g_free(fp); + } + + For the common case where the rcu_head member is the first of the + struct, you can use the following macro. + + void call_rcu(T *p, + void (*func)(T *p), + field-name); + void g_free_rcu(T *p, + field-name); + + call_rcu1 is typically used through these macro, in the common case + where the "struct rcu_head" is the first field in the struct. If + the callback function is g_free, in particular, g_free_rcu can be + used. In the above case, one could have written simply: + + g_free_rcu(foo_reclaim, rcu); + + typeof(*p) atomic_rcu_read(p); + + atomic_rcu_read() is similar to atomic_mb_read(), but it makes + some assumptions on the code that calls it. This allows a more + optimized implementation. + + atomic_rcu_read assumes that whenever a single RCU critical + section reads multiple shared data, these reads are either + data-dependent or need no ordering. This is almost always the + case when using RCU, because read-side critical sections typically + navigate one or more pointers (the pointers that are changed on + every update) until reaching a data structure of interest, + and then read from there. + + RCU read-side critical sections must use atomic_rcu_read() to + read data, unless concurrent writes are presented by another + synchronization mechanism. + + Furthermore, RCU read-side critical sections should traverse the + data structure in a single direction, opposite to the direction + in which the updater initializes it. + + void atomic_rcu_set(p, typeof(*p) v); + + atomic_rcu_set() is also similar to atomic_mb_set(), and it also + makes assumptions on the code that calls it in order to allow a more + optimized implementation. + + In particular, atomic_rcu_set() suffices for synchronization + with readers, if the updater never mutates a field within a + data item that is already accessible to readers. This is the + case when initializing a new copy of the RCU-protected data + structure; just ensure that initialization of *p is carried out + before atomic_rcu_set() makes the data item visible to readers. + If this rule is observed, writes will happen in the opposite + order as reads in the RCU read-side critical sections (or if + there is just one update), and there will be no need for other + synchronization mechanism to coordinate the accesses. + +The following APIs must be used before RCU is used in a thread: + + void rcu_register_thread(void); + + Mark a thread as taking part in the RCU mechanism. Such a thread + will have to report quiescent points regularly, either manually + or through the QemuCond/QemuSemaphore/QemuEvent APIs. + + void rcu_unregister_thread(void); + + Mark a thread as not taking part anymore in the RCU mechanism. + It is not a problem if such a thread reports quiescent points, + either manually or by using the QemuCond/QemuSemaphore/QemuEvent + APIs. + +Note that these APIs are relatively heavyweight, and should _not_ be +nested. + + +DIFFERENCES WITH LINUX +====================== + +- Waiting on a mutex is possible, though discouraged, within an RCU critical + section. This is because spinlocks are rarely (if ever) used in userspace + programming; not allowing this would prevent upgrading an RCU read-side + critical section to become an updater. + +- atomic_rcu_read and atomic_rcu_set replace rcu_dereference and + rcu_assign_pointer. They take a _pointer_ to the variable being accessed. + +- call_rcu is a macro that has an extra argument (the name of the first + field in the struct, which must be a struct rcu_head), and expects the + type of the callback's argument to be the type of the first argument. + call_rcu1 is the same as Linux's call_rcu. + + +RCU PATTERNS +============ + +Many patterns using read-writer locks translate directly to RCU, with +the advantages of higher scalability and deadlock immunity. + +In general, RCU can be used whenever it is possible to create a new +"version" of a data structure every time the updater runs. This may +sound like a very strict restriction, however: + +- the updater does not mean "everything that writes to a data structure", + but rather "everything that involves a reclamation step". See the + array example below + +- in some cases, creating a new version of a data structure may actually + be very cheap. For example, modifying the "next" pointer of a singly + linked list is effectively creating a new version of the list. + +Here are some frequently-used RCU idioms that are worth noting. + + +RCU list processing +------------------- + +TBD (not yet used in QEMU) + + +RCU reference counting +---------------------- + +Because grace periods are not allowed to complete while there is an RCU +read-side critical section in progress, the RCU read-side primitives +may be used as a restricted reference-counting mechanism. For example, +consider the following code fragment: + + rcu_read_lock(); + p = atomic_rcu_read(&foo); + /* do something with p. */ + rcu_read_unlock(); + +The RCU read-side critical section ensures that the value of "p" remains +valid until after the rcu_read_unlock(). In some sense, it is acquiring +a reference to p that is later released when the critical section ends. +The write side looks simply like this (with appropriate locking): + + qemu_mutex_lock(&foo_mutex); + old = foo; + atomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + synchronize_rcu(); + free(old); + +If the processing cannot be done purely within the critical section, it +is possible to combine this idiom with a "real" reference count: + + rcu_read_lock(); + p = atomic_rcu_read(&foo); + foo_ref(p); + rcu_read_unlock(); + /* do something with p. */ + foo_unref(p); + +The write side can be like this: + + qemu_mutex_lock(&foo_mutex); + old = foo; + atomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + synchronize_rcu(); + foo_unref(old); + +or with call_rcu: + + qemu_mutex_lock(&foo_mutex); + old = foo; + atomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + call_rcu(foo_unref, old, rcu); + +In both cases, the write side only performs removal. Reclamation +happens when the last reference to a "foo" object is dropped. +Using synchronize_rcu() is undesirably expensive, because the +last reference may be dropped on the read side. Hence you can +use call_rcu() instead: + + foo_unref(struct foo *p) { + if (atomic_fetch_dec(&p->refcount) == 1) { + call_rcu(foo_destroy, p, rcu); + } + } + + +Note that the same idioms would be possible with reader/writer +locks: + + read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock); + p = foo; p = foo; + /* do something with p. */ foo = new; + read_unlock(&foo_rwlock); free(p); + write_mutex_unlock(&foo_rwlock); + free(p); + + ------------------------------------------------------------------ + + read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock); + p = foo; old = foo; + foo_ref(p); foo = new; + read_unlock(&foo_rwlock); foo_unref(old); + /* do something with p. */ write_mutex_unlock(&foo_rwlock); + read_lock(&foo_rwlock); + foo_unref(p); + read_unlock(&foo_rwlock); + +foo_unref could use a mechanism such as bottom halves to move deallocation +out of the write-side critical section. + + +RCU resizable arrays +-------------------- + +Resizable arrays can be used with RCU. The expensive RCU synchronization +(or call_rcu) only needs to take place when the array is resized. +The two items to take care of are: + +- ensuring that the old version of the array is available between removal + and reclamation; + +- avoiding mismatches in the read side between the array data and the + array size. + +The first problem is avoided simply by not using realloc. Instead, +each resize will allocate a new array and copy the old data into it. +The second problem would arise if the size and the data pointers were +two members of a larger struct: + + struct mystuff { + ... + int data_size; + int data_alloc; + T *data; + ... + }; + +Instead, we store the size of the array with the array itself: + + struct arr { + int size; + int alloc; + T data[]; + }; + struct arr *global_array; + + read side: + rcu_read_lock(); + struct arr *array = atomic_rcu_read(&global_array); + x = i < array->size ? array->data[i] : -1; + rcu_read_unlock(); + return x; + + write side (running under a lock): + if (global_array->size == global_array->alloc) { + /* Creating a new version. */ + new_array = g_malloc(sizeof(struct arr) + + global_array->alloc * 2 * sizeof(T)); + new_array->size = global_array->size; + new_array->alloc = global_array->alloc * 2; + memcpy(new_array->data, global_array->data, + global_array->alloc * sizeof(T)); + + /* Removal phase. */ + old_array = global_array; + atomic_rcu_set(&new_array->data, new_array); + synchronize_rcu(); + + /* Reclamation phase. */ + free(old_array); + } + + +SOURCES +======= + +* Documentation/RCU/ from the Linux kernel diff --git a/docs/rdma.txt b/docs/rdma.txt new file mode 100644 index 0000000..2bdd0a5 --- /dev/null +++ b/docs/rdma.txt @@ -0,0 +1,420 @@ +(RDMA: Remote Direct Memory Access) +RDMA Live Migration Specification, Version # 1 +============================================== +Wiki: http://wiki.qemu-project.org/Features/RDMALiveMigration +Github: git@github.com:hinesmr/qemu.git, 'rdma' branch + +Copyright (C) 2013 Michael R. Hines + +An *exhaustive* paper (2010) shows additional performance details +linked on the QEMU wiki above. + +Contents: +========= +* Introduction +* Before running +* Running +* Performance +* RDMA Migration Protocol Description +* Versioning and Capabilities +* QEMUFileRDMA Interface +* Migration of VM's ram +* Error handling +* TODO + +Introduction: +============= + +RDMA helps make your migration more deterministic under heavy load because +of the significantly lower latency and higher throughput over TCP/IP. This is +because the RDMA I/O architecture reduces the number of interrupts and +data copies by bypassing the host networking stack. In particular, a TCP-based +migration, under certain types of memory-bound workloads, may take a more +unpredicatable amount of time to complete the migration if the amount of +memory tracked during each live migration iteration round cannot keep pace +with the rate of dirty memory produced by the workload. + +RDMA currently comes in two flavors: both Ethernet based (RoCE, or RDMA +over Converged Ethernet) as well as Infiniband-based. This implementation of +migration using RDMA is capable of using both technologies because of +the use of the OpenFabrics OFED software stack that abstracts out the +programming model irrespective of the underlying hardware. + +Refer to openfabrics.org or your respective RDMA hardware vendor for +an understanding on how to verify that you have the OFED software stack +installed in your environment. You should be able to successfully link +against the "librdmacm" and "libibverbs" libraries and development headers +for a working build of QEMU to run successfully using RDMA Migration. + +BEFORE RUNNING: +=============== + +Use of RDMA during migration requires pinning and registering memory +with the hardware. This means that memory must be physically resident +before the hardware can transmit that memory to another machine. +If this is not acceptable for your application or product, then the use +of RDMA migration may in fact be harmful to co-located VMs or other +software on the machine if there is not sufficient memory available to +relocate the entire footprint of the virtual machine. If so, then the +use of RDMA is discouraged and it is recommended to use standard TCP migration. + +Experimental: Next, decide if you want dynamic page registration. +For example, if you have an 8GB RAM virtual machine, but only 1GB +is in active use, then enabling this feature will cause all 8GB to +be pinned and resident in memory. This feature mostly affects the +bulk-phase round of the migration and can be enabled for extremely +high-performance RDMA hardware using the following command: + +QEMU Monitor Command: +$ migrate_set_capability rdma-pin-all on # disabled by default + +Performing this action will cause all 8GB to be pinned, so if that's +not what you want, then please ignore this step altogether. + +On the other hand, this will also significantly speed up the bulk round +of the migration, which can greatly reduce the "total" time of your migration. +Example performance of this using an idle VM in the previous example +can be found in the "Performance" section. + +Note: for very large virtual machines (hundreds of GBs), pinning all +*all* of the memory of your virtual machine in the kernel is very expensive +may extend the initial bulk iteration time by many seconds, +and thus extending the total migration time. However, this will not +affect the determinism or predictability of your migration you will +still gain from the benefits of advanced pinning with RDMA. + +RUNNING: +======== + +First, set the migration speed to match your hardware's capabilities: + +QEMU Monitor Command: +$ migrate_set_speed 40g # or whatever is the MAX of your RDMA device + +Next, on the destination machine, add the following to the QEMU command line: + +qemu ..... -incoming rdma:host:port + +Finally, perform the actual migration on the source machine: + +QEMU Monitor Command: +$ migrate -d rdma:host:port + +PERFORMANCE +=========== + +Here is a brief summary of total migration time and downtime using RDMA: +Using a 40gbps infiniband link performing a worst-case stress test, +using an 8GB RAM virtual machine: + +Using the following command: +$ apt-get install stress +$ stress --vm-bytes 7500M --vm 1 --vm-keep + +1. Migration throughput: 26 gigabits/second. +2. Downtime (stop time) varies between 15 and 100 milliseconds. + +EFFECTS of memory registration on bulk phase round: + +For example, in the same 8GB RAM example with all 8GB of memory in +active use and the VM itself is completely idle using the same 40 gbps +infiniband link: + +1. rdma-pin-all disabled total time: approximately 7.5 seconds @ 9.5 Gbps +2. rdma-pin-all enabled total time: approximately 4 seconds @ 26 Gbps + +These numbers would of course scale up to whatever size virtual machine +you have to migrate using RDMA. + +Enabling this feature does *not* have any measurable affect on +migration *downtime*. This is because, without this feature, all of the +memory will have already been registered already in advance during +the bulk round and does not need to be re-registered during the successive +iteration rounds. + +RDMA Protocol Description: +========================== + +Migration with RDMA is separated into two parts: + +1. The transmission of the pages using RDMA +2. Everything else (a control channel is introduced) + +"Everything else" is transmitted using a formal +protocol now, consisting of infiniband SEND messages. + +An infiniband SEND message is the standard ibverbs +message used by applications of infiniband hardware. +The only difference between a SEND message and an RDMA +message is that SEND messages cause notifications +to be posted to the completion queue (CQ) on the +infiniband receiver side, whereas RDMA messages (used +for VM's ram) do not (to behave like an actual DMA). + +Messages in infiniband require two things: + +1. registration of the memory that will be transmitted +2. (SEND only) work requests to be posted on both + sides of the network before the actual transmission + can occur. + +RDMA messages are much easier to deal with. Once the memory +on the receiver side is registered and pinned, we're +basically done. All that is required is for the sender +side to start dumping bytes onto the link. + +(Memory is not released from pinning until the migration +completes, given that RDMA migrations are very fast.) + +SEND messages require more coordination because the +receiver must have reserved space (using a receive +work request) on the receive queue (RQ) before QEMUFileRDMA +can start using them to carry all the bytes as +a control transport for migration of device state. + +To begin the migration, the initial connection setup is +as follows (migration-rdma.c): + +1. Receiver and Sender are started (command line or libvirt): +2. Both sides post two RQ work requests +3. Receiver does listen() +4. Sender does connect() +5. Receiver accept() +6. Check versioning and capabilities (described later) + +At this point, we define a control channel on top of SEND messages +which is described by a formal protocol. Each SEND message has a +header portion and a data portion (but together are transmitted +as a single SEND message). + +Header: + * Length (of the data portion, uint32, network byte order) + * Type (what command to perform, uint32, network byte order) + * Repeat (Number of commands in data portion, same type only) + +The 'Repeat' field is here to support future multiple page registrations +in a single message without any need to change the protocol itself +so that the protocol is compatible against multiple versions of QEMU. +Version #1 requires that all server implementations of the protocol must +check this field and register all requests found in the array of commands located +in the data portion and return an equal number of results in the response. +The maximum number of repeats is hard-coded to 4096. This is a conservative +limit based on the maximum size of a SEND message along with empirical +observations on the maximum future benefit of simultaneous page registrations. + +The 'type' field has 12 different command values: + 1. Unused + 2. Error (sent to the source during bad things) + 3. Ready (control-channel is available) + 4. QEMU File (for sending non-live device state) + 5. RAM Blocks request (used right after connection setup) + 6. RAM Blocks result (used right after connection setup) + 7. Compress page (zap zero page and skip registration) + 8. Register request (dynamic chunk registration) + 9. Register result ('rkey' to be used by sender) + 10. Register finished (registration for current iteration finished) + 11. Unregister request (unpin previously registered memory) + 12. Unregister finished (confirmation that unpin completed) + +A single control message, as hinted above, can contain within the data +portion an array of many commands of the same type. If there is more than +one command, then the 'repeat' field will be greater than 1. + +After connection setup, message 5 & 6 are used to exchange ram block +information and optionally pin all the memory if requested by the user. + +After ram block exchange is completed, we have two protocol-level +functions, responsible for communicating control-channel commands +using the above list of values: + +Logically: + +qemu_rdma_exchange_recv(header, expected command type) + +1. We transmit a READY command to let the sender know that + we are *ready* to receive some data bytes on the control channel. +2. Before attempting to receive the expected command, we post another + RQ work request to replace the one we just used up. +3. Block on a CQ event channel and wait for the SEND to arrive. +4. When the send arrives, librdmacm will unblock us. +5. Verify that the command-type and version received matches the one we expected. + +qemu_rdma_exchange_send(header, data, optional response header & data): + +1. Block on the CQ event channel waiting for a READY command + from the receiver to tell us that the receiver + is *ready* for us to transmit some new bytes. +2. Optionally: if we are expecting a response from the command + (that we have not yet transmitted), let's post an RQ + work request to receive that data a few moments later. +3. When the READY arrives, librdmacm will + unblock us and we immediately post a RQ work request + to replace the one we just used up. +4. Now, we can actually post the work request to SEND + the requested command type of the header we were asked for. +5. Optionally, if we are expecting a response (as before), + we block again and wait for that response using the additional + work request we previously posted. (This is used to carry + 'Register result' commands #6 back to the sender which + hold the rkey need to perform RDMA. Note that the virtual address + corresponding to this rkey was already exchanged at the beginning + of the connection (described below). + +All of the remaining command types (not including 'ready') +described above all use the aformentioned two functions to do the hard work: + +1. After connection setup, RAMBlock information is exchanged using + this protocol before the actual migration begins. This information includes + a description of each RAMBlock on the server side as well as the virtual addresses + and lengths of each RAMBlock. This is used by the client to determine the + start and stop locations of chunks and how to register them dynamically + before performing the RDMA operations. +2. During runtime, once a 'chunk' becomes full of pages ready to + be sent with RDMA, the registration commands are used to ask the + other side to register the memory for this chunk and respond + with the result (rkey) of the registration. +3. Also, the QEMUFile interfaces also call these functions (described below) + when transmitting non-live state, such as devices or to send + its own protocol information during the migration process. +4. Finally, zero pages are only checked if a page has not yet been registered + using chunk registration (or not checked at all and unconditionally + written if chunk registration is disabled. This is accomplished using + the "Compress" command listed above. If the page *has* been registered + then we check the entire chunk for zero. Only if the entire chunk is + zero, then we send a compress command to zap the page on the other side. + +Versioning and Capabilities +=========================== +Current version of the protocol is version #1. + +The same version applies to both for protocol traffic and capabilities +negotiation. (i.e. There is only one version number that is referred to +by all communication). + +librdmacm provides the user with a 'private data' area to be exchanged +at connection-setup time before any infiniband traffic is generated. + +Header: + * Version (protocol version validated before send/recv occurs), + uint32, network byte order + * Flags (bitwise OR of each capability), + uint32, network byte order + +There is no data portion of this header right now, so there is +no length field. The maximum size of the 'private data' section +is only 192 bytes per the Infiniband specification, so it's not +very useful for data anyway. This structure needs to remain small. + +This private data area is a convenient place to check for protocol +versioning because the user does not need to register memory to +transmit a few bytes of version information. + +This is also a convenient place to negotiate capabilities +(like dynamic page registration). + +If the version is invalid, we throw an error. + +If the version is new, we only negotiate the capabilities that the +requested version is able to perform and ignore the rest. + +Currently there is only one capability in Version #1: dynamic page registration + +Finally: Negotiation happens with the Flags field: If the primary-VM +sets a flag, but the destination does not support this capability, it +will return a zero-bit for that flag and the primary-VM will understand +that as not being an available capability and will thus disable that +capability on the primary-VM side. + +QEMUFileRDMA Interface: +======================= + +QEMUFileRDMA introduces a couple of new functions: + +1. qemu_rdma_get_buffer() (QEMUFileOps rdma_read_ops) +2. qemu_rdma_put_buffer() (QEMUFileOps rdma_write_ops) + +These two functions are very short and simply use the protocol +describe above to deliver bytes without changing the upper-level +users of QEMUFile that depend on a bytestream abstraction. + +Finally, how do we handoff the actual bytes to get_buffer()? + +Again, because we're trying to "fake" a bytestream abstraction +using an analogy not unlike individual UDP frames, we have +to hold on to the bytes received from control-channel's SEND +messages in memory. + +Each time we receive a complete "QEMU File" control-channel +message, the bytes from SEND are copied into a small local holding area. + +Then, we return the number of bytes requested by get_buffer() +and leave the remaining bytes in the holding area until get_buffer() +comes around for another pass. + +If the buffer is empty, then we follow the same steps +listed above and issue another "QEMU File" protocol command, +asking for a new SEND message to re-fill the buffer. + +Migration of VM's ram: +==================== + +At the beginning of the migration, (migration-rdma.c), +the sender and the receiver populate the list of RAMBlocks +to be registered with each other into a structure. +Then, using the aforementioned protocol, they exchange a +description of these blocks with each other, to be used later +during the iteration of main memory. This description includes +a list of all the RAMBlocks, their offsets and lengths, virtual +addresses and possibly includes pre-registered RDMA keys in case dynamic +page registration was disabled on the server-side, otherwise not. + +Main memory is not migrated with the aforementioned protocol, +but is instead migrated with normal RDMA Write operations. + +Pages are migrated in "chunks" (hard-coded to 1 Megabyte right now). +Chunk size is not dynamic, but it could be in a future implementation. +There's nothing to indicate that this is useful right now. + +When a chunk is full (or a flush() occurs), the memory backed by +the chunk is registered with librdmacm is pinned in memory on +both sides using the aforementioned protocol. +After pinning, an RDMA Write is generated and transmitted +for the entire chunk. + +Chunks are also transmitted in batches: This means that we +do not request that the hardware signal the completion queue +for the completion of *every* chunk. The current batch size +is about 64 chunks (corresponding to 64 MB of memory). +Only the last chunk in a batch must be signaled. +This helps keep everything as asynchronous as possible +and helps keep the hardware busy performing RDMA operations. + +Error-handling: +=============== + +Infiniband has what is called a "Reliable, Connected" +link (one of 4 choices). This is the mode in which +we use for RDMA migration. + +If a *single* message fails, +the decision is to abort the migration entirely and +cleanup all the RDMA descriptors and unregister all +the memory. + +After cleanup, the Virtual Machine is returned to normal +operation the same way that would happen if the TCP +socket is broken during a non-RDMA based migration. + +TODO: +===== +1. Currently, 'ulimit -l' mlock() limits as well as cgroups swap limits + are not compatible with infinband memory pinning and will result in + an aborted migration (but with the source VM left unaffected). +2. Use of the recent /proc//pagemap would likely speed up + the use of KSM and ballooning while using RDMA. +3. Also, some form of balloon-device usage tracking would also + help alleviate some issues. +4. Use LRU to provide more fine-grained direction of UNREGISTER + requests for unpinning memory in an overcommitted environment. +5. Expose UNREGISTER support to the user by way of workload-specific + hints about application behavior. diff --git a/docs/specs/acpi_cpu_hotplug.txt b/docs/specs/acpi_cpu_hotplug.txt new file mode 100644 index 0000000..340b751 --- /dev/null +++ b/docs/specs/acpi_cpu_hotplug.txt @@ -0,0 +1,24 @@ +QEMU<->ACPI BIOS CPU hotplug interface +-------------------------------------- + +QEMU supports CPU hotplug via ACPI. This document +describes the interface between QEMU and the ACPI BIOS. + +ACPI GPE block (IO ports 0xafe0-0xafe3, byte access): +----------------------------------------- + +Generic ACPI GPE block. Bit 2 (GPE.2) used to notify CPU +hot-add/remove event to ACPI BIOS, via SCI interrupt. + +CPU present bitmap for: + ICH9-LPC (IO port 0x0cd8-0xcf7, 1-byte access) + PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access) +--------------------------------------------------------------- +One bit per CPU. Bit position reflects corresponding CPU APIC ID. +Read-only. + +CPU hot-add/remove notification: +----------------------------------------------------- +QEMU sets/clears corresponding CPU bit on hot-add/remove event. +CPU present map read by ACPI BIOS GPE.2 handler to notify OS of CPU +hot-(un)plug events. diff --git a/docs/specs/acpi_mem_hotplug.txt b/docs/specs/acpi_mem_hotplug.txt new file mode 100644 index 0000000..3df3620 --- /dev/null +++ b/docs/specs/acpi_mem_hotplug.txt @@ -0,0 +1,94 @@ +QEMU<->ACPI BIOS memory hotplug interface +-------------------------------------- + +ACPI BIOS GPE.3 handler is dedicated for notifying OS about memory hot-add +and hot-remove events. + +Memory hot-plug interface (IO port 0xa00-0xa17, 1-4 byte access): +--------------------------------------------------------------- +0xa00: + read access: + [0x0-0x3] Lo part of memory device phys address + [0x4-0x7] Hi part of memory device phys address + [0x8-0xb] Lo part of memory device size in bytes + [0xc-0xf] Hi part of memory device size in bytes + [0x10-0x13] Memory device proximity domain + [0x14] Memory device status fields + bits: + 0: Device is enabled and may be used by guest + 1: Device insert event, used to distinguish device for which + no device check event to OSPM was issued. + It's valid only when bit 1 is set. + 2: Device remove event, used to distinguish device for which + no device eject request to OSPM was issued. + 3-7: reserved and should be ignored by OSPM + [0x15-0x17] reserved + + write access: + [0x0-0x3] Memory device slot selector, selects active memory device. + All following accesses to other registers in 0xa00-0xa17 + region will read/store data from/to selected memory device. + [0x4-0x7] OST event code reported by OSPM + [0x8-0xb] OST status code reported by OSPM + [0xc-0x13] reserved, writes into it are ignored + [0x14] Memory device control fields + bits: + 0: reserved, OSPM must clear it before writing to register. + Due to BUG in versions prior 2.4 that field isn't cleared + when other fields are written. Keep it reserved and don't + try to reuse it. + 1: if set to 1 clears device insert event, set by OSPM + after it has emitted device check event for the + selected memory device + 2: if set to 1 clears device remove event, set by OSPM + after it has emitted device eject request for the + selected memory device + 3: if set to 1 initiates device eject, set by OSPM when it + triggers memory device removal and calls _EJ0 method + 4-7: reserved, OSPM must clear them before writing to register + +Selecting memory device slot beyond present range has no effect on platform: + - write accesses to memory hot-plug registers not documented above are + ignored + - read accesses to memory hot-plug registers not documented above return + all bits set to 1. + +Memory hot remove process diagram: +---------------------------------- + +-------------+     +-----------------------+      +------------------+      + |  1. QEMU    |     | 2. QEMU               |      |3. QEMU           |      + |  device_del +---->+ device unplug request +----->+Send SCI to guest,|      + |             |     |         cb            |      |return control to |      + +-------------+     +-----------------------+      |management        |      +                                                    +------------------+      +                                                                              + +---------------------------------------------------------------------+      +                                                                              + +---------------------+              +-------------------------+             + | OSPM:               | remove event | OSPM:                   |             + | send Eject Request, |              | Scan memory devices     |             + | clear remove event  +<-------------+ for event flags         |             + |                     |              |                         |             + +---------------------+              +-------------------------+             +           |                                                                  +           |                                                                  + +---------v--------+            +-----------------------+                    + | Guest OS:        |  success   | OSPM:                 |                    + | process Ejection +----------->+ Execute _EJ0 method,  |                    + | request          |            | set eject bit in flags|                    + +------------------+            +-----------------------+                    +           |failure                         |                                 +           v                                v                                 + +------------------------+      +-----------------------+                    + | OSPM:                  |      | QEMU:                 |                    + | set OST event & status |      | call device unplug cb |                    + | fields                 |      |                       |                    + +------------------------+      +-----------------------+                    +          |                                  |                                +          v                                  v                                + +------------------+              +-------------------+                      + |QEMU:             |              |QEMU:              |                      + |Send OST QMP event|              |Send device deleted|                      + |                  |              |QMP event          |                      + +------------------+              |                   |                      +                                   +-------------------+ diff --git a/docs/specs/acpi_pci_hotplug.txt b/docs/specs/acpi_pci_hotplug.txt new file mode 100644 index 0000000..a839434 --- /dev/null +++ b/docs/specs/acpi_pci_hotplug.txt @@ -0,0 +1,45 @@ +QEMU<->ACPI BIOS PCI hotplug interface +-------------------------------------- + +QEMU supports PCI hotplug via ACPI, for PCI bus 0. This document +describes the interface between QEMU and the ACPI BIOS. + +ACPI GPE block (IO ports 0xafe0-0xafe3, byte access): +----------------------------------------- + +Generic ACPI GPE block. Bit 1 (GPE.1) used to notify PCI hotplug/eject +event to ACPI BIOS, via SCI interrupt. + +PCI slot injection notification pending (IO port 0xae00-0xae03, 4-byte access): +--------------------------------------------------------------- +Slot injection notification pending. One bit per slot. + +Read by ACPI BIOS GPE.1 handler to notify OS of injection +events. Read-only. + +PCI slot removal notification (IO port 0xae04-0xae07, 4-byte access): +----------------------------------------------------- +Slot removal notification pending. One bit per slot. + +Read by ACPI BIOS GPE.1 handler to notify OS of removal +events. Read-only. + +PCI device eject (IO port 0xae08-0xae0b, 4-byte access): +---------------------------------------- + +Write: Used by ACPI BIOS _EJ0 method to request device removal. +One bit per slot. + +Read: Hotplug features register. Used by platform to identify features +available. Current base feature set (no bits set): + - Read-only "up" register @0xae00, 4-byte access, bit per slot + - Read-only "down" register @0xae04, 4-byte access, bit per slot + - Read/write "eject" register @0xae08, 4-byte access, + write: bit per slot eject, read: hotplug feature set + - Read-only hotplug capable register @0xae0c, 4-byte access, bit per slot + +PCI removability status (IO port 0xae0c-0xae0f, 4-byte access): +----------------------------------------------- + +Used by ACPI BIOS _RMV method to indicate removability status to OS. One +bit per slot. Read-only diff --git a/docs/specs/edu.txt b/docs/specs/edu.txt new file mode 100644 index 0000000..7f81467 --- /dev/null +++ b/docs/specs/edu.txt @@ -0,0 +1,110 @@ + +EDU device +========== + +Copyright (c) 2014-2015 Jiri Slaby + +This document is licensed under the GPLv2 (or later). + +This is an educational device for writing (kernel) drivers. Its original +intention was to support the Linux kernel lectures taught at the Masaryk +University. Students are given this virtual device and are expected to write a +driver with I/Os, IRQs, DMAs and such. + +The devices behaves very similar to the PCI bridge present in the COMBO6 cards +developed under the Liberouter wings. Both PCI device ID and PCI space is +inherited from that device. + +Command line switches: + -device edu[,dma_mask=mask] + + dma_mask makes the virtual device work with DMA addresses with the given + mask. For educational purposes, the device supports only 28 bits (256 MiB) + by default. Students shall set dma_mask for the device in the OS driver + properly. + +PCI specs +--------- + +PCI ID: 1234:11e8 + +PCI Region 0: + I/O memory, 1 MB in size. Users are supposed to communicate with the card + through this memory. + +MMIO area spec +-------------- + +Only size == 4 accesses are allowed for addresses < 0x80. size == 4 or +size == 8 for the rest. + +0x00 (RO) : identification (0xRRrr00edu) + RR -- major version + rr -- minor version + +0x04 (RW) : card liveness check + It is a simple value inversion (~ C operator). + +0x08 (RW) : factorial computation + The stored value is taken and factorial of it is put back here. + This happens only after factorial bit in the status register (0x20 + below) is cleared. + +0x20 (RW) : status register, bitwise OR + 0x01 -- computing factorial (RO) + 0x80 -- raise interrupt 0x01 after finishing factorial computation + +0x24 (RO) : interrupt status register + It contains values which raised the interrupt (see interrupt raise + register below). + +0x60 (WO) : interrupt raise register + Raise an interrupt. The value will be put to the interrupt status + register (using bitwise OR). + +0x64 (WO) : interrupt acknowledge register + Clear an interrupt. The value will be cleared from the interrupt + status register. This needs to be done from the ISR to stop + generating interrupts. + +0x80 (RW) : DMA source address + Where to perform the DMA from. + +0x88 (RW) : DMA destination address + Where to perform the DMA to. + +0x90 (RW) : DMA transfer count + The size of the area to perform the DMA on. + +0x98 (RW) : DMA command register, bitwise OR + 0x01 -- start transfer + 0x02 -- direction (0: from RAM to EDU, 1: from EDU to RAM) + 0x04 -- raise interrupt 0x100 after finishing the DMA + +IRQ controller +-------------- +An IRQ is generated when written to the interrupt raise register. The value +appears in interrupt status register when the interrupt is raised and has to +be written to the interrupt acknowledge register to lower it. + +DMA controller +-------------- +One has to specify, source, destination, size, and start the transfer. One +4096 bytes long buffer at offset 0x40000 is available in the EDU device. I.e. +one can perform DMA to/from this space when programmed properly. + +Example of transferring a 100 byte block to and from the buffer using a given +PCI address 'addr': +addr -> DMA source address +0x40000 -> DMA destination address +100 -> DMA transfer count +1 -> DMA command register +while (DMA command register & 1) + ; + +0x40000 -> DMA source address +addr+100 -> DMA destination address +100 -> DMA transfer count +3 -> DMA command register +while (DMA command register & 1) + ; diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt new file mode 100644 index 0000000..6accd92 --- /dev/null +++ b/docs/specs/fw_cfg.txt @@ -0,0 +1,205 @@ +QEMU Firmware Configuration (fw_cfg) Device +=========================================== + += Guest-side Hardware Interface = + +This hardware interface allows the guest to retrieve various data items +(blobs) that can influence how the firmware configures itself, or may +contain tables to be installed for the guest OS. Examples include device +boot order, ACPI and SMBIOS tables, virtual machine UUID, SMP and NUMA +information, kernel/initrd images for direct (Linux) kernel booting, etc. + +== Selector (Control) Register == + +* Write only +* Location: platform dependent (IOport or MMIO) +* Width: 16-bit +* Endianness: little-endian (if IOport), or big-endian (if MMIO) + +A write to this register sets the index of a firmware configuration +item which can subsequently be accessed via the data register. + +Setting the selector register will cause the data offset to be set +to zero. The data offset impacts which data is accessed via the data +register, and is explained below. + +Bit14 of the selector register indicates whether the configuration +setting is being written. A value of 0 means the item is only being +read, and all write access to the data port will be ignored. A value +of 1 means the item's data can be overwritten by writes to the data +register. In other words, configuration write mode is enabled when +the selector value is between 0x4000-0x7fff or 0xc000-0xffff. + +NOTE: As of QEMU v2.4, writes to the fw_cfg data register are no + longer supported, and will be ignored (treated as no-ops)! + +Bit15 of the selector register indicates whether the configuration +setting is architecture specific. A value of 0 means the item is a +generic configuration item. A value of 1 means the item is specific +to a particular architecture. In other words, generic configuration +items are accessed with a selector value between 0x0000-0x7fff, and +architecture specific configuration items are accessed with a selector +value between 0x8000-0xffff. + +== Data Register == + +* Read/Write (writes ignored as of QEMU v2.4) +* Location: platform dependent (IOport [*] or MMIO) +* Width: 8-bit (if IOport), 8/16/32/64-bit (if MMIO) +* Endianness: string-preserving + +[*] On platforms where the data register is exposed as an IOport, its +port number will always be one greater than the port number of the +selector register. In other words, the two ports overlap, and can not +be mapped separately. + +The data register allows access to an array of bytes for each firmware +configuration data item. The specific item is selected by writing to +the selector register, as described above. + +Initially following a write to the selector register, the data offset +will be set to zero. Each successful access to the data register will +increment the data offset by the appropriate access width. + +Each firmware configuration item has a maximum length of data +associated with the item. After the data offset has passed the +end of this maximum data length, then any reads will return a data +value of 0x00, and all writes will be ignored. + +An N-byte wide read of the data register will return the next available +N bytes of the selected firmware configuration item, as a substring, in +increasing address order, similar to memcpy(). + +== Register Locations == + +=== x86, x86_64 Register Locations === + +Selector Register IOport: 0x510 +Data Register IOport: 0x511 + +== Firmware Configuration Items == + +=== Signature (Key 0x0000, FW_CFG_SIGNATURE) === + +The presence of the fw_cfg selector and data registers can be verified +by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), +and reading four bytes from the data register. If the fw_cfg device is +present, the four bytes read will contain the characters "QEMU". + +=== Revision (Key 0x0001, FW_CFG_ID) === + +A 32-bit little-endian unsigned int, this item is used as an interface +revision number, and is currently set to 1 by QEMU when fw_cfg is +initialized. + +=== File Directory (Key 0x0019, FW_CFG_FILE_DIR) === + +Firmware configuration items stored at selector keys 0x0020 or higher +(FW_CFG_FILE_FIRST or higher) have an associated entry in a directory +structure, which makes it easier for guest-side firmware to identify +and retrieve them. The format of this file directory (from fw_cfg.h in +the QEMU source tree) is shown here, slightly annotated for clarity: + +struct FWCfgFiles { /* the entire file directory fw_cfg item */ + uint32_t count; /* number of entries, in big-endian format */ + struct FWCfgFile f[]; /* array of file entries, see below */ +}; + +struct FWCfgFile { /* an individual file entry, 64 bytes total */ + uint32_t size; /* size of referenced fw_cfg item, big-endian */ + uint16_t select; /* selector key of fw_cfg item, big-endian */ + uint16_t reserved; + char name[56]; /* fw_cfg item name, NUL-terminated ascii */ +}; + +=== All Other Data Items === + +Please consult the QEMU source for the most up-to-date and authoritative +list of selector keys and their respective items' purpose and format. + +=== Ranges === + +Theoretically, there may be up to 0x4000 generic firmware configuration +items, and up to 0x4000 architecturally specific ones. + +Selector Reg. Range Usage +--------------- ----------- +0x0000 - 0x3fff Generic (0x0000 - 0x3fff, RO) +0x4000 - 0x7fff Generic (0x0000 - 0x3fff, RW, ignored in QEMU v2.4+) +0x8000 - 0xbfff Arch. Specific (0x0000 - 0x3fff, RO) +0xc000 - 0xffff Arch. Specific (0x0000 - 0x3fff, RW, ignored in v2.4+) + +In practice, the number of allowed firmware configuration items is given +by the value of FW_CFG_MAX_ENTRY (see fw_cfg.h). + += Host-side API = + +The following functions are available to the QEMU programmer for adding +data to a fw_cfg device during guest initialization (see fw_cfg.h for +each function's complete prototype): + +== fw_cfg_add_bytes() == + +Given a selector key value, starting pointer, and size, create an item +as a raw "blob" of the given size, available by selecting the given key. +The data referenced by the starting pointer is only linked, NOT copied, +into the data structure of the fw_cfg device. + +== fw_cfg_add_string() == + +Instead of a starting pointer and size, this function accepts a pointer +to a NUL-terminated ascii string, and inserts a newly allocated copy of +the string (including the NUL terminator) into the fw_cfg device data +structure. + +== fw_cfg_add_iXX() == + +Insert an XX-bit item, where XX may be 16, 32, or 64. These functions +will convert a 16-, 32-, or 64-bit integer to little-endian, then add +a dynamically allocated copy of the appropriately sized item to fw_cfg +under the given selector key value. + +== fw_cfg_add_file() == + +Given a filename (i.e., fw_cfg item name), starting pointer, and size, +create an item as a raw "blob" of the given size. Unlike fw_cfg_add_bytes() +above, the next available selector key (above 0x0020, FW_CFG_FILE_FIRST) +will be used, and a new entry will be added to the file directory structure +(at key 0x0019), containing the item name, blob size, and automatically +assigned selector key value. The data referenced by the starting pointer +is only linked, NOT copied, into the fw_cfg data structure. + +== fw_cfg_add_file_callback() == + +Like fw_cfg_add_file(), but additionally sets pointers to a callback +function (and opaque argument), which will be executed host-side by +QEMU each time a byte is read by the guest from this particular item. + +NOTE: The callback function is given the opaque argument set by +fw_cfg_add_file_callback(), but also the current data offset, +allowing it the option of only acting upon specific offset values +(e.g., 0, before the first data byte of the selected item is +returned to the guest). + +== fw_cfg_modify_file() == + +Given a filename (i.e., fw_cfg item name), starting pointer, and size, +completely replace the configuration item referenced by the given item +name with the new given blob. If an existing blob is found, its +callback information is removed, and a pointer to the old data is +returned to allow the caller to free it, helping avoid memory leaks. +If a configuration item does not already exist under the given item +name, a new item will be created as with fw_cfg_add_file(), and NULL +is returned to the caller. In any case, the data referenced by the +starting pointer is only linked, NOT copied, into the fw_cfg data +structure. + +== fw_cfg_add_callback() == + +Like fw_cfg_add_bytes(), but additionally sets pointers to a callback +function (and opaque argument), which will be executed host-side by +QEMU each time a guest-side write operation to this particular item +completes fully overwriting the item's data. + +NOTE: This function is deprecated, and will be completely removed +starting with QEMU v2.4. diff --git a/docs/specs/ivshmem_device_spec.txt b/docs/specs/ivshmem_device_spec.txt new file mode 100644 index 0000000..667a862 --- /dev/null +++ b/docs/specs/ivshmem_device_spec.txt @@ -0,0 +1,96 @@ + +Device Specification for Inter-VM shared memory device +------------------------------------------------------ + +The Inter-VM shared memory device is designed to share a region of memory to +userspace in multiple virtual guests. The memory region does not belong to any +guest, but is a POSIX memory object on the host. Optionally, the device may +support sending interrupts to other guests sharing the same memory region. + + +The Inter-VM PCI device +----------------------- + +*BARs* + +The device supports three BARs. BAR0 is a 1 Kbyte MMIO region to support +registers. BAR1 is used for MSI-X when it is enabled in the device. BAR2 is +used to map the shared memory object from the host. The size of BAR2 is +specified when the guest is started and must be a power of 2 in size. + +*Registers* + +The device currently supports 4 registers of 32-bits each. Registers +are used for synchronization between guests sharing the same memory object when +interrupts are supported (this requires using the shared memory server). + +The server assigns each VM an ID number and sends this ID number to the QEMU +process when the guest starts. + +enum ivshmem_registers { + IntrMask = 0, + IntrStatus = 4, + IVPosition = 8, + Doorbell = 12 +}; + +The first two registers are the interrupt mask and status registers. Mask and +status are only used with pin-based interrupts. They are unused with MSI +interrupts. + +Status Register: The status register is set to 1 when an interrupt occurs. + +Mask Register: The mask register is bitwise ANDed with the interrupt status +and the result will raise an interrupt if it is non-zero. However, since 1 is +the only value the status will be set to, it is only the first bit of the mask +that has any effect. Therefore interrupts can be masked by setting the first +bit to 0 and unmasked by setting the first bit to 1. + +IVPosition Register: The IVPosition register is read-only and reports the +guest's ID number. The guest IDs are non-negative integers. When using the +server, since the server is a separate process, the VM ID will only be set when +the device is ready (shared memory is received from the server and accessible via +the device). If the device is not ready, the IVPosition will return -1. +Applications should ensure that they have a valid VM ID before accessing the +shared memory. + +Doorbell Register: To interrupt another guest, a guest must write to the +Doorbell register. The doorbell register is 32-bits, logically divided into +two 16-bit fields. The high 16-bits are the guest ID to interrupt and the low +16-bits are the interrupt vector to trigger. The semantics of the value +written to the doorbell depends on whether the device is using MSI or a regular +pin-based interrupt. In short, MSI uses vectors while regular interrupts set the +status register. + +Regular Interrupts + +If regular interrupts are used (due to either a guest not supporting MSI or the +user specifying not to use them on startup) then the value written to the lower +16-bits of the Doorbell register results is arbitrary and will trigger an +interrupt in the destination guest. + +Message Signalled Interrupts + +A ivshmem device may support multiple MSI vectors. If so, the lower 16-bits +written to the Doorbell register must be between 0 and the maximum number of +vectors the guest supports. The lower 16 bits written to the doorbell is the +MSI vector that will be raised in the destination guest. The number of MSI +vectors is configurable but it is set when the VM is started. + +The important thing to remember with MSI is that it is only a signal, no status +is set (since MSI interrupts are not shared). All information other than the +interrupt itself should be communicated via the shared memory region. Devices +supporting multiple MSI vectors can use different vectors to indicate different +events have occurred. The semantics of interrupt vectors are left to the +user's discretion. + + +Usage in the Guest +------------------ + +The shared memory device is intended to be used with the provided UIO driver. +Very little configuration is needed. The guest should map BAR0 to access the +registers (an array of 32-bit ints allows simple writing) and map BAR2 to +access the shared memory region itself. The size of the shared memory region +is specified when the guest (or shared memory server) is started. A guest may +map the whole shared memory region or only part of it. diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt new file mode 100644 index 0000000..e4a4490 --- /dev/null +++ b/docs/specs/pci-ids.txt @@ -0,0 +1,53 @@ + +PCI IDs for qemu +================ + +Red Hat, Inc. donates a part of its device ID range to qemu, to be used for +virtual devices. The vendor IDs are 1af4 (formerly Qumranet ID) and 1b36. + +Contact Gerd Hoffmann to get a device ID assigned +for your devices. + +1af4 vendor ID +-------------- + +The 1000 -> 10ff device ID range is used as follows for virtio-pci devices. +Note that this allocation separate from the virtio device IDs, which are +maintained as part of the virtio specification. + +1af4:1000 network device +1af4:1001 block device +1af4:1002 balloon device +1af4:1003 console device +1af4:1004 SCSI host bus adapter device +1af4:1005 entropy generator device +1af4:1009 9p filesystem device + +1af4:10f0 Available for experimental usage without registration. Must get + to official ID when the code leaves the test lab (i.e. when seeking +1af4:10ff upstream merge or shipping a distro/product) to avoid conflicts. + +1af4:1100 Used as PCI Subsystem ID for existing hardware devices emulated + by qemu. + +1af4:1110 ivshmem device (shared memory, docs/specs/ivshmem_device_spec.txt) + +All other device IDs are reserved. + +1b36 vendor ID +-------------- + +The 0000 -> 00ff device ID range is used as follows for QEMU-specific +PCI devices (other than virtio): + +1b36:0001 PCI-PCI bridge +1b36:0002 PCI serial port (16550A) adapter (docs/specs/pci-serial.txt) +1b36:0003 PCI Dual-port 16550A adapter (docs/specs/pci-serial.txt) +1b36:0004 PCI Quad-port 16550A adapter (docs/specs/pci-serial.txt) +1b36:0005 PCI test device (docs/specs/pci-testdev.txt) +1b36:0006 PCI Rocker Ethernet switch device +1b36:0007 PCI SD Card Host Controller Interface (SDHCI) + +All these devices are documented in docs/specs. + +The 0100 device ID is used for the QXL video card device. diff --git a/docs/specs/pci-serial.txt b/docs/specs/pci-serial.txt new file mode 100644 index 0000000..66c761f --- /dev/null +++ b/docs/specs/pci-serial.txt @@ -0,0 +1,34 @@ + +QEMU pci serial devices +======================= + +There is one single-port variant and two muliport-variants. Linux +guests out-of-the box with all cards. There is a Windows inf file +(docs/qemupciserial.inf) to setup the single-port card in Windows +guests. + + +single-port card +---------------- + +Name: pci-serial +PCI ID: 1b36:0002 + +PCI Region 0: + IO bar, 8 bytes long, with the 16550 uart mapped to it. + Interrupt is wired to pin A. + + +multiport cards +--------------- + +Name: pci-serial-2x +PCI ID: 1b36:0003 + +Name: pci-serial-4x +PCI ID: 1b36:0004 + +PCI Region 0: + IO bar, with two/four 16550 uart mapped after each other. + The first is at offset 0, second at offset 8, ... + Interrupt is wired to pin A. diff --git a/docs/specs/pci-testdev.txt b/docs/specs/pci-testdev.txt new file mode 100644 index 0000000..128ae22 --- /dev/null +++ b/docs/specs/pci-testdev.txt @@ -0,0 +1,26 @@ +pci-test is a device used for testing low level IO + +device implements up to two BARs: BAR0 and BAR1. +Each BAR can be memory or IO. Guests must detect +BAR type and act accordingly. + +Each BAR size is up to 4K bytes. +Each BAR starts with the following header: + +typedef struct PCITestDevHdr { + uint8_t test; <- write-only, starts a given test number + uint8_t width_type; <- read-only, type and width of access for a given test. + 1,2,4 for byte,word or long write. + any other value if test not supported on this BAR + uint8_t pad0[2]; + uint32_t offset; <- read-only, offset in this BAR for a given test + uint32_t data; <- read-only, data to use for a given test + uint32_t count; <- for debugging. number of writes detected. + uint8_t name[]; <- for debugging. 0-terminated ASCII string. +} PCITestDevHdr; + +All registers are little endian. + +device is expected to always implement tests 0 to N on each BAR, and to add new +tests with higher numbers. In this way a guest can scan test numbers until it +detects an access type that it does not support on this BAR, then stop. diff --git a/docs/specs/ppc-spapr-hcalls.txt b/docs/specs/ppc-spapr-hcalls.txt new file mode 100644 index 0000000..667b3fa --- /dev/null +++ b/docs/specs/ppc-spapr-hcalls.txt @@ -0,0 +1,78 @@ +When used with the "pseries" machine type, QEMU-system-ppc64 implements +a set of hypervisor calls using a subset of the server "PAPR" specification +(IBM internal at this point), which is also what IBM's proprietary hypervisor +adheres too. + +The subset is selected based on the requirements of Linux as a guest. + +In addition to those calls, we have added our own private hypervisor +calls which are mostly used as a private interface between the firmware +running in the guest and QEMU. + +All those hypercalls start at hcall number 0xf000 which correspond +to a implementation specific range in PAPR. + +- H_RTAS (0xf000) + +RTAS is a set of runtime services generally provided by the firmware +inside the guest to the operating system. It predates the existence +of hypervisors (it was originally an extension to Open Firmware) and +is still used by PAPR to provide various services that aren't performance +sensitive. + +We currently implement the RTAS services in QEMU itself. The actual RTAS +"firmware" blob in the guest is a small stub of a few instructions which +calls our private H_RTAS hypervisor call to pass the RTAS calls to QEMU. + +Arguments: + + r3 : H_RTAS (0xf000) + r4 : Guest physical address of RTAS parameter block + +Returns: + + H_SUCCESS : Successfully called the RTAS function (RTAS result + will have been stored in the parameter block) + H_PARAMETER : Unknown token + +- H_LOGICAL_MEMOP (0xf001) + +When the guest runs in "real mode" (in powerpc lingua this means +with MMU disabled, ie guest effective == guest physical), it only +has access to a subset of memory and no IOs. + +PAPR provides a set of hypervisor calls to perform cachable or +non-cachable accesses to any guest physical addresses that the +guest can use in order to access IO devices while in real mode. + +This is typically used by the firmware running in the guest. + +However, doing a hypercall for each access is extremely inefficient +(even more so when running KVM) when accessing the frame buffer. In +that case, things like scrolling become unusably slow. + +This hypercall allows the guest to request a "memory op" to be applied +to memory. The supported memory ops at this point are to copy a range +of memory (supports overlap of source and destination) and XOR which +is used by our SLOF firmware to invert the screen. + +Arguments: + + r3: H_LOGICAL_MEMOP (0xf001) + r4: Guest physical address of destination + r5: Guest physical address of source + r6: Individual element size + 0 = 1 byte + 1 = 2 bytes + 2 = 4 bytes + 3 = 8 bytes + r7: Number of elements + r8: Operation + 0 = copy + 1 = xor + +Returns: + + H_SUCCESS : Success + H_PARAMETER : Invalid argument + diff --git a/docs/specs/pvpanic.txt b/docs/specs/pvpanic.txt new file mode 100644 index 0000000..c7bbacc --- /dev/null +++ b/docs/specs/pvpanic.txt @@ -0,0 +1,39 @@ +PVPANIC DEVICE +============== + +pvpanic device is a simulated ISA device, through which a guest panic +event is sent to qemu, and a QMP event is generated. This allows +management apps (e.g. libvirt) to be notified and respond to the event. + +The management app has the option of waiting for GUEST_PANICKED events, +and/or polling for guest-panicked RunState, to learn when the pvpanic +device has fired a panic event. + +ISA Interface +------------- + +pvpanic exposes a single I/O port, by default 0x505. On read, the bits +recognized by the device are set. Software should ignore bits it doesn't +recognize. On write, the bits not recognized by the device are ignored. +Software should set only bits both itself and the device recognize. +Currently, only bit 0 is recognized, setting it indicates a guest panic +has happened. + +ACPI Interface +-------------- + +pvpanic device is defined with ACPI ID "QEMU0001". Custom methods: + +RDPT: To determine whether guest panic notification is supported. +Arguments: None +Return: Returns a byte, bit 0 set to indicate guest panic + notification is supported. Other bits are reserved and + should be ignored. + +WRPT: To send a guest panic event +Arguments: Arg0 is a byte, with bit 0 set to indicate guest panic has + happened. Other bits are reserved and should be cleared. +Return: None + +The ACPI device will automatically refer to the right port in case it +is modified. diff --git a/docs/specs/qcow2.txt b/docs/specs/qcow2.txt new file mode 100644 index 0000000..121dfc8 --- /dev/null +++ b/docs/specs/qcow2.txt @@ -0,0 +1,362 @@ +== General == + +A qcow2 image file is organized in units of constant size, which are called +(host) clusters. A cluster is the unit in which all allocations are done, +both for actual guest data and for image metadata. + +Likewise, the virtual disk as seen by the guest is divided into (guest) +clusters of the same size. + +All numbers in qcow2 are stored in Big Endian byte order. + + +== Header == + +The first cluster of a qcow2 image contains the file header: + + Byte 0 - 3: magic + QCOW magic string ("QFI\xfb") + + 4 - 7: version + Version number (valid values are 2 and 3) + + 8 - 15: backing_file_offset + Offset into the image file at which the backing file name + is stored (NB: The string is not null terminated). 0 if the + image doesn't have a backing file. + + 16 - 19: backing_file_size + Length of the backing file name in bytes. Must not be + longer than 1023 bytes. Undefined if the image doesn't have + a backing file. + + 20 - 23: cluster_bits + Number of bits that are used for addressing an offset + within a cluster (1 << cluster_bits is the cluster size). + Must not be less than 9 (i.e. 512 byte clusters). + + Note: qemu as of today has an implementation limit of 2 MB + as the maximum cluster size and won't be able to open images + with larger cluster sizes. + + 24 - 31: size + Virtual disk size in bytes + + 32 - 35: crypt_method + 0 for no encryption + 1 for AES encryption + + 36 - 39: l1_size + Number of entries in the active L1 table + + 40 - 47: l1_table_offset + Offset into the image file at which the active L1 table + starts. Must be aligned to a cluster boundary. + + 48 - 55: refcount_table_offset + Offset into the image file at which the refcount table + starts. Must be aligned to a cluster boundary. + + 56 - 59: refcount_table_clusters + Number of clusters that the refcount table occupies + + 60 - 63: nb_snapshots + Number of snapshots contained in the image + + 64 - 71: snapshots_offset + Offset into the image file at which the snapshot table + starts. Must be aligned to a cluster boundary. + +If the version is 3 or higher, the header has the following additional fields. +For version 2, the values are assumed to be zero, unless specified otherwise +in the description of a field. + + 72 - 79: incompatible_features + Bitmask of incompatible features. An implementation must + fail to open an image if an unknown bit is set. + + Bit 0: Dirty bit. If this bit is set then refcounts + may be inconsistent, make sure to scan L1/L2 + tables to repair refcounts before accessing the + image. + + Bit 1: Corrupt bit. If this bit is set then any data + structure may be corrupt and the image must not + be written to (unless for regaining + consistency). + + Bits 2-63: Reserved (set to 0) + + 80 - 87: compatible_features + Bitmask of compatible features. An implementation can + safely ignore any unknown bits that are set. + + Bit 0: Lazy refcounts bit. If this bit is set then + lazy refcount updates can be used. This means + marking the image file dirty and postponing + refcount metadata updates. + + Bits 1-63: Reserved (set to 0) + + 88 - 95: autoclear_features + Bitmask of auto-clear features. An implementation may only + write to an image with unknown auto-clear features if it + clears the respective bits from this field first. + + Bits 0-63: Reserved (set to 0) + + 96 - 99: refcount_order + Describes the width of a reference count block entry (width + in bits: refcount_bits = 1 << refcount_order). For version 2 + images, the order is always assumed to be 4 + (i.e. refcount_bits = 16). + This value may not exceed 6 (i.e. refcount_bits = 64). + + 100 - 103: header_length + Length of the header structure in bytes. For version 2 + images, the length is always assumed to be 72 bytes. + +Directly after the image header, optional sections called header extensions can +be stored. Each extension has a structure like the following: + + Byte 0 - 3: Header extension type: + 0x00000000 - End of the header extension area + 0xE2792ACA - Backing file format name + 0x6803f857 - Feature name table + other - Unknown header extension, can be safely + ignored + + 4 - 7: Length of the header extension data + + 8 - n: Header extension data + + n - m: Padding to round up the header extension size to the next + multiple of 8. + +Unless stated otherwise, each header extension type shall appear at most once +in the same image. + +If the image has a backing file then the backing file name should be stored in +the remaining space between the end of the header extension area and the end of +the first cluster. It is not allowed to store other data here, so that an +implementation can safely modify the header and add extensions without harming +data of compatible features that it doesn't support. Compatible features that +need space for additional data can use a header extension. + + +== Feature name table == + +The feature name table is an optional header extension that contains the name +for features used by the image. It can be used by applications that don't know +the respective feature (e.g. because the feature was introduced only later) to +display a useful error message. + +The number of entries in the feature name table is determined by the length of +the header extension data. Each entry look like this: + + Byte 0: Type of feature (select feature bitmap) + 0: Incompatible feature + 1: Compatible feature + 2: Autoclear feature + + 1: Bit number within the selected feature bitmap (valid + values: 0-63) + + 2 - 47: Feature name (padded with zeros, but not necessarily null + terminated if it has full length) + + +== Host cluster management == + +qcow2 manages the allocation of host clusters by maintaining a reference count +for each host cluster. A refcount of 0 means that the cluster is free, 1 means +that it is used, and >= 2 means that it is used and any write access must +perform a COW (copy on write) operation. + +The refcounts are managed in a two-level table. The first level is called +refcount table and has a variable size (which is stored in the header). The +refcount table can cover multiple clusters, however it needs to be contiguous +in the image file. + +It contains pointers to the second level structures which are called refcount +blocks and are exactly one cluster in size. + +Given a offset into the image file, the refcount of its cluster can be obtained +as follows: + + refcount_block_entries = (cluster_size * 8 / refcount_bits) + + refcount_block_index = (offset / cluster_size) % refcount_block_entries + refcount_table_index = (offset / cluster_size) / refcount_block_entries + + refcount_block = load_cluster(refcount_table[refcount_table_index]); + return refcount_block[refcount_block_index]; + +Refcount table entry: + + Bit 0 - 8: Reserved (set to 0) + + 9 - 63: Bits 9-63 of the offset into the image file at which the + refcount block starts. Must be aligned to a cluster + boundary. + + If this is 0, the corresponding refcount block has not yet + been allocated. All refcounts managed by this refcount block + are 0. + +Refcount block entry (x = refcount_bits - 1): + + Bit 0 - x: Reference count of the cluster. If refcount_bits implies a + sub-byte width, note that bit 0 means the least significant + bit in this context. + + +== Cluster mapping == + +Just as for refcounts, qcow2 uses a two-level structure for the mapping of +guest clusters to host clusters. They are called L1 and L2 table. + +The L1 table has a variable size (stored in the header) and may use multiple +clusters, however it must be contiguous in the image file. L2 tables are +exactly one cluster in size. + +Given a offset into the virtual disk, the offset into the image file can be +obtained as follows: + + l2_entries = (cluster_size / sizeof(uint64_t)) + + l2_index = (offset / cluster_size) % l2_entries + l1_index = (offset / cluster_size) / l2_entries + + l2_table = load_cluster(l1_table[l1_index]); + cluster_offset = l2_table[l2_index]; + + return cluster_offset + (offset % cluster_size) + +L1 table entry: + + Bit 0 - 8: Reserved (set to 0) + + 9 - 55: Bits 9-55 of the offset into the image file at which the L2 + table starts. Must be aligned to a cluster boundary. If the + offset is 0, the L2 table and all clusters described by this + L2 table are unallocated. + + 56 - 62: Reserved (set to 0) + + 63: 0 for an L2 table that is unused or requires COW, 1 if its + refcount is exactly one. This information is only accurate + in the active L1 table. + +L2 table entry: + + Bit 0 - 61: Cluster descriptor + + 62: 0 for standard clusters + 1 for compressed clusters + + 63: 0 for a cluster that is unused or requires COW, 1 if its + refcount is exactly one. This information is only accurate + in L2 tables that are reachable from the the active L1 + table. + +Standard Cluster Descriptor: + + Bit 0: If set to 1, the cluster reads as all zeros. The host + cluster offset can be used to describe a preallocation, + but it won't be used for reading data from this cluster, + nor is data read from the backing file if the cluster is + unallocated. + + With version 2, this is always 0. + + 1 - 8: Reserved (set to 0) + + 9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a + cluster boundary. If the offset is 0, the cluster is + unallocated. + + 56 - 61: Reserved (set to 0) + + +Compressed Clusters Descriptor (x = 62 - (cluster_bits - 8)): + + Bit 0 - x: Host cluster offset. This is usually _not_ aligned to a + cluster boundary! + + x+1 - 61: Compressed size of the images in sectors of 512 bytes + +If a cluster is unallocated, read requests shall read the data from the backing +file (except if bit 0 in the Standard Cluster Descriptor is set). If there is +no backing file or the backing file is smaller than the image, they shall read +zeros for all parts that are not covered by the backing file. + + +== Snapshots == + +qcow2 supports internal snapshots. Their basic principle of operation is to +switch the active L1 table, so that a different set of host clusters are +exposed to the guest. + +When creating a snapshot, the L1 table should be copied and the refcount of all +L2 tables and clusters reachable from this L1 table must be increased, so that +a write causes a COW and isn't visible in other snapshots. + +When loading a snapshot, bit 63 of all entries in the new active L1 table and +all L2 tables referenced by it must be reconstructed from the refcount table +as it doesn't need to be accurate in inactive L1 tables. + +A directory of all snapshots is stored in the snapshot table, a contiguous area +in the image file, whose starting offset and length are given by the header +fields snapshots_offset and nb_snapshots. The entries of the snapshot table +have variable length, depending on the length of ID, name and extra data. + +Snapshot table entry: + + Byte 0 - 7: Offset into the image file at which the L1 table for the + snapshot starts. Must be aligned to a cluster boundary. + + 8 - 11: Number of entries in the L1 table of the snapshots + + 12 - 13: Length of the unique ID string describing the snapshot + + 14 - 15: Length of the name of the snapshot + + 16 - 19: Time at which the snapshot was taken in seconds since the + Epoch + + 20 - 23: Subsecond part of the time at which the snapshot was taken + in nanoseconds + + 24 - 31: Time that the guest was running until the snapshot was + taken in nanoseconds + + 32 - 35: Size of the VM state in bytes. 0 if no VM state is saved. + If there is VM state, it starts at the first cluster + described by first L1 table entry that doesn't describe a + regular guest cluster (i.e. VM state is stored like guest + disk content, except that it is stored at offsets that are + larger than the virtual disk presented to the guest) + + 36 - 39: Size of extra data in the table entry (used for future + extensions of the format) + + variable: Extra data for future extensions. Unknown fields must be + ignored. Currently defined are (offset relative to snapshot + table entry): + + Byte 40 - 47: Size of the VM state in bytes. 0 if no VM + state is saved. If this field is present, + the 32-bit value in bytes 32-35 is ignored. + + Byte 48 - 55: Virtual disk size of the snapshot in bytes + + Version 3 images must include extra data at least up to + byte 55. + + variable: Unique ID string for the snapshot (not null terminated) + + variable: Name of the snapshot (not null terminated) + + variable: Padding to round up the snapshot table entry size to the + next multiple of 8. diff --git a/docs/specs/qed_spec.txt b/docs/specs/qed_spec.txt new file mode 100644 index 0000000..7982e05 --- /dev/null +++ b/docs/specs/qed_spec.txt @@ -0,0 +1,138 @@ +=Specification= + +The file format looks like this: + + +----------+----------+----------+-----+ + | cluster0 | cluster1 | cluster2 | ... | + +----------+----------+----------+-----+ + +The first cluster begins with the '''header'''. The header contains information about where regular clusters start; this allows the header to be extensible and store extra information about the image file. A regular cluster may be a '''data cluster''', an '''L2''', or an '''L1 table'''. L1 and L2 tables are composed of one or more contiguous clusters. + +Normally the file size will be a multiple of the cluster size. If the file size is not a multiple, extra information after the last cluster may not be preserved if data is written. Legitimate extra information should use space between the header and the first regular cluster. + +All fields are little-endian. + +==Header== + Header { + uint32_t magic; /* QED\0 */ + + uint32_t cluster_size; /* in bytes */ + uint32_t table_size; /* for L1 and L2 tables, in clusters */ + uint32_t header_size; /* in clusters */ + + uint64_t features; /* format feature bits */ + uint64_t compat_features; /* compat feature bits */ + uint64_t autoclear_features; /* self-resetting feature bits */ + + uint64_t l1_table_offset; /* in bytes */ + uint64_t image_size; /* total logical image size, in bytes */ + + /* if (features & QED_F_BACKING_FILE) */ + uint32_t backing_filename_offset; /* in bytes from start of header */ + uint32_t backing_filename_size; /* in bytes */ + } + +Field descriptions: +* ''cluster_size'' must be a power of 2 in range [2^12, 2^26]. +* ''table_size'' must be a power of 2 in range [1, 16]. +* ''header_size'' is the number of clusters used by the header and any additional information stored before regular clusters. +* ''features'', ''compat_features'', and ''autoclear_features'' are file format extension bitmaps. They work as follows: +** An image with unknown ''features'' bits enabled must not be opened. File format changes that are not backwards-compatible must use ''features'' bits. +** An image with unknown ''compat_features'' bits enabled can be opened safely. The unknown features are simply ignored and represent backwards-compatible changes to the file format. +** An image with unknown ''autoclear_features'' bits enable can be opened safely after clearing the unknown bits. This allows for backwards-compatible changes to the file format which degrade gracefully and can be re-enabled again by a new program later. +* ''l1_table_offset'' is the offset of the first byte of the L1 table in the image file and must be a multiple of ''cluster_size''. +* ''image_size'' is the block device size seen by the guest and must be a multiple of 512 bytes. +* ''backing_filename_offset'' and ''backing_filename_size'' describe a string in (byte offset, byte size) form. It is not NUL-terminated and has no alignment constraints. The string must be stored within the first ''header_size'' clusters. The backing filename may be an absolute path or relative to the image file. + +Feature bits: +* QED_F_BACKING_FILE = 0x01. The image uses a backing file. +* QED_F_NEED_CHECK = 0x02. The image needs a consistency check before use. +* QED_F_BACKING_FORMAT_NO_PROBE = 0x04. The backing file is a raw disk image and no file format autodetection should be attempted. This should be used to ensure that raw backing files are never detected as an image format if they happen to contain magic constants. + +There are currently no defined ''compat_features'' or ''autoclear_features'' bits. + +Fields predicated on a feature bit are only used when that feature is set. The fields always take up header space, regardless of whether or not the feature bit is set. + +==Tables== + +Tables provide the translation from logical offsets in the block device to cluster offsets in the file. + + #define TABLE_NOFFSETS (table_size * cluster_size / sizeof(uint64_t)) + + Table { + uint64_t offsets[TABLE_NOFFSETS]; + } + +The tables are organized as follows: + + +----------+ + | L1 table | + +----------+ + ,------' | '------. + +----------+ | +----------+ + | L2 table | ... | L2 table | + +----------+ +----------+ + ,------' | '------. + +----------+ | +----------+ + | Data | ... | Data | + +----------+ +----------+ + +A table is made up of one or more contiguous clusters. The table_size header field determines table size for an image file. For example, cluster_size=64 KB and table_size=4 results in 256 KB tables. + +The logical image size must be less than or equal to the maximum possible size of clusters rooted by the L1 table: + header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size + +L1, L2, and data cluster offsets must be aligned to header.cluster_size. The following offsets have special meanings: + +===L2 table offsets=== +* 0 - unallocated. The L2 table is not yet allocated. + +===Data cluster offsets=== +* 0 - unallocated. The data cluster is not yet allocated. +* 1 - zero. The data cluster contents are all zeroes and no cluster is allocated. + +Future format extensions may wish to store per-offset information. The least significant 12 bits of an offset are reserved for this purpose and must be set to zero. Image files with cluster_size > 2^12 will have more unused bits which should also be zeroed. + +===Unallocated L2 tables and data clusters=== +Reads to an unallocated area of the image file access the backing file. If there is no backing file, then zeroes are produced. The backing file may be smaller than the image file and reads of unallocated areas beyond the end of the backing file produce zeroes. + +Writes to an unallocated area cause a new data clusters to be allocated, and a new L2 table if that is also unallocated. The new data cluster is populated with data from the backing file (or zeroes if no backing file) and the data being written. + +===Zero data clusters=== +Zero data clusters are a space-efficient way of storing zeroed regions of the image. + +Reads to a zero data cluster produce zeroes. Note that the difference between an unallocated and a zero data cluster is that zero data clusters stop the reading of contents from the backing file. + +Writes to a zero data cluster cause a new data cluster to be allocated. The new data cluster is populated with zeroes and the data being written. + +===Logical offset translation=== +Logical offsets are translated into cluster offsets as follows: + + table_bits table_bits cluster_bits + <--------> <--------> <---------------> + +----------+----------+-----------------+ + | L1 index | L2 index | byte offset | + +----------+----------+-----------------+ + + Structure of a logical offset + + offset_mask = ~(cluster_size - 1) # mask for the image file byte offset + + def logical_to_cluster_offset(l1_index, l2_index, byte_offset): + l2_offset = l1_table[l1_index] + l2_table = load_table(l2_offset) + cluster_offset = l2_table[l2_index] & offset_mask + return cluster_offset + byte_offset + +==Consistency checking== + +This section is informational and included to provide background on the use of the QED_F_NEED_CHECK ''features'' bit. + +The QED_F_NEED_CHECK bit is used to mark an image as dirty before starting an operation that could leave the image in an inconsistent state if interrupted by a crash or power failure. A dirty image must be checked on open because its metadata may not be consistent. + +Consistency check includes the following invariants: +# Each cluster is referenced once and only once. It is an inconsistency to have a cluster referenced more than once by L1 or L2 tables. A cluster has been leaked if it has no references. +# Offsets must be within the image file size and must be ''cluster_size'' aligned. +# Table offsets must at least ''table_size'' * ''cluster_size'' bytes from the end of the image file so that there is space for the entire table. + +The consistency check process starts by from ''l1_table_offset'' and scans all L2 tables. After the check completes with no other errors besides leaks, the QED_F_NEED_CHECK bit can be cleared and the image can be accessed. diff --git a/docs/specs/rocker.txt b/docs/specs/rocker.txt new file mode 100644 index 0000000..1e7e1e1 --- /dev/null +++ b/docs/specs/rocker.txt @@ -0,0 +1,1009 @@ +Rocker Network Switch Register Programming Guide +Copyright (c) Scott Feldman +Copyright (c) Neil Horman +Version 0.11, 12/29/2014 + +LICENSE +======= + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +SECTION 1: Introduction +======================= + +Overview +-------- + +This document describes the hardware/software interface for the Rocker switch +device. The intended audience is authors of OS drivers and device emulation +software. + +Notations and Conventions +------------------------- + +o In register descriptions, [n:m] indicates a range from bit n to bit m, +inclusive. +o Use of leading 0x indicates a hexadecimal number. +o Use of leading 0b indicates a binary number. +o The use of RSVD or Reserved indicates that a bit or field is reserved for +future use. +o Field width is in bytes, unless otherwise noted. +o Register are (R) read-only, (R/W) read/write, (W) write-only, or (COR) clear +on read +o TLV values in network-byte-order are designated with (N). + + +SECTION 2: PCI Configuration Registers +====================================== + +PCI Configuration Space +----------------------- + +Each switch instance registers as a PCI device with PCI configuration space: + + offset width description value + --------------------------------------------- + 0x0 2 Vendor ID 0x1b36 + 0x2 2 Device ID 0x0006 + 0x4 4 Command/Status + 0x8 1 Revision ID 0x01 + 0x9 3 Class code 0x2800 + 0xC 1 Cache line size + 0xD 1 Latency timer + 0xE 1 Header type + 0xF 1 Built-in self test + 0x10 4 Base address low + 0x14 4 Base address high + 0x18-28 Reserved + 0x2C 2 Subsystem vendor ID * + 0x2E 2 Subsystem ID * + 0x30-38 Reserved + 0x3C 1 Interrupt line + 0x3D 1 Interrupt pin 0x00 + 0x3E 1 Min grant 0x00 + 0x3D 1 Max latency 0x00 + 0x40 1 TRDY timeout + 0x41 1 Retry count + 0x42 2 Reserved + + +* Assigned by sub-system implementation + +SECTION 3: Memory-Mapped Register Space +======================================= + +There are two memory-mapped BARs. BAR0 maps device register space and is +0x2000 in size. BAR1 maps MSI-X vector and PBA tables and is also 0x2000 in +size, allowing for 256 MSI-X vectors. + +All registers are 4 or 8 bytes long. It is assumed host software will access 4 +byte registers with one 4-byte access, and 8 byte registers with either two +4-byte accesses or a single 8-byte access. In the case of two 4-byte accesses, +access must be lower and then upper 4-bytes, in that order. + +BAR0 device register space is organized as follows: + + offset description + ------------------------------------------------------ + 0x0000-0x000f Bogus registers to catch misbehaving + drivers. Writes do nothing. Reads + back as 0xDEADBABE. + 0x0010-0x00ff Test registers + 0x0300-0x03ff General purpose registers + 0x1000-0x1fff Descriptor control + +Holes in register space are reserved. Writes to reserved registers do nothing. +Reads to reserved registers read back as 0. + +No fancy stuff like write-combining is enabled on any of the registers. + +BAR1 MSI-X register space is organized as follows: + + offset description + ------------------------------------------------------ + 0x0000-0x0fff MSI-X vector table (256 vectors total) + 0x1000-0x1fff MSI-X PBA table + + +SECTION 4: Interrupts, DMA, and Endianness +========================================== + +PCI Interrupts +-------------- + +The device supports only MSI-X interrupts. BAR1 memory-mapped region contains +the MSI-X vector and PBA tables, with support for up to 256 MSI-X vectors. + +The vector assignment is: + + vector description + ----------------------------------------------------- + 0 Command descriptor ring completion + 1 Event descriptor ring completion + 2 Test operation completion + 3 RSVD + 4-255 Tx and Rx descriptor ring completion + Tx vector is even + Rx vector is odd + +A MSI-X vector table entry is 16 bytes: + + field offset width description + ------------------------------------------------------------- + lower_addr 0x0 4 [31:2] message address[31:2] + [1:0] Rsvd (4 byte alignment + required) + upper_addr 0x4 4 [31:19] Rsvd + [14:0] message address[46:32] + data 0x8 4 message data[31:0] + control 0xc 4 [31:1] Rsvd + [0] mask (0 = enable, + 1 = masked) + +Software should install the Interrupt Service Routine (ISR) before any ports +are enabled or any commands are issued on the command ring. + +DMA Operations +-------------- + +DMA operations are used for packet DMA to/from the CPU, command and event +processing. Command processing includes statistical counters and table dumps, +table insertion/deletion, and more. Event processing provides an async +notification method for device-originating events. Each DMA operation has a +set of control registers to manage a descriptor ring. The descriptor rings are +allocated from contiguous host DMA-able memory and registers specify the rings +base address, size and current head and tail indices. Software always writes +the head, and hardware always writes the tail. + +The higher-order bit of DMA_DESC_COMP_ERR is used to mark hardware completion +of a descriptor. Software will clear this bit when posting a descriptor to the +ring, and hardware will set this bit when the descriptor is complete. + +Descriptor ring sizes must be a power of 2 and range from 2 to 64K entries. +Descriptor rings' base address must be 8-byte aligned. Descriptors must be +packed within ring. Each descriptor in each ring must also be aligned on an 8 +byte boundary. Each descriptor ring will have these registers: + + DMA_DESC_xxx_BASE_ADDR, offset 0x1000 + (x * 32), 64-bit, (R/W) + DMA_DESC_xxx_SIZE, offset 0x1008 + (x * 32), 32-bit, (R/W) + DMA_DESC_xxx_HEAD, offset 0x100c + (x * 32), 32-bit, (R/W) + DMA_DESC_xxx_TAIL, offset 0x1010 + (x * 32), 32-bit, (R) + DMA_DESC_xxx_CTRL, offset 0x1014 + (x * 32), 32-bit, (W) + DMA_DESC_xxx_CREDITS, offset 0x1018 + (x * 32), 32-bit, (R/W) + DMA_DESC_xxx_RSVD1, offset 0x101c + (x * 32), 32-bit, (R/W) + +Where x is descriptor ring index: + + index ring + -------------------- + 0 CMD + 1 EVENT + 2 TX (port 0) + 3 RX (port 0) + 4 TX (port 1) + 5 RX (port 1) + . + . + . + 124 TX (port 61) + 125 RX (port 61) + 126 Resv + 127 Resv + +Writing BASE_ADDR or SIZE will reset HEAD and TAIL to zero. HEAD cannot be +written past TAIL. To do so would wrap the ring. An empty ring is when HEAD +== TAIL. A full ring is when HEAD is one position behind TAIL. Both HEAD and +TAIL increment and modulo wrap at the ring size. + +CTRL register bits: + + bit name description + ------------------------------------------------------------------------ + [0] CTRL_RESET Reset the descriptor ring + [1:31] Reserved + +All descriptor types share some common fields: + + field width description + ------------------------------------------------------------------- + DMA_DESC_BUF_ADDR 8 Phys addr of desc payload, 8-byte + aligned + DMA_DESC_COOKIE 8 Desc cookie for completion matching, + upper-most bit is reserved + DMA_DESC_BUF_SIZE 2 Desc payload size in bytes + DMA_DESC_TLV_SIZE 2 Desc payload total size in bytes + used for TLVs. Must be <= + DMA_DESC_BUF_SIZE. + DMA_DESC_COMP_ERR 2 Completion status of associated + desc payload. High order bit is + clear on new descs, toggled by + hw for completed items. + +To support forward- and backward-compatibility, descriptor and completion +payloads are specified in TLV format. Fields are packed with Type=field name, +Length=field length, and Value=field value. Software will ignore unknown fields +filled in by the switch. Likewise, the switch will ignore unknown fields +filled in by software. + +Descriptor payload buffer is 8-byte aligned and TLVs are 8-byte aligned. The +value within a TLV is also 8-byte aligned. The (packed, 8 byte) TLV header is: + + field width description + ----------------------------- + type 4 TLV type + len 2 TLV value length + pad 2 Reserved + +The alignment requirements for descriptors and TLVs are to avoid unaligned +access exceptions in software. Note that the payload for each TLV is also +8 byte aligned. + +Figure 1 shows an example descriptor buffer with two TLVs. + + <------- 8 bytes -------> + + 8-byte +––––+ +–––––––––––+–––––+–––––+ +–+ + align | type | len | pad | TLV#1 hdr | + +–––––––––––+–––––+–––––+ (len=22) | + | | | + | value | TVL#1 value | + | | (padded to 8-byte | + | +–––––+ alignment) | + | |/////| | + 8-byte +––––+ +–––––––––––+–––––––––––+ | + align | type | len | pad | TLV#2 hdr DESC_BUF_SIZE + +–––––+–––––+–––––+–––––+ (len=2) | + |value|/////////////////| TLV#2 value | + +–––––+/////////////////| | + |///////////////////////| | + |///////////////////////| | + |///////////////////////| | + |////////unused/////////| | + |////////space//////////| | + |///////////////////////| | + |///////////////////////| | + |///////////////////////| | + +–––––––––––––––––––––––+ +–+ + + fig. 1 + +TLVs can be nested within the NEST TLV type. + +Interrupt credits +^^^^^^^^^^^^^^^^^ + +MSI-X vectors used for descriptor ring completions use a credit mechanism for +efficient device, PCIe bus, OS and driver operations. Each descriptor ring has +a credit count which represents the number of outstanding descriptors to be +processed by the driver. As the device marks descriptors complete, the credit +count is incremented. As the driver processes those outstanding descriptors, +it returns credits back to the device. This way, the device knows the driver's +progress and can make decisions about when to fire the next interrupt or not. +When the credit count is zero, and the first descriptors are posted for the +driver, a single interrupt is fired. Once the interrupt is fired, the +interrupt is disabled (auto-masked*). In response to the interrupt, the driver +will process descriptors and PIO write a returned credit value for that +descriptor ring. If the driver returns all credits (the driver caught up with +the device and there is no outstanding work), then the interrupt is unmasked, +but not fired. If only partial credits are returned, the interrupt remains +masked but the device generates an interrupt, signaling the driver that more +outstanding work is available. + +(* this masking is unrelated to to the MSI-X interrupt mask register) + +Endianness +---------- + +Device registers are hard-coded to little-endian (LE). The driver should +convert to/from host endianess to LE for device register accesses. + +Descriptors are LE. Descriptor buffer TLVs will have LE type and length +fields, but the value field can either be LE or network-byte-order, depending +on context. TLV values containing network packet data will be in network-byte +order. A TLV value containing a field or mask used to compare against network +packet data is network-byte order. For example, flow match fields (and masks) +are network-byte-order since they're matched directly, byte-by-byte, against +network packet data. All non-network-packet TLV multi-byte values will be LE. + +TLV values in network-byte-order are designated with (N). + + +SECTION 5: Test Registers +========================= + +Rocker has several test registers to support troubleshooting register access, +interrupt generation, and DMA operations: + + TEST_REG, offset 0x0010, 32-bit (R/W) + TEST_REG64, offset 0x0018, 64-bit (R/W) + TEST_IRQ, offset 0x0020, 32-bit (R/W) + TEST_DMA_ADDR, offset 0x0028, 64-bit (R/W) + TEST_DMA_SIZE, offset 0x0030, 32-bit (R/W) + TEST_DMA_CTRL, offset 0x0034, 32-bit (R/W) + +Reads to TEST_REG and TEST_REG64 will read a value equal to twice the last +value written to the register. The 32-bit and 64-bit versions are for testing +32-bit and 64-bit host accesses. + +A vector can be written to TEST_IRQ and the device will generate an interrupt +for that vector. + +To test basic DMA operations, allocate a DMA-able host buffer and put the +buffer address into TEST_DMA_ADDR and size into TEST_DMA_SIZE. Then, write to +TEST_DMA_CTRL to manipulate the buffer contents. TEST_DMA_CTRL operations are: + + operation value description + ----------------------------------------------------------- + TEST_DMA_CTRL_CLEAR 1 clear buffer + TEST_DMA_CTRL_FILL 2 fill buffer bytes with 0x96 + TEST_DMA_CTRL_INVERT 4 invert bytes in buffer + +Various buffer address and sizes should be tested to verify no address boundary +issue exists. In particular, buffers that start on odd-8-byte boundary and/or +span multiple PAGE sizes should be tested. + + +SECTION 6: Ports +================ + +Physical and Logical Ports +------------------------------------ + +The switch supports up to 62 physical (front-panel) ports. Register +PORT_PHYS_COUNT returns the actual number of physical ports available: + + PORT_PHYS_COUNT, offset 0x0304, 32-bit, (R) + +In addition to front-panel ports, the switch supports logical ports for +tunnels. + +Front-panel ports and logical tunnel ports are mapped into a single 32-bit port +space. A special CPU port is assigned port 0. The front-panel ports are +mapped to ports 1-62. A special loopback port is assigned port 63. Logical +tunnel ports are assigned ports 0x0001000-0x0001ffff. +To summarize the port assignments: + + port mapping + ------------------------------------------------------- + 0 CPU port (for packets to/from host CPU) + 1-62 front-panel physical ports + 63 loopback port + 64-0x0000ffff RSVD + 0x00010000-0x0001ffff logical tunnel ports + 0x00020000-0xffffffff RSVD + +Physical Port Mode +------------------ + +Switch front-panel ports operate in a mode. Currently, the only mode is +OF-DPA. OF-DPA[1] mode is based on OpenFlow Data Plane Abstraction (OF-DPA) +Abstract Switch Specification, Version 1.0, from Broadcom Corporation. To +set/get the mode for front-panel ports, see port settings, below. + +Port Settings +------------- + +Link status for all front-panel ports is available via PORT_PHYS_LINK_STATUS: + + PORT_PHYS_LINK_STATUS, offset 0x0310, 64-bit, (R) + + Value is port bitmap. Bits 0 and 63 always read 0. Bits 1-62 + read 1 for link UP and 0 for link DOWN for respective front-panel ports. + +Other properties for front-panel ports are available via DMA CMD descriptors: + + Get PORT_SETTINGS descriptor: + + field width description + ---------------------------------------------- + PORT_SETTINGS 2 CMD_GET + PPORT 4 Physical port # + + Get PORT_SETTINGS completion: + + field width description + ---------------------------------------------- + PPORT 4 Physical port # + SPEED 4 Current port interface speed, in Mbps + DUPLEX 1 1 = Full, 0 = Half + AUTONEG 1 1 = enabled, 0 = disabled + MACADDR 6 Port MAC address + MODE 1 0 = OF-DPA + LEARNING 1 MAC address learning on port + 1 = enabled + 0 = disabled + + Set PORT_SETTINGS descriptor: + + field width description + ---------------------------------------------- + PORT_SETTINGS 2 CMD_SET + PPORT 4 Physical port # + SPEED 4 Port interface speed, in Mbps + DUPLEX 1 1 = Full, 0 = Half + AUTONEG 1 1 = enabled, 0 = disabled + MACADDR 6 Port MAC address + MODE 1 0 = OF-DPA + +Port Enable +----------- + +Front-panel ports are initially disabled, which means port ingress and egress +packets will be dropped. To enable or disable a port, use PORT_PHYS_ENABLE: + + PORT_PHYS_ENABLE: offset 0x0318, 64-bit, (R/W) + + Value is bitmap of first 64 ports. Bits 0 and 63 are ignored + and always read as 0. Write 1 to enable port; write 0 to disable it. + Default is 0. + + +SECTION 7: Switch Control +========================= + +This section covers switch-wide register settings. + +Control +------- + +This register is used for low level control of the switch. + + CONTROL: offset 0x0300, 32-bit, (W) + + bit name description + ------------------------------------------------------------------------ + [0] CONTROL_RESET If set, device will perform reset + [1:31] Reserved + +Switch ID +--------- + +The switch has a SWITCH_ID to be used by software to uniquely identify the +switch: + + SWITCH_ID: offset 0x0320, 64-bit, (R) + + Value is opaque to switch software and no special encoding is implied. + + +SECTION 8: Events +================= + +Non-I/O asynchronous events from the device are notified to the host using the +event ring. The TLV structure for events is: + + field width description + --------------------------------------------------- + TYPE 4 Event type, one of: + 1: LINK_CHANGED + 2: MAC_VLAN_SEEN + INFO Event info (details below) + +Link Changed Event +------------------ + +When link status changes on a physical port, this event is generated. + + field width description + --------------------------------------------------- + INFO + PPORT 4 Physical port + LINKUP 1 Link status: + 0: down + 1: up + +MAC VLAN Seen Event +------------------- + +When a packet ingresses on a port and the source MAC/VLAN isn't known to the +device, the device will generate this event. In response to the event, the +driver should install to the device the MAC/VLAN on the port into the bridge +table. Once installed, the MAC/VLAN is known on the port and this event will +no longer be generated. + + field width description + --------------------------------------------------- + INFO + PPORT 4 Physical port + MAC 6 MAC address + VLAN 2 VLAN ID + + +SECTION 9: CPU Packet Processing +================================ + +Ingress packets directed to the host CPU for further processing are delivered +in the DMA RX ring. Likewise, host CPU originating packets destined to egress +on switch ports are scheduled by software using the DMA TX ring. + +Tx Packet Processing +-------------------- + +Software schedules packets for egress on switch ports using the DMA TX ring. A +TX descriptor buffer describes the packet location and size in host DMA-able +memory, the destination port, and any hardware-offload functions (such as L3 +payload checksum offload). Software then bumps the descriptor head to signal +hardware of new Tx work. In response, hardware will DMA read Tx descriptors up +to head, DMA read descriptor buffer and packet data, perform offloading +functions, and finally frame packet on wire (network). Once packet processing +is complete, hardware will writeback status to descriptor(s) to signal to +software that Tx is complete and software resources (e.g. skb) backing packet +can be released. + +Figure 2 shows an example 3-fragment packet queued with one Tx descriptor. A +TLV is used for each packet fragment. + + pkt frag 1 + +–––––––+ +–+ + +–––+ | | + desc buf | | | | + +––––––––+ | | | | + Tx ring +–––+ +–––––+ | | | + +–––––––––+ | | TLVs | +–––––––+ | + | +–––+ +––––––––+ pkt frag 2 | + | desc 0 | | +–––––+ +–––––––+ | + +–––––––––+ | TLVs | +–––+ | | + head+–+ | +––––––––+ | | | + | desc 1 | | +–––––+ +–––––––+ |pkt + +–––––––––+ | TLVs | | | + | | +––––––––+ | pkt frag 3 | + | | | +–––––––+ | + +–––––––––+ +–––+ | | + | | | | | + | | | | | + +–––––––––+ | | | + | | | | | + | | | | | + +–––––––––+ | | | + | | +–––––––+ +–+ + | | + +–––––––––+ + + fig 2. + +The TLVs for Tx descriptor buffer are: + + field width description + --------------------------------------------------------------------- + PPORT 4 Destination physical port # + TX_OFFLOAD 1 Hardware offload modes: + 0: no offload + 1: insert IP csum (ipv4 only) + 2: insert TCP/UDP csum + 3: L3 csum calc and insert + into csum offset (TX_L3_CSUM_OFF) + 16-bit 1's complement csum value. + IPv4 pseudo-header and IP + already calculated by OS + and inserted. + 4: TSO (TCP Segmentation Offload) + TX_L3_CSUM_OFF 2 For L3 csum offload mode, the offset, + from the beginning of the packet, + of the csum field in the L3 header + TX_TSO_MSS 2 For TSO offload mode, the + Maximum Segment Size in bytes + TX_TSO_HDR_LEN 2 For TSO offload mode, the + length of ethernet, IP, and + TCP/UDP headers, including IP + and TCP options. + TX_FRAGS Packet fragments + TX_FRAG Packet fragment + TX_FRAG_ADDR 8 DMA address of packet fragment + TX_FRAG_LEN 2 Packet fragment length + +Possible status return codes in descriptor on completion are: + + DESC_COMP_ERR reason + -------------------------------------------------------------------- + 0 OK + -ROCKER_ENXIO address or data read err on desc buf or packet + fragment + -ROCKER_EINVAL bad pport or TSO or csum offloading error + -ROCKER_ENOMEM no memory for internal staging tx fragment + +Rx Packet Processing +-------------------- + +For packets ingressing on switch ports that are not forwarded by the switch but +rather directed to the host CPU for further processing are delivered in the DMA +RX ring. Rx descriptor buffers are allocated by software and placed on the +ring. Hardware will fill Rx descriptor buffers with packet data, write the +completion, and signal to software that a new packet is ready. Since Rx packet +size is not known a-priori, the Rx descriptor buffer must be allocated for +worst-case packet size. A single Rx descriptor will contain the entire Rx +packet data in one RX_FRAG. Other Rx TLVs describe and hardware offloads +performed on the packet, such as checksum validation. + +The TLVs for Rx descriptor buffer are: + + field width description + --------------------------------------------------- + PPORT 4 Source physical port # + RX_FLAGS 2 Packet parsing flags: + (1 << 0): IPv4 packet + (1 << 1): IPv6 packet + (1 << 2): csum calculated + (1 << 3): IPv4 csum good + (1 << 4): IP fragment + (1 << 5): TCP packet + (1 << 6): UDP packet + (1 << 7): TCP/UDP csum good + RX_CSUM 2 IP calculated checksum: + IPv4: IP payload csum + IPv6: header and payload csum + (Only valid is RX_FLAGS:csum calc is set) + RX_FRAG_ADDR 8 DMA address of packet fragment + RX_FRAG_MAX_LEN 2 Packet maximum fragment length + RX_FRAG_LEN 2 Actual packet fragment length after receive + +Possible status return codes in descriptor on completion are: + + DESC_COMP_ERR reason + -------------------------------------------------------------------- + 0 OK + -ROCKER_ENXIO address or data read err on desc buf + -ROCKER_ENOMEM no memory for internal staging desc buf + -ROCKER_EMSGSIZE Rx descriptor buffer wasn't big enough to contain + packet data TLV and other TLVs. + + +SECTION 10: OF-DPA Mode +====================== + +OF-DPA mode allows the switch to offload flow packet processing functions to +hardware. An OpenFlow controller would communicate with an OpenFlow agent +installed on the switch. The OpenFlow agent would (directly or indirectly) +communicate with the Rocker switch driver, which in turn would program switch +hardware with flow functionality, as defined in OF-DPA. The block diagram is: + + +–––––––––––––––----–––+ + | OF | + | Remote Controller | + +––––––––+––----–––––––+ + | + | + +––––––––+–––––––––+ + | OF | + | Local Agent | + +––––––––––––––––––+ + | | + | Rocker Driver | + +––––––––––––––––––+ + + +––––––––––––––––––+ + | | + | Rocker Switch | + +––––––––––––––––––+ + +To participate in flow functions, ports must be configure for OF-DPA mode +during switch initialization. + +OF-DPA Flow Table Interface +--------------------------- + +There are commands to add, modify, delete, and get stats of flow table entries. +The commands are issued using the DMA CMD descriptor ring. The following +commands are defined: + + CMD_ADD: add an entry to flow table + CMD_MOD: modify an entry in flow table + CMD_DEL: delete an entry from flow table + CMD_GET_STATS: get stats for flow entry + +TLVs for add and modify commands are: + + field width description + ---------------------------------------------------- + OF_DPA_CMD 2 CMD_[ADD|MOD] + OF_DPA_TBL 2 Flow table ID + 0: ingress port + 10: vlan + 20: termination mac + 30: unicast routing + 40: multicast routing + 50: bridging + 60: ACL policy + OF_DPA_PRIORITY 4 Flow priority + OF_DPA_HARDTIME 4 Hard timeout for flow + OF_DPA_IDLETIME 4 Idle timeout for flow + OF_DPA_COOKIE 8 Cookie + +Additional TLVs based on flow table ID: + +Table ID 0: ingress port + + field width description + ---------------------------------------------------- + OF_DPA_IN_PPORT 4 ingress physical port number + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + +Table ID 10: vlan + + field width description + ---------------------------------------------------- + OF_DPA_IN_PPORT 4 ingress physical port number + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + OF_DPA_NEW_VLAN_ID 2 (N) new vlan ID + +Table ID 20: termination mac + + field width description + ---------------------------------------------------- + OF_DPA_IN_PPORT 4 ingress physical port number + OF_DPA_IN_PPORT_MASK 4 ingress physical port number mask + OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd + OF_DPA_DST_MAC 6 (N) destination MAC + OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask + OF_DPA_GOTO_TBL 2 only acceptable values are + unicast or multicast routing + table IDs + OF_DPA_OUT_PPORT 2 if specified, must be + controller, set zero otherwise + +Table ID 30: unicast routing + + field width description + ---------------------------------------------------- + OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd + OF_DPA_DST_IP 4 (N) destination IPv4 address. + Must be unicast address + OF_DPA_DST_IP_MASK 4 (N) IP mask. Must be prefix mask + OF_DPA_DST_IPV6 16 (N) destination IPv6 address. + Must be unicast address + OF_DPA_DST_IPV6_MASK 16 (N) IPv6 mask. Must be prefix mask + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + OF_DPA_GROUP_ID 4 data for GROUP action must + be an L3 Unicast group entry + +Table ID 40: multicast routing + + field width description + ---------------------------------------------------- + OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_SRC_IP 4 (N) source IPv4. Optional, + can contain IPv4 address, + must be completely masked + if not used + OF_DPA_SRC_IP_MASK 4 (N) IP Mask + OF_DPA_DST_IP 4 (N) destination IPv4 address. + Must be multicast address + OF_DPA_SRC_IPV6 16 (N) source IPv6 Address. Optional. + Can contain IPv6 address, + must be completely masked + if not used + OF_DPA_SRC_IPV6_MASK 16 (N) IPv6 mask. + OF_DPA_DST_IPV6 16 (N) destination IPv6 Address. Must + be multicast address + Must be multicast address + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + OF_DPA_GROUP_ID 4 data for GROUP action must + be an L3 multicast group entry + +Table ID 50: bridging + + field width description + ---------------------------------------------------- + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_TUNNEL_ID 4 tunnel ID + OF_DPA_DST_MAC 6 (N) destination MAC + OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + OF_DPA_GROUP_ID 4 data for GROUP action must + be a L2 Interface, L2 + Multicast, L2 Flood, + or L2 Overlay group entry + as appropriate + OF_DPA_TUNNEL_LPORT 4 unicast Tenant Bridging + flows specify a tunnel + logical port ID + OF_DPA_OUT_PPORT 2 data for OUTPUT action, + restricted to CONTROLLER, + set to 0 otherwise + +Table ID 60: acl policy + + field width description + ---------------------------------------------------- + OF_DPA_IN_PPORT 4 ingress physical port number + OF_DPA_IN_PPORT_MASK 4 ingress physical port number mask + OF_DPA_ETHERTYPE 2 (N) ethertype + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask + OF_DPA_VLAN_PCP 2 (N) vlan Priority Code Point + OF_DPA_VLAN_PCP_MASK 2 (N) vlan Priority Code Point mask + OF_DPA_SRC_MAC 6 (N) source MAC + OF_DPA_SRC_MAC_MASK 6 (N) source MAC mask + OF_DPA_DST_MAC 6 (N) destination MAC + OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask + OF_DPA_TUNNEL_ID 4 tunnel ID + OF_DPA_SRC_IP 4 (N) source IPv4. Optional, + can contain IPv4 address, + must be completely masked + if not used + OF_DPA_SRC_IP_MASK 4 (N) IP Mask + OF_DPA_DST_IP 4 (N) destination IPv4 address. + Must be multicast address + OF_DPA_DST_IP_MASK 4 (N) IP Mask + OF_DPA_SRC_IPV6 16 (N) source IPv6 Address. Optional. + Can contain IPv6 address, + must be completely masked + if not used + OF_DPA_SRC_IPV6_MASK 16 (N) IPv6 mask + OF_DPA_DST_IPV6 16 (N) destination IPv6 Address. Must + be multicast address. + OF_DPA_DST_IPV6_MASK 16 (N) IPv6 mask + OF_DPA_SRC_ARP_IP 4 (N) source IPv4 address in the ARP + payload. Only used if ethertype + == 0x0806. + OF_DPA_SRC_ARP_IP_MASK 4 (N) IP Mask + OF_DPA_IP_PROTO 1 IP protocol + OF_DPA_IP_PROTO_MASK 1 IP protocol mask + OF_DPA_IP_DSCP 1 DSCP + OF_DPA_IP_DSCP_MASK 1 DSCP mask + OF_DPA_IP_ECN 1 ECN + OF_DPA_IP_ECN_MASK 1 ECN mask + OF_DPA_L4_SRC_PORT 2 (N) L4 source port, only for + TCP, UDP, or SCTP + OF_DPA_L4_SRC_PORT_MASK 2 (N) L4 source port mask + OF_DPA_L4_DST_PORT 2 (N) L4 source port, only for + TCP, UDP, or SCTP + OF_DPA_L4_DST_PORT_MASK 2 (N) L4 source port mask + OF_DPA_ICMP_TYPE 1 ICMP type, only if IP + protocol is 1 + OF_DPA_ICMP_TYPE_MASK 1 ICMP type mask + OF_DPA_ICMP_CODE 1 ICMP code + OF_DPA_ICMP_CODE_MASK 1 ICMP code mask + OF_DPA_IPV6_LABEL 4 (N) IPv6 flow label + OF_DPA_IPV6_LABEL_MASK 4 (N) IPv6 flow label mask + OF_DPA_GROUP_ID 4 data for GROUP action + OF_DPA_QUEUE_ID_ACTION 1 write the queue ID + OF_DPA_NEW_QUEUE_ID 1 queue ID + OF_DPA_VLAN_PCP_ACTION 1 write the VLAN priority + OF_DPA_NEW_VLAN_PCP 1 VLAN priority + OF_DPA_IP_DSCP_ACTION 1 write the DSCP + OF_DPA_NEW_IP_DSCP 1 new DSCP + OF_DPA_TUNNEL_LPORT 4 restrct to valid tunnel + logical port, set to 0 + otherwise. + OF_DPA_OUT_PPORT 2 data for OUTPUT action, + restricted to CONTROLLER, + set to 0 otherwise + OF_DPA_CLEAR_ACTIONS 4 if 1 packets matching flow are + dropped (all other instructions + ignored) + +TLVs for flow delete and get stats command are: + + field width description + --------------------------------------------------- + OF_DPA_CMD 2 CMD_[DEL|GET_STATS] + OF_DPA_COOKIE 8 Cookie + +On completion of get stats command, the descriptor buffer is written back with +the following TLVs: + + field width description + --------------------------------------------------- + OF_DPA_STAT_DURATION 4 Flow duration + OF_DPA_STAT_RX_PKTS 8 Received packets + OF_DPA_STAT_TX_PKTS 8 Transmit packets + +Possible status return codes in descriptor on completion are: + + DESC_COMP_ERR command reason + -------------------------------------------------------------------- + 0 all OK + -ROCKER_EFAULT all head or tail index outside + of ring + -ROCKER_ENXIO all address or data read err on + desc buf + -ROCKER_EMSGSIZE GET_STATS cmd descriptor buffer wasn't + big enough to contain write-back + TLVs + -ROCKER_EINVAL all invalid parameters passed in + -ROCKER_EEXIST ADD entry already exists + -ROCKER_ENOSPC ADD no space left in flow table + -ROCKER_ENOENT MOD|DEL|GET_STATS cookie invalid + +Group Table Interface +--------------------- + +There are commands to add, modify, delete, and get stats of group table +entries. The commands are issued using the DMA CMD descriptor ring. The +following commands are defined: + + CMD_ADD: add an entry to group table + CMD_MOD: modify an entry in group table + CMD_DEL: delete an entry from group table + CMD_GET_STATS: get stats for group entry + +TLVs for add and modify commands are: + + field width description + ----------------------------------------------------------- + FLOW_GROUP_CMD 2 CMD_[ADD|MOD] + FLOW_GROUP_ID 2 Flow group ID + FLOW_GROUP_TYPE 1 Group type: + 0: L2 interface + 1: L2 rewrite + 2: L3 unicast + 3: L2 multicast + 4: L2 flood + 5: L3 interface + 6: L3 multicast + 7: L3 ECMP + 8: L2 overlay + FLOW_VLAN_ID 2 Vlan ID (types 0, 3, 4, 6) + FLOW_L2_PORT 2 Port (types 0) + FLOW_INDEX 4 Index (all types but 0) + FLOW_OVERLAY_TYPE 1 Overlay sub-type (type 8): + 0: Flood unicast tunnel + 1: Flood multicast tunnel + 2: Multicast unicast tunnel + 3: Multicast multicast tunnel + FLOW_GROUP_ACTION nest + FLOW_GROUP_ID 2 next group ID in chain (all + types except 0) + FLOW_OUT_PORT 4 egress port (types 0, 8) + FLOW_POP_VLAN_TAG 1 strip outer VLAN tag (type 1 + only) + FLOW_VLAN_ID 2 (types 1, 5) + FLOW_SRC_MAC 6 (types 1, 2, 5) + FLOW_DST_MAC 6 (types 1, 2) + +TLVs for flow delete and get stats command are: + + field width description + ----------------------------------------------------------- + FLOW_GROUP_CMD 2 CMD_[DEL|GET_STATS] + FLOW_GROUP_ID 2 Flow group ID + +On completion of get stats command, the descriptor buffer is written back with +the following TLVs: + + field width description + --------------------------------------------------- + FLOW_GROUP_ID 2 Flow group ID + FLOW_STAT_DURATION 4 Flow duration + FLOW_STAT_REF_COUNT 4 Flow reference count + FLOW_STAT_BUCKET_COUNT 4 Flow bucket count + +Possible status return codes in descriptor on completion are: + + DESC_COMP_ERR command reason + -------------------------------------------------------------------- + 0 all OK + -ROCKER_EFAULT all head or tail index outside + of ring + -ROCKER_ENXIO all address or data read err on + desc buf + -ROCKER_ENOSPC GET_STATS cmd descriptor buffer wasn't + big enough to contain write-back + TLVs + -ROCKER_EINVAL ADD|MOD invalid parameters passed in + -ROCKER_EEXIST ADD entry already exists + -ROCKER_ENOSPC ADD no space left in flow table + -ROCKER_ENOENT MOD|DEL|GET_STATS group ID invalid + -ROCKER_EBUSY DEL group reference count non-zero + -ROCKER_ENODEV ADD next group ID doesn't exist + + + +References +========== + +[1] OpenFlow Data Plane Abstraction (OF-DPA) Abstract Switch Specification, +Version 1.0, from Broadcom Corporation, February 21, 2014. diff --git a/docs/specs/standard-vga.txt b/docs/specs/standard-vga.txt new file mode 100644 index 0000000..19d2a74 --- /dev/null +++ b/docs/specs/standard-vga.txt @@ -0,0 +1,81 @@ + +QEMU Standard VGA +================= + +Exists in two variants, for isa and pci. + +command line switches: + -vga std [ picks isa for -M isapc, otherwise pci ] + -device VGA [ pci variant ] + -device isa-vga [ isa variant ] + -device secondary-vga [ legacy-free pci variant ] + + +PCI spec +-------- + +Applies to the pci variant only for obvious reasons. + +PCI ID: 1234:1111 + +PCI Region 0: + Framebuffer memory, 16 MB in size (by default). + Size is tunable via vga_mem_mb property. + +PCI Region 1: + Reserved (so we have the option to make the framebuffer bar 64bit). + +PCI Region 2: + MMIO bar, 4096 bytes in size (qemu 1.3+) + +PCI ROM Region: + Holds the vgabios (qemu 0.14+). + + +The legacy-free variant has no ROM and has PCI_CLASS_DISPLAY_OTHER +instead of PCI_CLASS_DISPLAY_VGA. + + +IO ports used +------------- + +Doesn't apply to the legacy-free pci variant, use the MMIO bar instead. + +03c0 - 03df : standard vga ports +01ce : bochs vbe interface index port +01cf : bochs vbe interface data port (x86 only) +01d0 : bochs vbe interface data port + + +Memory regions used +------------------- + +0xe0000000 : Framebuffer memory, isa variant only. + +The pci variant used to mirror the framebuffer bar here, qemu 0.14+ +stops doing that (except when in -M pc-$old compat mode). + + +MMIO area spec +-------------- + +Likewise applies to the pci variant only for obvious reasons. + +0000 - 03ff : reserved, for possible virtio extension. +0400 - 041f : vga ioports (0x3c0 -> 0x3df), remapped 1:1. + word access is supported, bytes are written + in little endia order (aka index port first), + so indexed registers can be updated with a + single mmio write (and thus only one vmexit). +0500 - 0515 : bochs dispi interface registers, mapped flat + without index/data ports. Use (index << 1) + as offset for (16bit) register access. + +0600 - 0607 : qemu extended registers. qemu 2.2+ only. + The pci revision is 2 (or greater) when + these registers are present. The registers + are 32bit. + 0600 : qemu extended register region size, in bytes. + 0604 : framebuffer endianness register. + - 0xbebebebe indicates big endian. + - 0x1e1e1e1e indicates little endian. diff --git a/docs/specs/vhost-user.txt b/docs/specs/vhost-user.txt new file mode 100644 index 0000000..650bb18 --- /dev/null +++ b/docs/specs/vhost-user.txt @@ -0,0 +1,266 @@ +Vhost-user Protocol +=================== + +Copyright (c) 2014 Virtual Open Systems Sarl. + +This work is licensed under the terms of the GNU GPL, version 2 or later. +See the COPYING file in the top-level directory. +=================== + +This protocol is aiming to complement the ioctl interface used to control the +vhost implementation in the Linux kernel. It implements the control plane needed +to establish virtqueue sharing with a user space process on the same host. It +uses communication over a Unix domain socket to share file descriptors in the +ancillary data of the message. + +The protocol defines 2 sides of the communication, master and slave. Master is +the application that shares its virtqueues, in our case QEMU. Slave is the +consumer of the virtqueues. + +In the current implementation QEMU is the Master, and the Slave is intended to +be a software Ethernet switch running in user space, such as Snabbswitch. + +Master and slave can be either a client (i.e. connecting) or server (listening) +in the socket communication. + +Message Specification +--------------------- + +Note that all numbers are in the machine native byte order. A vhost-user message +consists of 3 header fields and a payload: + +------------------------------------ +| request | flags | size | payload | +------------------------------------ + + * Request: 32-bit type of the request + * Flags: 32-bit bit field: + - Lower 2 bits are the version (currently 0x01) + - Bit 2 is the reply flag - needs to be sent on each reply from the slave + * Size - 32-bit size of the payload + + +Depending on the request type, payload can be: + + * A single 64-bit integer + ------- + | u64 | + ------- + + u64: a 64-bit unsigned integer + + * A vring state description + --------------- + | index | num | + --------------- + + Index: a 32-bit index + Num: a 32-bit number + + * A vring address description + -------------------------------------------------------------- + | index | flags | size | descriptor | used | available | log | + -------------------------------------------------------------- + + Index: a 32-bit vring index + Flags: a 32-bit vring flags + Descriptor: a 64-bit user address of the vring descriptor table + Used: a 64-bit user address of the vring used ring + Available: a 64-bit user address of the vring available ring + Log: a 64-bit guest address for logging + + * Memory regions description + --------------------------------------------------- + | num regions | padding | region0 | ... | region7 | + --------------------------------------------------- + + Num regions: a 32-bit number of regions + Padding: 32-bit + + A region is: + ----------------------------------------------------- + | guest address | size | user address | mmap offset | + ----------------------------------------------------- + + Guest address: a 64-bit guest address of the region + Size: a 64-bit size + User address: a 64-bit user address + mmap offset: 64-bit offset where region starts in the mapped memory + +In QEMU the vhost-user message is implemented with the following struct: + +typedef struct VhostUserMsg { + VhostUserRequest request; + uint32_t flags; + uint32_t size; + union { + uint64_t u64; + struct vhost_vring_state state; + struct vhost_vring_addr addr; + VhostUserMemory memory; + }; +} QEMU_PACKED VhostUserMsg; + +Communication +------------- + +The protocol for vhost-user is based on the existing implementation of vhost +for the Linux Kernel. Most messages that can be sent via the Unix domain socket +implementing vhost-user have an equivalent ioctl to the kernel implementation. + +The communication consists of master sending message requests and slave sending +message replies. Most of the requests don't require replies. Here is a list of +the ones that do: + + * VHOST_GET_FEATURES + * VHOST_GET_VRING_BASE + +There are several messages that the master sends with file descriptors passed +in the ancillary data: + + * VHOST_SET_MEM_TABLE + * VHOST_SET_LOG_FD + * VHOST_SET_VRING_KICK + * VHOST_SET_VRING_CALL + * VHOST_SET_VRING_ERR + +If Master is unable to send the full message or receives a wrong reply it will +close the connection. An optional reconnection mechanism can be implemented. + +Message types +------------- + + * VHOST_USER_GET_FEATURES + + Id: 1 + Equivalent ioctl: VHOST_GET_FEATURES + Master payload: N/A + Slave payload: u64 + + Get from the underlying vhost implementation the features bitmask. + + * VHOST_USER_SET_FEATURES + + Id: 2 + Ioctl: VHOST_SET_FEATURES + Master payload: u64 + + Enable features in the underlying vhost implementation using a bitmask. + + * VHOST_USER_SET_OWNER + + Id: 3 + Equivalent ioctl: VHOST_SET_OWNER + Master payload: N/A + + Issued when a new connection is established. It sets the current Master + as an owner of the session. This can be used on the Slave as a + "session start" flag. + + * VHOST_USER_RESET_OWNER + + Id: 4 + Equivalent ioctl: VHOST_RESET_OWNER + Master payload: N/A + + Issued when a new connection is about to be closed. The Master will no + longer own this connection (and will usually close it). + + * VHOST_USER_SET_MEM_TABLE + + Id: 5 + Equivalent ioctl: VHOST_SET_MEM_TABLE + Master payload: memory regions description + + Sets the memory map regions on the slave so it can translate the vring + addresses. In the ancillary data there is an array of file descriptors + for each memory mapped region. The size and ordering of the fds matches + the number and ordering of memory regions. + + * VHOST_USER_SET_LOG_BASE + + Id: 6 + Equivalent ioctl: VHOST_SET_LOG_BASE + Master payload: u64 + + Sets the logging base address. + + * VHOST_USER_SET_LOG_FD + + Id: 7 + Equivalent ioctl: VHOST_SET_LOG_FD + Master payload: N/A + + Sets the logging file descriptor, which is passed as ancillary data. + + * VHOST_USER_SET_VRING_NUM + + Id: 8 + Equivalent ioctl: VHOST_SET_VRING_NUM + Master payload: vring state description + + Sets the number of vrings for this owner. + + * VHOST_USER_SET_VRING_ADDR + + Id: 9 + Equivalent ioctl: VHOST_SET_VRING_ADDR + Master payload: vring address description + Slave payload: N/A + + Sets the addresses of the different aspects of the vring. + + * VHOST_USER_SET_VRING_BASE + + Id: 10 + Equivalent ioctl: VHOST_SET_VRING_BASE + Master payload: vring state description + + Sets the base offset in the available vring. + + * VHOST_USER_GET_VRING_BASE + + Id: 11 + Equivalent ioctl: VHOST_USER_GET_VRING_BASE + Master payload: vring state description + Slave payload: vring state description + + Get the available vring base offset. + + * VHOST_USER_SET_VRING_KICK + + Id: 12 + Equivalent ioctl: VHOST_SET_VRING_KICK + Master payload: u64 + + Set the event file descriptor for adding buffers to the vring. It + is passed in the ancillary data. + Bits (0-7) of the payload contain the vring index. Bit 8 is the + invalid FD flag. This flag is set when there is no file descriptor + in the ancillary data. This signals that polling should be used + instead of waiting for a kick. + + * VHOST_USER_SET_VRING_CALL + + Id: 13 + Equivalent ioctl: VHOST_SET_VRING_CALL + Master payload: u64 + + Set the event file descriptor to signal when buffers are used. It + is passed in the ancillary data. + Bits (0-7) of the payload contain the vring index. Bit 8 is the + invalid FD flag. This flag is set when there is no file descriptor + in the ancillary data. This signals that polling will be used + instead of waiting for the call. + + * VHOST_USER_SET_VRING_ERR + + Id: 14 + Equivalent ioctl: VHOST_SET_VRING_ERR + Master payload: u64 + + Set the event file descriptor to signal when error occurs. It + is passed in the ancillary data. + Bits (0-7) of the payload contain the vring index. Bit 8 is the + invalid FD flag. This flag is set when there is no file descriptor + in the ancillary data. diff --git a/docs/specs/vmw_pvscsi-spec.txt b/docs/specs/vmw_pvscsi-spec.txt new file mode 100644 index 0000000..49affb2 --- /dev/null +++ b/docs/specs/vmw_pvscsi-spec.txt @@ -0,0 +1,92 @@ +General Description +=================== + +This document describes VMWare PVSCSI device interface specification. +Created by Dmitry Fleytman (dmitry@daynix.com), Daynix Computing LTD. +Based on source code of PVSCSI Linux driver from kernel 3.0.4 + +PVSCSI Device Interface Overview +================================ + +The interface is based on memory area shared between hypervisor and VM. +Memory area is obtained by driver as device IO memory resource of +PVSCSI_MEM_SPACE_SIZE length. +The shared memory consists of registers area and rings area. +The registers area is used to raise hypervisor interrupts and issue device +commands. The rings area is used to transfer data descriptors and SCSI +commands from VM to hypervisor and to transfer messages produced by +hypervisor to VM. Data itself is transferred via virtual scatter-gather DMA. + +PVSCSI Device Registers +======================= + +The length of the registers area is 1 page (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES). +The structure of the registers area is described by the PVSCSIRegOffset enum. +There are registers to issue device command (with optional short data), +issue device interrupt, control interrupts masking. + +PVSCSI Device Rings +=================== + +There are three rings in shared memory: + + 1. Request ring (struct PVSCSIRingReqDesc *req_ring) + - ring for OS to device requests + 2. Completion ring (struct PVSCSIRingCmpDesc *cmp_ring) + - ring for device request completions + 3. Message ring (struct PVSCSIRingMsgDesc *msg_ring) + - ring for messages from device. + This ring is optional and the guest might not configure it. +There is a control area (struct PVSCSIRingsState *rings_state) used to control +rings operation. + +PVSCSI Device to Host Interrupts +================================ +There are following interrupt types supported by PVSCSI device: + 1. Completion interrupts (completion ring notifications): + PVSCSI_INTR_CMPL_0 + PVSCSI_INTR_CMPL_1 + 2. Message interrupts (message ring notifications): + PVSCSI_INTR_MSG_0 + PVSCSI_INTR_MSG_1 + +Interrupts are controlled via PVSCSI_REG_OFFSET_INTR_MASK register +Bit set means interrupt enabled, bit cleared - disabled + +Interrupt modes supported are legacy, MSI and MSI-X +In case of legacy interrupts, register PVSCSI_REG_OFFSET_INTR_STATUS +is used to check which interrupt has arrived. Interrupts are +acknowledged when the corresponding bit is written to the interrupt +status register. + +PVSCSI Device Operation Sequences +================================= + +1. Startup sequence: + a. Issue PVSCSI_CMD_ADAPTER_RESET command; + aa. Windows driver reads interrupt status register here; + b. Issue PVSCSI_CMD_SETUP_MSG_RING command with no additional data, + check status and disable device messages if error returned; + (Omitted if device messages disabled by driver configuration) + c. Issue PVSCSI_CMD_SETUP_RINGS command, provide rings configuration + as struct PVSCSICmdDescSetupRings; + d. Issue PVSCSI_CMD_SETUP_MSG_RING command again, provide + rings configuration as struct PVSCSICmdDescSetupMsgRing; + e. Unmask completion and message (if device messages enabled) interrupts. + +2. Shutdown sequences + a. Mask interrupts; + b. Flush request ring using PVSCSI_REG_OFFSET_KICK_NON_RW_IO; + c. Issue PVSCSI_CMD_ADAPTER_RESET command. + +3. Send request + a. Fill next free request ring descriptor; + b. Issue PVSCSI_REG_OFFSET_KICK_RW_IO for R/W operations; + or PVSCSI_REG_OFFSET_KICK_NON_RW_IO for other operations. + +4. Abort command + a. Issue PVSCSI_CMD_ABORT_CMD command; + +5. Request completion processing + a. Upon completion interrupt arrival process completion + and message (if enabled) rings. diff --git a/docs/spice-port-fqdn.txt b/docs/spice-port-fqdn.txt new file mode 100644 index 0000000..5077895 --- /dev/null +++ b/docs/spice-port-fqdn.txt @@ -0,0 +1,19 @@ +A Spice port channel is an arbitrary communication between the Spice +server host side and the client side. + +Thanks to the associated reverse fully qualified domain name (fqdn), +a Spice client can handle the various ports appropriately. + +The following fqdn names are reserved by the QEMU project: + +org.qemu.monitor.hmp.0 + QEMU human monitor + +org.qemu.monitor.qmp.0: + QEMU control monitor + +org.qemu.console.serial.0 + QEMU virtual serial port + +org.qemu.console.debug.0 + QEMU debug console diff --git a/docs/tracing.txt b/docs/tracing.txt new file mode 100644 index 0000000..7117c5e --- /dev/null +++ b/docs/tracing.txt @@ -0,0 +1,349 @@ += Tracing = + +== Introduction == + +This document describes the tracing infrastructure in QEMU and how to use it +for debugging, profiling, and observing execution. + +== Quickstart == + +1. Build with the 'simple' trace backend: + + ./configure --enable-trace-backends=simple + make + +2. Create a file with the events you want to trace: + + echo bdrv_aio_readv > /tmp/events + echo bdrv_aio_writev >> /tmp/events + +3. Run the virtual machine to produce a trace file: + + qemu -trace events=/tmp/events ... # your normal QEMU invocation + +4. Pretty-print the binary trace file: + + ./scripts/simpletrace.py trace-events trace-* # Override * with QEMU + +== Trace events == + +There is a set of static trace events declared in the "trace-events" source +file. Each trace event declaration names the event, its arguments, and the +format string which can be used for pretty-printing: + + qemu_vmalloc(size_t size, void *ptr) "size %zu ptr %p" + qemu_vfree(void *ptr) "ptr %p" + +The "trace-events" file is processed by the "tracetool" script during build to +generate code for the trace events. Trace events are invoked directly from +source code like this: + + #include "trace.h" /* needed for trace event prototype */ + + void *qemu_vmalloc(size_t size) + { + void *ptr; + size_t align = QEMU_VMALLOC_ALIGN; + + if (size < align) { + align = getpagesize(); + } + ptr = qemu_memalign(align, size); + trace_qemu_vmalloc(size, ptr); + return ptr; + } + +=== Declaring trace events === + +The "tracetool" script produces the trace.h header file which is included by +every source file that uses trace events. Since many source files include +trace.h, it uses a minimum of types and other header files included to keep the +namespace clean and compile times and dependencies down. + +Trace events should use types as follows: + + * Use stdint.h types for fixed-size types. Most offsets and guest memory + addresses are best represented with uint32_t or uint64_t. Use fixed-size + types over primitive types whose size may change depending on the host + (32-bit versus 64-bit) so trace events don't truncate values or break + the build. + + * Use void * for pointers to structs or for arrays. The trace.h header + cannot include all user-defined struct declarations and it is therefore + necessary to use void * for pointers to structs. + + * For everything else, use primitive scalar types (char, int, long) with the + appropriate signedness. + +Format strings should reflect the types defined in the trace event. Take +special care to use PRId64 and PRIu64 for int64_t and uint64_t types, +respectively. This ensures portability between 32- and 64-bit platforms. + +=== Hints for adding new trace events === + +1. Trace state changes in the code. Interesting points in the code usually + involve a state change like starting, stopping, allocating, freeing. State + changes are good trace events because they can be used to understand the + execution of the system. + +2. Trace guest operations. Guest I/O accesses like reading device registers + are good trace events because they can be used to understand guest + interactions. + +3. Use correlator fields so the context of an individual line of trace output + can be understood. For example, trace the pointer returned by malloc and + used as an argument to free. This way mallocs and frees can be matched up. + Trace events with no context are not very useful. + +4. Name trace events after their function. If there are multiple trace events + in one function, append a unique distinguisher at the end of the name. + +== Generic interface and monitor commands == + +You can programmatically query and control the state of trace events through a +backend-agnostic interface provided by the header "trace/control.h". + +Note that some of the backends do not provide an implementation for some parts +of this interface, in which case QEMU will just print a warning (please refer to +header "trace/control.h" to see which routines are backend-dependent). + +The state of events can also be queried and modified through monitor commands: + +* info trace-events + View available trace events and their state. State 1 means enabled, state 0 + means disabled. + +* trace-event NAME on|off + Enable/disable a given trace event or a group of events (using wildcards). + +The "-trace events=" command line argument can be used to enable the +events listed in from the very beginning of the program. This file must +contain one event name per line. + +If a line in the "-trace events=" file begins with a '-', the trace event +will be disabled instead of enabled. This is useful when a wildcard was used +to enable an entire family of events but one noisy event needs to be disabled. + +Wildcard matching is supported in both the monitor command "trace-event" and the +events list file. That means you can enable/disable the events having a common +prefix in a batch. For example, virtio-blk trace events could be enabled using +the following monitor command: + + trace-event virtio_blk_* on + +== Trace backends == + +The "tracetool" script automates tedious trace event code generation and also +keeps the trace event declarations independent of the trace backend. The trace +events are not tightly coupled to a specific trace backend, such as LTTng or +SystemTap. Support for trace backends can be added by extending the "tracetool" +script. + +The trace backends are chosen at configure time: + + ./configure --enable-trace-backends=simple + +For a list of supported trace backends, try ./configure --help or see below. +If multiple backends are enabled, the trace is sent to them all. + +The following subsections describe the supported trace backends. + +=== Nop === + +The "nop" backend generates empty trace event functions so that the compiler +can optimize out trace events completely. This is the default and imposes no +performance penalty. + +Note that regardless of the selected trace backend, events with the "disable" +property will be generated with the "nop" backend. + +=== Stderr === + +The "stderr" backend sends trace events directly to standard error. This +effectively turns trace events into debug printfs. + +This is the simplest backend and can be used together with existing code that +uses DPRINTF(). + +=== Simpletrace === + +The "simple" backend supports common use cases and comes as part of the QEMU +source tree. It may not be as powerful as platform-specific or third-party +trace backends but it is portable. This is the recommended trace backend +unless you have specific needs for more advanced backends. + +The "simple" backend currently does not capture string arguments, it simply +records the char* pointer value instead of the string that is pointed to. + +=== Ftrace === + +The "ftrace" backend writes trace data to ftrace marker. This effectively +sends trace events to ftrace ring buffer, and you can compare qemu trace +data and kernel(especially kvm.ko when using KVM) trace data. + +if you use KVM, enable kvm events in ftrace: + + # echo 1 > /sys/kernel/debug/tracing/events/kvm/enable + +After running qemu by root user, you can get the trace: + + # cat /sys/kernel/debug/tracing/trace + +Restriction: "ftrace" backend is restricted to Linux only. + +==== Monitor commands ==== + +* trace-file on|off|flush|set + Enable/disable/flush the trace file or set the trace file name. + +==== Analyzing trace files ==== + +The "simple" backend produces binary trace files that can be formatted with the +simpletrace.py script. The script takes the "trace-events" file and the binary +trace: + + ./scripts/simpletrace.py trace-events trace-12345 + +You must ensure that the same "trace-events" file was used to build QEMU, +otherwise trace event declarations may have changed and output will not be +consistent. + +=== LTTng Userspace Tracer === + +The "ust" backend uses the LTTng Userspace Tracer library. There are no +monitor commands built into QEMU, instead UST utilities should be used to list, +enable/disable, and dump traces. + +Package lttng-tools is required for userspace tracing. You must ensure that the +current user belongs to the "tracing" group, or manually launch the +lttng-sessiond daemon for the current user prior to running any instance of +QEMU. + +While running an instrumented QEMU, LTTng should be able to list all available +events: + + lttng list -u + +Create tracing session: + + lttng create mysession + +Enable events: + + lttng enable-event qemu:g_malloc -u + +Where the events can either be a comma-separated list of events, or "-a" to +enable all tracepoint events. Start and stop tracing as needed: + + lttng start + lttng stop + +View the trace: + + lttng view + +Destroy tracing session: + + lttng destroy + +Babeltrace can be used at any later time to view the trace: + + babeltrace $HOME/lttng-traces/mysession--